Merge mozilla-central to inbound. a=merge CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Thu, 09 May 2019 18:37:16 +0300
changeset 532045 1cff97132fe54b935825a17f98056f8f8e5752a5
parent 532044 1c632bfc14a5f139f9f6e0c5e8194ce02fe9a7cf (current diff)
parent 532022 34b343ca6c2a44e1fd4402a0d7eb247cc2c6df2e (diff)
child 532046 53e37cfca31e24fd6d35d65b33018336126eacc2
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
mobile/android/app/src/main/res/xml-v11/preferences_default_browser_tablet.xml
mobile/android/base/java/org/mozilla/gecko/trackingprotection/TrackingProtectionPrompt.java
testing/web-platform/meta/css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height.tentative.html.ini
testing/web-platform/meta/css/css-shapes/parsing/shape-outside-computed.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/values/shape-outside-inset-004.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/values/shape-outside-polygon-005.html.ini
testing/web-platform/meta/resize-observer/eventloop.html.ini
testing/web-platform/meta/visual-viewport/viewport-read-size-causes-layout.html.ini
testing/web-platform/meta/visual-viewport/viewport-read-size-in-iframe-causes-layout.html.ini
testing/web-platform/meta/visual-viewport/viewport-scrollbars-cause-resize.html.ini
testing/web-platform/meta/visual-viewport/viewport-unscaled-size-iframe.html.ini
testing/web-platform/meta/visual-viewport/viewport-unscaled-size.html.ini
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist lastupdate="1557087541924" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+<blocklist lastupdate="1557222511299" xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1211" id="flvto@hotger.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
@@ -2887,16 +2887,28 @@
     <emItem blockID="95cfb43b-72c3-4fb3-a0f2-fc975aff398d" id="{93d460ee-879f-4d8f-8599-a1c69ed59ec2}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="654077f4-a8b3-4822-8962-0bb1cac1d70d" id="/^((\{7e4a75c1-dddc-4496-9963-1c6ac99cf226\})|(\{35af257d-14dd-4cd0-8ebd-2d30c2b30561\})|(\{d6362448-1e8f-47bf-9d2f-491648d18e3d\})|(\{80a42dcf-193e-43a2-b662-d6b14882403f\})|(\{044e39fc-333b-423c-8291-26078a780b02\})|(\{68b3c6ce-162f-4ece-9ffa-8279855a4228\})|(\{057b93a7-84e6-43ff-9686-d452435ed3c5\})|(\{1223cfa2-7aad-4a16-b98a-6bf63b346835\})|(\{9815ca8b-a77c-4e4d-beac-aad1c7021dcb\})|(\{e3f2795a-cefc-4f7f-9435-5f091b988d2f\})|(\{98fd0bd5-f486-4d81-9eb1-e89e9d56bfa2\})|(\{f96fafd2-5860-4bfa-9537-3f2ca9dd496e\})|(\{da93cdd9-6aca-410e-b2f2-e927da726559\})|(\{d97e0506-d330-4547-8a5c-093b8aa08d7a\})|(\{425ad6b3-72b8-43c0-be7c-2f6585fa0ec1\})|(\{0375f007-f5ba-46ec-86d2-c5da84576562\})|(\{e8915f55-6566-4872-97eb-d77fbdbf2fb3\})|(\{ebd3a0c4-bf9e-4dfa-b487-f77722055edb\})|(\{7cc62e47-ed20-45bc-8c92-bb57128e78d6\})|(\{b5a15631-6429-49bd-a670-e83ac41f93a9\})|(\{f263d545-3234-460f-b546-a8406a0a729d\})|(\{6468c148-9888-4243-8de5-cb6291cac82a\})|(\{da2281db-0036-46f6-8878-ff26e1cf6a2b\})|(\{63f579ba-eaf5-4e1a-a7c2-c5e889beaf9f\})|(\{84569fbb-d367-40ce-b24b-fd3b611283b8\})|(\{da2bc16f-b499-401b-8771-9d9f32d88f86\})|(\{1a275ad6-5dd3-47e9-a563-41a0bebdfd90\})|(\{e07ebf1e-5917-46a2-95d9-61d9b51f3797\})|(\{0d6791d2-ce0b-4f78-90e4-8e773703bd35\})|(\{502c7ef7-745c-4ea0-8066-a17cf1b74957\})|(\{c93f0aeb-ae9b-49d9-835b-c58a6b03aa46\})|(\{1f0bf2a4-aff5-42d3-8633-71e65f289250\})|(\{28766320-358e-42e3-a2c7-67ec77552424\})|(\{74d4fcda-c103-4fb7-810a-4596530c00a4\})|(\{7b3fd37a-a127-41a0-9e4d-59ccfa165e41\})|(\{787fa0b0-d5f1-4454-8b0c-72d191d6775f\})|(\{e2bae2ed-0368-48e7-8671-3bdcc5d7713f\})|(\{fee16fb4-830f-438a-a3d5-f7e911d23e02\})|(\{72113405-b4a5-46c3-a7c6-5353568b87bd\})|(\{5ede50a4-4151-4635-804f-a6f56115a0c6\})|(\{c11487a0-d104-4bc3-814b-474f8c29049c\})|(\{35690b6e-1979-4ea3-89aa-44a94dda2afa\})|(\{e9d698ef-bad4-4960-9df3-8c41605a6d7b\})|(\{1472b3c1-cae8-42c4-bbdf-e71134dccf08\})|(\{7a40b654-1232-4e76-81e7-d95260db25cd\})|(\{f54699c8-c82f-4d6e-a161-919bbe8410de\})|(\{dca6a5cd-0d24-442a-afd4-80572bb20c34\})|(\{b8d5d169-f076-4098-b671-a3cb8b410f56\})|(\{903e6561-0646-4c38-8039-d372d8e7c90a\})|(\{b39977b9-bcb2-448b-9d7b-9aec7f62bc26\})|(\{059b5c30-b96a-48df-8083-5fff97a8f9bf\})|(\{1d0351bb-1d96-4779-b639-44eeceb2ebfb\})|(\{80c0bdb4-ba98-472d-ae56-afd8b3021115\})|(\{4dfc5596-9655-4b0c-819d-e2ff48fb8556\})|(\{d7d3ed3c-6f73-42cb-b724-c33fccc1b465\})|(\{b378a858-89bb-492e-8b4d-eb83e910a14b\})|(\{ec1fa94c-8700-49d0-ba5d-df99a912519e\})|(\{4db5d249-881f-4442-8c01-28536c45ebfd\})|(\{7a411d82-fc50-4f20-bd2c-b2b065f18097\})|(\{675e002b-e144-4694-a725-9e8cc6a3fa67\})|(\{1902a069-c039-421e-b502-1e367c237196\})|(\{866bb3a8-82bb-4c9a-bca5-26fd5f37c4ec\})|(\{6a4e7017-43cd-4646-bb48-003620bb60fe\})|(\{bc5c676e-a75f-475b-a27c-79687b1de3ec\})|(\{6b544e1a-932d-4da9-aafe-c4b4bbfe1958\})|(\{99631434-ff1e-49d3-88d3-9ac40d0dd1bd\})|(\{623b31e0-f289-47cf-995e-5a195e261758\})|(\{1b4d88a5-4b5d-44c8-849c-82f129a7dacd\})|(\{48ba880a-b7c2-4e4e-af55-9134ac926c61\})|(\{4b498e2a-8b17-47c0-a449-89a76b6e737f\})|(\{d9cbd45f-cdbc-4be1-bb16-8e60764630ff\})|(\{bfaaa94a-1a93-4a1c-9b54-9dbe98f3ef07\})|(\{87b93e6e-70a6-4538-9848-e9d0f060e372\})|(\{fea4fd50-ed6a-4b8e-b00d-3b2276df6e34\})|(\{c15450f8-8da2-4add-a8f6-603d90e8d344\})|(\{ec972135-8e5c-49d4-bff8-b6006b21f2d2\})|(\{b039f24d-8b51-40d3-abf7-55e1dc502112\})|(\{b308870f-ae9a-4972-af28-0218717a47f4\})|(\{9349a202-8b8e-4777-ba93-c723810da51e\})|(\{798750dc-0057-47e0-a1af-73dec73544fa\})|(\{186e4b6a-e3f0-4970-8f7b-05ab6bc50320\})|(\{dec8de3e-d3a4-4946-bcbd-c3523fee11c5\})|(\{06539c62-00d3-4513-9aa4-048dd273107a\})|(\{b200a289-900a-4953-b2c6-b7a323d6fb66\})|(\{4080defb-6c6b-4012-bcac-71379e9c430f\})|(\{b110855c-90dd-427a-894c-54b93c6572b7\})|(\{dd599e99-3a48-4e36-9d83-56f8c0019d4d\})|(\{4f43f2c7-c1e6-4091-88fe-c829b3bfe553\})|(\{b7a022bc-6b89-4ac1-a1fa-bf02251336b0\})|(\{1aa370ca-9865-4c52-89a8-79e95abc82f7\})|(\{fb727d0f-7c3d-4bf6-8be4-284e7e8b8f83\})|(\{1579b5dd-ef3d-4754-bc59-8a7707fe1219\})|(\{66f0cb42-bb3e-4a16-90c1-bed1e3be4aad\})|(\{f13a1f79-f63a-4332-a9c9-11fc50328fc2\})|(\{29962f4d-bf74-4775-9d02-31fe546d6fa6\})|(\{aa539764-9ec3-41a6-af0e-6c2dc46ecbf5\})|(\{9412adf1-2714-4cb2-ad5b-13d41096234a\})|(\{86f2f4cc-97c5-4cc5-8151-c327ab379fba\})|(\{ed4c3ce2-5372-429c-ae20-fa5b1f540fd7\})|(\{cd11da28-330d-4f09-a21f-fae7509f1b60\})|(\{74bc7a66-d4e6-4f1c-a0ef-1b65baa41cbf\})|(\{8069effc-45bb-4caf-8b27-a135431cd6b9\})|(\{577fc233-25bf-4e43-a164-aa75eb9d053a\})|(\{f5626996-f5cd-4d00-bcea-20dda6d9edd6\})|(\{9bb810ef-716e-4dc5-9f03-491a2c59384e\})|(\{02634a24-04d0-439f-9faf-a323ab4a1bac\})|(\{b73f7a43-a43a-47f5-8b1f-1ef7caa7857d\})|(\{3b5bf07b-5964-408a-8e43-e0239219c524\})|(\{73d3a404-150f-4594-ac2c-24f9beec78b1\})|(\{ef6a2133-5ed9-4dbc-a735-6ffe8490062e\})|(\{76b61321-01a1-4a17-850f-b064a0366b57\})|(\{bc41ca18-9209-4500-a847-4e514fea2536\})|(\{32c4c845-9bd7-4b20-97fa-a7616e7802ef\})|(\{410d9002-b517-471f-956e-30129e307af3\})|(\{43366e90-e4be-4ba6-bec0-3fb149128480\})|(\{8ebc90a4-f7a1-420a-8380-f85545403f80\})|(\{6d3fa41f-e896-4f85-ba59-321f4b26f380\})|(\{05c811f2-f828-4d3e-ad02-7386373e9a28\})|(\{e10a0ee6-8083-42a7-bed1-35400b029bf2\})|(\{c925be5a-ae0e-4958-be36-44dc2e64d4f7\})|(\{3803ed37-c101-4b21-a678-762f51b7eabf\})|(\{08a15cc0-d6cb-43c5-9a40-27443554b455\})|(\{d0953283-5970-4ebe-b270-940c6befdbb7\})|(\{7c983689-80c7-46dd-b9d2-4d2db1cf94a6\})|(\{8f320a17-868c-43dc-94fb-9d1ab7f4fe73\})|(\{843b406a-9593-49bf-9365-684fe8cb2f5a\})|(\{42cd0cbd-248d-4a44-88b6-1a3680d159ac\})|(\{23efa05d-99b1-49e0-a67d-5378f2afc20d\})|(\{f91c606c-dd33-42a4-9219-824187730f59\})|(\{9e233d16-18ae-4519-a83c-2806f4fee321\})|(\{a93cdf30-75da-463b-865b-f49cc7fd2697\})|(\{dcacb62c-9096-482d-845d-10413199a89b\})|(\{9d5da26f-c366-46b2-b3e7-5c8e3e0b9788\})|(\{dd176d1f-8cd5-4b5b-8b06-839449e87b5e\})|(\{9ada3b66-4412-427d-8696-ac0fe0ac891e\})|(\{9695495e-cb65-4cd6-8a93-52c9e2b8d767\})|(\{e9d1a027-a84c-4e90-b602-66ffe22a0ad6\})|(\{f71bda5e-c591-44aa-8f84-2f04989f7e7a\})|(\{e6e67c6f-c010-406d-8575-1835341ec4cf\})|(\{22fbf524-38be-4ead-b6ce-e55cb23ed74b\})|(\{866dafe9-1c49-47d7-a46c-1cb50ca52461\})|(\{3479fadc-41b1-492e-bb16-d8f9e514d488\})|(\{8c02daf2-79ed-4650-89ca-1e099d28c5e7\})|(\{65e6b805-7f0c-455f-b1b4-c34621056b46\})|(\{78de7006-944c-4c18-a33a-d6931619f2b0\})|(\{16c8051b-2c16-4641-bf29-2daee7883fd0\})|(\{19263ccc-a97f-49f4-867a-b49351c42c0c\})|(\{bfe416d8-e8c3-469c-908e-6926770152f0\})|(\{a29a4a96-2fcd-48f8-bfe1-a1d1df46e73d\})|(\{2d651636-a0fa-45b7-a97e-ebc85959ff23\})|(\{ac8a3af8-e264-4a0b-b813-d7fab03ae3fe\})|(\{4719ad8b-354b-443d-b1e6-4d60b851c465\})|(\{7679a9f9-29d8-4979-86e7-a5b5cf0e2fd3\})|(\{099e1648-58e7-492e-8019-3418263b9265\})|(\{9a83d154-4ea3-45f9-ae21-28f3c1f86773\})|(\{475b88fd-574c-4881-98e0-0184a03593cb\})|(\{d7b586f8-a22d-4986-9dfb-67d49ba46a68\})|(\{50b79e30-a649-4477-8612-7085c0ee3ad4\})|(\{e9b2d453-9a98-41e4-9837-c0d68ff1aeac\})|(\{b1814ce5-0d9f-495f-b260-a7e1e5538263\})|(\{9b06d35e-2eb0-4653-886f-a3f4cdcbb754\})|(\{e7d6a360-69d4-4f8c-a96f-fd63388995b2\})|(\{68a50af6-ddad-4750-a9a7-a71c55e019b7\})|(\{8286a0e0-ba89-48b3-871b-8c9acff32023\})|(\{b3c79903-9bc5-4ddf-aeeb-7d91989ae819\})|(\{7c5cc4ec-9637-428c-bcf7-28bba279cf84\})|(\{93d460ee-879f-4d8f-8599-a1c69ed59ec2\})|(\{207c95d5-2bb9-4760-b3a4-8c58ea173bff\})|(\{b3482681-1abf-4dfa-bace-dc7b51e6a150\})|(\{d3516cf6-d531-434a-b80a-df72c7166744\})|(\{da01a2aa-0cbb-4f57-a395-2256d142c519\}))$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="413065ac-176f-440d-b4a7-0f928f3e330d" id="jid1-HfFCNbAsKx6Aow@jetpack">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
+    <emItem blockID="d7ca07b4-9c97-4f49-a304-117c874ff073" id="artur.dubovoy@gmail.com">
+      <prefs/>
+      <versionRange minVersion="16.3.5" maxVersion="16.3.9" severity="3"/>
+    </emItem>
+    <emItem blockID="b62c9ee1-d66f-4964-906e-2a9b07e3fdc1" id="/^((adsmin@vietbacsecurity\.com)|(\{efdefbd4-5c30-42c3-ad2b-4c49082ec4cd\})|(\{63d83b36-a85c-4b51-8f68-8eb6c0ea6922\})|(\{4613a1ed-6cb1-410b-a8b1-3f81f73b6e00\})|(\{90b1aef7-7a52-4649-b5ca-91b5e81b5eab\})|(\{d6e2e76d-edff-416b-8c04-53052ff9fec7\})|(\{43af2e0f-b5ce-409b-9ee6-5360785c9b08\})|(\{e45fa96d-8b74-4666-86de-3bbfb774a74f\})|(\{4f8332b6-6167-4b7f-a1f9-61d8eb89b102\})|(cpcnbnofbhmpimepokdpmoomejafefhb@chrome-store-foxified-14654081)|(developios89@gmail\.com)|(\{d82da356-1fa8-4550-958a-bd2472972314\})|(\{1dfbd1c3-a8ca-4eb3-8747-d30bfd20ecd5\})|(\{6f9fa22a-128f-4d1b-8ef5-d20a44d24245\})|(\{5f6af572-35c1-44d7-9d0f-dffbb62fcafe\})|(developper@avast\.com)|(\{886a6486-37b3-4bcd-891b-fd0e335e7b1a\})|(\{886a6486-37b3-4bcd-891b-fd0e355e7b1a\})|(\{d1cd26ff-fde7-46a4-85cc-48e3bb7e9e8d\})|(\{ae11d5cc-8efb-43a0-89bf-e5a779b4fa40\})|(\{aca140ce-8249-4e6e-8e2c-cd5b1c987441\})|(\{f68b2ca7-0d2c-44cc-afc8-a606a896c467\})|(\{321db3c3-8cfd-49f1-99de-fcdc3485b379\}))$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
   </emItems>
   <pluginItems>
     <pluginItem blockID="p332">
       <match exp="libflashplayer\.so" name="filename"/>
       <match exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
@@ -2922,22 +2934,22 @@
     <pluginItem blockID="p1055">
       <match exp="DirectorShockwave\.plugin" name="filename"/>
       <infoURL>https://get.adobe.com/shockwave/</infoURL>
       <versionRange maxVersion="12.2.0.162" minVersion="0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="49b843cc-a8fc-4ede-be0c-a0da56d0214f" os="Linux">
       <match exp="libflashplayer\.so" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
-      <versionRange maxVersion="32.0.0.142" minVersion="0" severity="0" vulnerabilitystatus="1"/>
+      <versionRange maxVersion="32.0.0.156" minVersion="0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="832dc9ff-3314-4df2-abcf-7bd65a645371">
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
-      <versionRange maxVersion="32.0.0.142" minVersion="0" severity="0" vulnerabilitystatus="1"/>
+      <versionRange maxVersion="32.0.0.156" minVersion="0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
   </pluginItems>
   <gfxItems>
     <gfxBlacklistEntry blockID="g194">
       <os>WINNT 6.2</os>
       <vendor>0x1022</vendor>
       <feature>DIRECT2D</feature>
       <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
--- a/browser/components/newtab/common/Actions.jsm
+++ b/browser/components/newtab/common/Actions.jsm
@@ -127,16 +127,17 @@ for (const type of [
   "TOP_SITES_INSERT",
   "TOP_SITES_OPEN_SEARCH_SHORTCUTS_MODAL",
   "TOP_SITES_PIN",
   "TOP_SITES_PREFS_UPDATED",
   "TOP_SITES_UNPIN",
   "TOP_SITES_UPDATED",
   "TOTAL_BOOKMARKS_REQUEST",
   "TOTAL_BOOKMARKS_RESPONSE",
+  "TRAILHEAD_ENROLL_EVENT",
   "UNINIT",
   "UPDATE_PINNED_SEARCH_SHORTCUTS",
   "UPDATE_SEARCH_SHORTCUTS",
   "UPDATE_SECTION_PREFS",
   "WEBEXT_CLICK",
   "WEBEXT_DISMISS",
 ]) {
   actionTypes[type] = type;
--- a/browser/components/newtab/content-src/asrouter/components/ModalOverlay/ModalOverlay.jsx
+++ b/browser/components/newtab/content-src/asrouter/components/ModalOverlay/ModalOverlay.jsx
@@ -3,17 +3,17 @@ import React from "react";
 export class ModalOverlayWrapper extends React.PureComponent {
   constructor(props) {
     super(props);
     this.onKeyDown = this.onKeyDown.bind(this);
   }
 
   onKeyDown(event) {
     if (event.key === "Escape") {
-      this.props.onClose();
+      this.props.onClose(event);
     }
   }
 
   componentWillMount() {
     this.props.document.addEventListener("keydown", this.onKeyDown);
     this.props.document.body.classList.add("modal-open");
   }
 
--- a/browser/components/newtab/content-src/asrouter/templates/Trailhead/Trailhead.jsx
+++ b/browser/components/newtab/content-src/asrouter/templates/Trailhead/Trailhead.jsx
@@ -118,23 +118,28 @@ export class _Trailhead extends React.Pu
   }
 
   onSubmit() {
     this.props.dispatch(ac.UserEvent({event: "SUBMIT_EMAIL", ...this._getFormInfo()}));
 
     global.addEventListener("visibilitychange", this.closeModal);
   }
 
-  closeModal() {
+  closeModal(ev) {
     global.removeEventListener("visibilitychange", this.closeModal);
     this.props.document.body.classList.remove("welcome");
     this.props.document.getElementById("root").removeAttribute("aria-hidden");
     this.setState({isModalOpen: false});
     this.revealCards();
-    this.props.dispatch(ac.UserEvent({event: "SKIPPED_SIGNIN", ...this._getFormInfo()}));
+
+    // If closeModal() was triggered by a visibilitychange event, the user actually
+    // submitted the email form so we don't send a SKIPPED_SIGNIN ping.
+    if (!ev || ev.type !== "visibilitychange") {
+      this.props.dispatch(ac.UserEvent({event: "SKIPPED_SIGNIN", ...this._getFormInfo()}));
+    }
   }
 
   /**
    * Report to telemetry additional information about the form submission.
    */
   _getFormInfo() {
     const value = {has_flow_params: this.state.flowId.length > 0};
     return {value};
@@ -218,17 +223,17 @@ export class _Trailhead extends React.Pu
           <ul className="trailheadBenefits">
             {content.benefits.map(item => (
               <li key={item.id} className={item.id}>
                 <h3 data-l10n-id={item.title.string_id}>{this.getStringValue(item.title)}</h3>
                 <p data-l10n-id={item.text.string_id}>{this.getStringValue(item.text)}</p>
               </li>
             ))}
           </ul>
-          <a className="trailheadLearn" data-l10n-id={content.learn.text.string_id} href={this.addUtmParams(content.learn.url)}>
+          <a className="trailheadLearn" data-l10n-id={content.learn.text.string_id} href={this.addUtmParams(content.learn.url)} target="_blank" rel="noopener noreferrer">
             {this.getStringValue(content.learn.text)}
           </a>
         </div>
         <div className="trailheadForm">
           <h3 data-l10n-id={content.form.title.string_id}>{this.getStringValue(content.form.title)}</h3>
           <p data-l10n-id={content.form.text.string_id}>{this.getStringValue(content.form.text)}</p>
           <form method="get" action={this.props.fxaEndpoint} target="_blank" rel="noopener noreferrer" onSubmit={this.onSubmit}>
             <input name="service" type="hidden" value="sync" />
@@ -242,23 +247,23 @@ export class _Trailhead extends React.Pu
             <input name="flow_begin_time" type="hidden" value={this.state.flowBeginTime} />
             <input name="style" type="hidden" value="trailhead" />
             <p data-l10n-id="onboarding-join-form-email-error" className="error" />
             <input
               data-l10n-id={content.form.email.string_id}
               placeholder={this.getStringValue(content.form.email)}
               name="email"
               type="email"
-              required="true"
+              required="required"
               onInvalid={this.onInputInvalid}
               onChange={this.onInputChange} />
             <p className="trailheadTerms" data-l10n-id="onboarding-join-form-legal">
-              <a data-l10n-name="terms"
+              <a data-l10n-name="terms" target="_blank" rel="noopener noreferrer"
                 href={this.addUtmParams("https://accounts.firefox.com/legal/terms")} />
-              <a data-l10n-name="privacy"
+              <a data-l10n-name="privacy" target="_blank" rel="noopener noreferrer"
                 href={this.addUtmParams("https://accounts.firefox.com/legal/privacy")} />
             </p>
             <button data-l10n-id={content.form.button.string_id} type="submit">
               {this.getStringValue(content.form.button)}
             </button>
           </form>
         </div>
       </div>
--- a/browser/components/newtab/content-src/asrouter/templates/Trailhead/_Trailhead.scss
+++ b/browser/components/newtab/content-src/asrouter/templates/Trailhead/_Trailhead.scss
@@ -58,17 +58,17 @@
     }
 
     .trailheadInner {
       grid-template-columns: 4fr 3fr;
     }
 
     .trailheadContent {
       .trailheadBenefits {
-        background: url('#{$image-path}sync-devices.svg');
+        background: url('#{$image-path}sync-devices-trailhead.svg');
         background-position: center center;
         background-repeat: no-repeat;
         background-size: contain;
         height: 200px;
         margin-inline-end: 60px;
       }
 
       .trailheadLearn {
@@ -396,34 +396,30 @@
     bottom: 0;
     left: 0;
     width: 100%;
     text-align: center;
   }
 }
 
 .inline-onboarding {
+  &.activity-stream.welcome {
+    overflow: scroll;
+  }
+
+  .modalOverlayInner {
+    position: absolute;
+  }
+
   .outer-wrapper {
     position: relative;
 
     .prefs-button {
       button {
         position: absolute;
       }
     }
   }
 
   .asrouter-toggle {
     position: absolute;
   }
 }
-
-// If the window is too short or narrow, we need to allow scrolling so user can get to Start Browsing button.
-@media (max-height: 760px), (max-width: 924px) {
-  .activity-stream.welcome.inline-onboarding {
-    overflow: auto;
-  }
-
-  .trailhead {
-    position: absolute;
-    top: 20px;
-  }
-}
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
@@ -36,28 +36,23 @@ export class DSCard extends React.PureCo
           dispatch={this.props.dispatch}
           onLinkClick={!this.props.placeholder ? this.onLinkClick : undefined}
           url={this.props.url}>
           <div className="img-wrapper">
             <DSImage extraClassNames="img" source={this.props.image_src} rawSource={this.props.raw_image_src} />
           </div>
           <div className="meta">
             <div className="info-wrap">
+              <p className="source">{this.props.source}</p>
               <header className="title">{this.props.title}</header>
               {this.props.excerpt && <p className="excerpt">{this.props.excerpt}</p>}
             </div>
-            <p>
-              {this.props.context && (
-                <span>
-                  <span className="context">{this.props.context}</span>
-                  <br />
-                </span>
-              )}
-              <span className="source">{this.props.source}</span>
-            </p>
+            {this.props.context && (
+              <p className="context">{this.props.context}</p>
+            )}
           </div>
           <ImpressionStats
             campaignId={this.props.campaignId}
             rows={[{id: this.props.id, pos: this.props.pos}]}
             dispatch={this.props.dispatch}
             source={this.props.type} />
         </SafeAnchor>
         {!this.props.placeholder && <DSLinkMenu
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
@@ -85,16 +85,20 @@
       font-weight: 600;
     }
 
     .excerpt {
       // show only 3 lines of copy
       @include limit-visibile-lines(3, $excerpt-line-height, $excerpt-font-size);
     }
 
+    .source {
+      margin-bottom: 2px;
+    }
+
     .context,
     .source {
       @include dark-theme-only {
         color: $grey-40;
       }
 
       font-size: 13px;
       color: $grey-50;
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
@@ -76,24 +76,24 @@ export class Hero extends React.PureComp
             dispatch={this.props.dispatch}
             onLinkClick={this.onLinkClick}
             url={heroRec.url}>
             <div className="img-wrapper">
               <DSImage extraClassNames="img" source={heroRec.image_src} rawSource={heroRec.raw_image_src} />
             </div>
             <div className="meta">
               <div className="header-and-excerpt">
+                {heroRec.context ? (
+                  <p className="context">{heroRec.context}</p>
+                ) : (
+                  <p className="source">{heroRec.domain}</p>
+                )}
                 <header>{heroRec.title}</header>
                 <p className="excerpt">{heroRec.excerpt}</p>
               </div>
-              {heroRec.context ? (
-                <p className="context">{heroRec.context}</p>
-              ) : (
-                <p className="source">{heroRec.domain}</p>
-              )}
             </div>
             <ImpressionStats
               campaignId={heroRec.campaignId}
               rows={[{id: heroRec.id, pos: heroRec.pos}]}
               dispatch={this.props.dispatch}
               source={this.props.type} />
           </SafeAnchor>
           <DSLinkMenu
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/_Hero.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/_Hero.scss
@@ -122,16 +122,21 @@
           color: $white;
         }
 
         @include limit-visibile-lines(4, 28, 22);
         color: $grey-90;
         margin-bottom: 0;
       }
 
+      .context,
+      .source {
+        margin: 0 0 4px;
+      }
+
       .context {
         @include dark-theme-only {
           color: $teal-10;
         }
 
         color: $teal-70;
       }
 
--- a/browser/components/newtab/content-src/lib/selectLayoutRender.js
+++ b/browser/components/newtab/content-src/lib/selectLayoutRender.js
@@ -94,23 +94,23 @@ export const selectLayoutRender = (state
       data.recommendations[i].pos = positions[component.type]++;
     }
 
     return {...component, data};
   };
 
   const renderLayout = () => {
     const renderedLayoutArray = [];
-    for (const row of layout.filter(r => r.components.length)) {
+    for (const row of layout.filter(r => r.components.filter(c => !filterArray.includes(c.type)).length)) {
       let components = [];
       renderedLayoutArray.push({
         ...row,
         components,
       });
-      for (const component of row.components.filter(c => !filterArray.includes(c.type))) {
+      for (const component of row.components) {
         if (component.feed) {
           const spocsConfig = component.spocs;
           // Are we still waiting on a feed/spocs, render what we have, and bail out early.
           if (!feeds.data[component.feed.url] ||
             (spocsConfig && spocsConfig.positions && spocsConfig.positions.length && !spocs.loaded)) {
             return renderedLayoutArray;
           }
           components.push(handleComponent(component));
--- a/browser/components/newtab/css/activity-stream-linux.css
+++ b/browser/components/newtab/css/activity-stream-linux.css
@@ -2033,16 +2033,19 @@ main {
         font-size: 22px;
         line-height: 28px;
         max-height: 5.09091em;
         overflow: hidden;
         color: #0C0C0D;
         margin-bottom: 0; }
         [lwt-newtab-brighttext] .ds-hero .wrapper .meta header {
           color: #FFF; }
+      .ds-hero .wrapper .meta .context,
+      .ds-hero .wrapper .meta .source {
+        margin: 0 0 4px; }
       .ds-hero .wrapper .meta .context {
         color: #008EA4; }
         [lwt-newtab-brighttext] .ds-hero .wrapper .meta .context {
           color: #A7FFFE; }
       .ds-hero .wrapper .meta .source {
         font-size: 13px;
         color: #737373;
         margin-bottom: 0;
@@ -2638,16 +2641,18 @@ main {
       max-height: 4.23529em;
       overflow: hidden;
       font-weight: 600; }
     .ds-card .meta .excerpt {
       font-size: 14px;
       line-height: 20px;
       max-height: 4.28571em;
       overflow: hidden; }
+    .ds-card .meta .source {
+      margin-bottom: 2px; }
     .ds-card .meta .context,
     .ds-card .meta .source {
       font-size: 13px;
       color: #737373; }
       [lwt-newtab-brighttext] .ds-card .meta .context, [lwt-newtab-brighttext]
       .ds-card .meta .source {
         color: #B1B1B3; }
   .ds-card header {
@@ -3741,17 +3746,17 @@ a.firstrun-link {
     width: 860px; }
     @media (max-width: 860px) {
       .trailhead.syncCohort {
         left: 0;
         width: 100%; } }
     .trailhead.syncCohort .trailheadInner {
       grid-template-columns: 4fr 3fr; }
     .trailhead.syncCohort .trailheadContent .trailheadBenefits {
-      background: url("../data/content/assets/sync-devices.svg");
+      background: url("../data/content/assets/sync-devices-trailhead.svg");
       background-position: center center;
       background-repeat: no-repeat;
       background-size: contain;
       height: 200px;
       margin-inline-end: 60px; }
     .trailhead.syncCohort .trailheadContent .trailheadLearn {
       margin-inline-start: 0; }
   .trailhead .trailheadBenefits {
@@ -3965,22 +3970,21 @@ a.firstrun-link {
   .trailheadCard .onboardingButtonContainer {
     height: 60px;
     position: absolute;
     bottom: 0;
     left: 0;
     width: 100%;
     text-align: center; }
 
+.inline-onboarding.activity-stream.welcome {
+  overflow: scroll; }
+
+.inline-onboarding .modalOverlayInner {
+  position: absolute; }
+
 .inline-onboarding .outer-wrapper {
   position: relative; }
   .inline-onboarding .outer-wrapper .prefs-button button {
     position: absolute; }
 
 .inline-onboarding .asrouter-toggle {
   position: absolute; }
-
-@media (max-height: 760px), (max-width: 924px) {
-  .activity-stream.welcome.inline-onboarding {
-    overflow: auto; }
-  .trailhead {
-    position: absolute;
-    top: 20px; } }
--- a/browser/components/newtab/css/activity-stream-mac.css
+++ b/browser/components/newtab/css/activity-stream-mac.css
@@ -2036,16 +2036,19 @@ main {
         font-size: 22px;
         line-height: 28px;
         max-height: 5.09091em;
         overflow: hidden;
         color: #0C0C0D;
         margin-bottom: 0; }
         [lwt-newtab-brighttext] .ds-hero .wrapper .meta header {
           color: #FFF; }
+      .ds-hero .wrapper .meta .context,
+      .ds-hero .wrapper .meta .source {
+        margin: 0 0 4px; }
       .ds-hero .wrapper .meta .context {
         color: #008EA4; }
         [lwt-newtab-brighttext] .ds-hero .wrapper .meta .context {
           color: #A7FFFE; }
       .ds-hero .wrapper .meta .source {
         font-size: 13px;
         color: #737373;
         margin-bottom: 0;
@@ -2641,16 +2644,18 @@ main {
       max-height: 4.23529em;
       overflow: hidden;
       font-weight: 600; }
     .ds-card .meta .excerpt {
       font-size: 14px;
       line-height: 20px;
       max-height: 4.28571em;
       overflow: hidden; }
+    .ds-card .meta .source {
+      margin-bottom: 2px; }
     .ds-card .meta .context,
     .ds-card .meta .source {
       font-size: 13px;
       color: #737373; }
       [lwt-newtab-brighttext] .ds-card .meta .context, [lwt-newtab-brighttext]
       .ds-card .meta .source {
         color: #B1B1B3; }
   .ds-card header {
@@ -3744,17 +3749,17 @@ a.firstrun-link {
     width: 860px; }
     @media (max-width: 860px) {
       .trailhead.syncCohort {
         left: 0;
         width: 100%; } }
     .trailhead.syncCohort .trailheadInner {
       grid-template-columns: 4fr 3fr; }
     .trailhead.syncCohort .trailheadContent .trailheadBenefits {
-      background: url("../data/content/assets/sync-devices.svg");
+      background: url("../data/content/assets/sync-devices-trailhead.svg");
       background-position: center center;
       background-repeat: no-repeat;
       background-size: contain;
       height: 200px;
       margin-inline-end: 60px; }
     .trailhead.syncCohort .trailheadContent .trailheadLearn {
       margin-inline-start: 0; }
   .trailhead .trailheadBenefits {
@@ -3968,22 +3973,21 @@ a.firstrun-link {
   .trailheadCard .onboardingButtonContainer {
     height: 60px;
     position: absolute;
     bottom: 0;
     left: 0;
     width: 100%;
     text-align: center; }
 
+.inline-onboarding.activity-stream.welcome {
+  overflow: scroll; }
+
+.inline-onboarding .modalOverlayInner {
+  position: absolute; }
+
 .inline-onboarding .outer-wrapper {
   position: relative; }
   .inline-onboarding .outer-wrapper .prefs-button button {
     position: absolute; }
 
 .inline-onboarding .asrouter-toggle {
   position: absolute; }
-
-@media (max-height: 760px), (max-width: 924px) {
-  .activity-stream.welcome.inline-onboarding {
-    overflow: auto; }
-  .trailhead {
-    position: absolute;
-    top: 20px; } }
--- a/browser/components/newtab/css/activity-stream-windows.css
+++ b/browser/components/newtab/css/activity-stream-windows.css
@@ -2033,16 +2033,19 @@ main {
         font-size: 22px;
         line-height: 28px;
         max-height: 5.09091em;
         overflow: hidden;
         color: #0C0C0D;
         margin-bottom: 0; }
         [lwt-newtab-brighttext] .ds-hero .wrapper .meta header {
           color: #FFF; }
+      .ds-hero .wrapper .meta .context,
+      .ds-hero .wrapper .meta .source {
+        margin: 0 0 4px; }
       .ds-hero .wrapper .meta .context {
         color: #008EA4; }
         [lwt-newtab-brighttext] .ds-hero .wrapper .meta .context {
           color: #A7FFFE; }
       .ds-hero .wrapper .meta .source {
         font-size: 13px;
         color: #737373;
         margin-bottom: 0;
@@ -2638,16 +2641,18 @@ main {
       max-height: 4.23529em;
       overflow: hidden;
       font-weight: 600; }
     .ds-card .meta .excerpt {
       font-size: 14px;
       line-height: 20px;
       max-height: 4.28571em;
       overflow: hidden; }
+    .ds-card .meta .source {
+      margin-bottom: 2px; }
     .ds-card .meta .context,
     .ds-card .meta .source {
       font-size: 13px;
       color: #737373; }
       [lwt-newtab-brighttext] .ds-card .meta .context, [lwt-newtab-brighttext]
       .ds-card .meta .source {
         color: #B1B1B3; }
   .ds-card header {
@@ -3741,17 +3746,17 @@ a.firstrun-link {
     width: 860px; }
     @media (max-width: 860px) {
       .trailhead.syncCohort {
         left: 0;
         width: 100%; } }
     .trailhead.syncCohort .trailheadInner {
       grid-template-columns: 4fr 3fr; }
     .trailhead.syncCohort .trailheadContent .trailheadBenefits {
-      background: url("../data/content/assets/sync-devices.svg");
+      background: url("../data/content/assets/sync-devices-trailhead.svg");
       background-position: center center;
       background-repeat: no-repeat;
       background-size: contain;
       height: 200px;
       margin-inline-end: 60px; }
     .trailhead.syncCohort .trailheadContent .trailheadLearn {
       margin-inline-start: 0; }
   .trailhead .trailheadBenefits {
@@ -3965,22 +3970,21 @@ a.firstrun-link {
   .trailheadCard .onboardingButtonContainer {
     height: 60px;
     position: absolute;
     bottom: 0;
     left: 0;
     width: 100%;
     text-align: center; }
 
+.inline-onboarding.activity-stream.welcome {
+  overflow: scroll; }
+
+.inline-onboarding .modalOverlayInner {
+  position: absolute; }
+
 .inline-onboarding .outer-wrapper {
   position: relative; }
   .inline-onboarding .outer-wrapper .prefs-button button {
     position: absolute; }
 
 .inline-onboarding .asrouter-toggle {
   position: absolute; }
-
-@media (max-height: 760px), (max-width: 924px) {
-  .activity-stream.welcome.inline-onboarding {
-    overflow: auto; }
-  .trailhead {
-    position: absolute;
-    top: 20px; } }
--- a/browser/components/newtab/data/content/activity-stream.bundle.js
+++ b/browser/components/newtab/data/content/activity-stream.bundle.js
@@ -192,17 +192,17 @@ const globalImportContext = typeof Windo
 
 // Create an object that avoids accidental differing key/value pairs:
 // {
 //   INIT: "INIT",
 //   UNINIT: "UNINIT"
 // }
 const actionTypes = {};
 
-for (const type of ["ADDONS_INFO_REQUEST", "ADDONS_INFO_RESPONSE", "ARCHIVE_FROM_POCKET", "AS_ROUTER_INITIALIZED", "AS_ROUTER_PREF_CHANGED", "AS_ROUTER_TELEMETRY_USER_EVENT", "BLOCK_URL", "BOOKMARK_URL", "COPY_DOWNLOAD_LINK", "DELETE_BOOKMARK_BY_ID", "DELETE_FROM_POCKET", "DELETE_HISTORY_URL", "DIALOG_CANCEL", "DIALOG_OPEN", "DISCOVERY_STREAM_CONFIG_CHANGE", "DISCOVERY_STREAM_CONFIG_SETUP", "DISCOVERY_STREAM_CONFIG_SET_VALUE", "DISCOVERY_STREAM_FEEDS_UPDATE", "DISCOVERY_STREAM_FEED_UPDATE", "DISCOVERY_STREAM_IMPRESSION_STATS", "DISCOVERY_STREAM_LAYOUT_RESET", "DISCOVERY_STREAM_LAYOUT_UPDATE", "DISCOVERY_STREAM_LINK_BLOCKED", "DISCOVERY_STREAM_LOADED_CONTENT", "DISCOVERY_STREAM_OPT_OUT", "DISCOVERY_STREAM_SPOCS_CAPS", "DISCOVERY_STREAM_SPOCS_ENDPOINT", "DISCOVERY_STREAM_SPOCS_FILL", "DISCOVERY_STREAM_SPOCS_UPDATE", "DISCOVERY_STREAM_SPOC_IMPRESSION", "DOWNLOAD_CHANGED", "FAKE_FOCUS_SEARCH", "FILL_SEARCH_TERM", "HANDOFF_SEARCH_TO_AWESOMEBAR", "HIDE_SEARCH", "INIT", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_DOWNLOAD_FILE", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "OPEN_WEBEXT_SETTINGS", "PAGE_PRERENDERED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_CHANGED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PLACES_SAVED_TO_POCKET", "POCKET_CTA", "POCKET_LINK_DELETED_OR_ARCHIVED", "POCKET_LOGGED_IN", "POCKET_WAITING_FOR_SPOC", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "PREVIEW_REQUEST", "PREVIEW_REQUEST_CANCEL", "PREVIEW_RESPONSE", "REMOVE_DOWNLOAD_FILE", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_MOVE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_DOWNLOAD_FILE", "SHOW_FIREFOX_ACCOUNTS", "SHOW_SEARCH", "SKIPPED_SIGNIN", "SNIPPETS_BLOCKLIST_CLEARED", "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_PREVIEW_MODE", "SNIPPETS_RESET", "SNIPPET_BLOCKED", "SUBMIT_EMAIL", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_CLOSE_SEARCH_SHORTCUTS_MODAL", "TOP_SITES_EDIT", "TOP_SITES_INSERT", "TOP_SITES_OPEN_SEARCH_SHORTCUTS_MODAL", "TOP_SITES_PIN", "TOP_SITES_PREFS_UPDATED", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "TOTAL_BOOKMARKS_REQUEST", "TOTAL_BOOKMARKS_RESPONSE", "UNINIT", "UPDATE_PINNED_SEARCH_SHORTCUTS", "UPDATE_SEARCH_SHORTCUTS", "UPDATE_SECTION_PREFS", "WEBEXT_CLICK", "WEBEXT_DISMISS"]) {
+for (const type of ["ADDONS_INFO_REQUEST", "ADDONS_INFO_RESPONSE", "ARCHIVE_FROM_POCKET", "AS_ROUTER_INITIALIZED", "AS_ROUTER_PREF_CHANGED", "AS_ROUTER_TELEMETRY_USER_EVENT", "BLOCK_URL", "BOOKMARK_URL", "COPY_DOWNLOAD_LINK", "DELETE_BOOKMARK_BY_ID", "DELETE_FROM_POCKET", "DELETE_HISTORY_URL", "DIALOG_CANCEL", "DIALOG_OPEN", "DISCOVERY_STREAM_CONFIG_CHANGE", "DISCOVERY_STREAM_CONFIG_SETUP", "DISCOVERY_STREAM_CONFIG_SET_VALUE", "DISCOVERY_STREAM_FEEDS_UPDATE", "DISCOVERY_STREAM_FEED_UPDATE", "DISCOVERY_STREAM_IMPRESSION_STATS", "DISCOVERY_STREAM_LAYOUT_RESET", "DISCOVERY_STREAM_LAYOUT_UPDATE", "DISCOVERY_STREAM_LINK_BLOCKED", "DISCOVERY_STREAM_LOADED_CONTENT", "DISCOVERY_STREAM_OPT_OUT", "DISCOVERY_STREAM_SPOCS_CAPS", "DISCOVERY_STREAM_SPOCS_ENDPOINT", "DISCOVERY_STREAM_SPOCS_FILL", "DISCOVERY_STREAM_SPOCS_UPDATE", "DISCOVERY_STREAM_SPOC_IMPRESSION", "DOWNLOAD_CHANGED", "FAKE_FOCUS_SEARCH", "FILL_SEARCH_TERM", "HANDOFF_SEARCH_TO_AWESOMEBAR", "HIDE_SEARCH", "INIT", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_DOWNLOAD_FILE", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "OPEN_WEBEXT_SETTINGS", "PAGE_PRERENDERED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_CHANGED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PLACES_SAVED_TO_POCKET", "POCKET_CTA", "POCKET_LINK_DELETED_OR_ARCHIVED", "POCKET_LOGGED_IN", "POCKET_WAITING_FOR_SPOC", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "PREVIEW_REQUEST", "PREVIEW_REQUEST_CANCEL", "PREVIEW_RESPONSE", "REMOVE_DOWNLOAD_FILE", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_MOVE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_DOWNLOAD_FILE", "SHOW_FIREFOX_ACCOUNTS", "SHOW_SEARCH", "SKIPPED_SIGNIN", "SNIPPETS_BLOCKLIST_CLEARED", "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_PREVIEW_MODE", "SNIPPETS_RESET", "SNIPPET_BLOCKED", "SUBMIT_EMAIL", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_CLOSE_SEARCH_SHORTCUTS_MODAL", "TOP_SITES_EDIT", "TOP_SITES_INSERT", "TOP_SITES_OPEN_SEARCH_SHORTCUTS_MODAL", "TOP_SITES_PIN", "TOP_SITES_PREFS_UPDATED", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "TOTAL_BOOKMARKS_REQUEST", "TOTAL_BOOKMARKS_RESPONSE", "TRAILHEAD_ENROLL_EVENT", "UNINIT", "UPDATE_PINNED_SEARCH_SHORTCUTS", "UPDATE_SEARCH_SHORTCUTS", "UPDATE_SECTION_PREFS", "WEBEXT_CLICK", "WEBEXT_DISMISS"]) {
   actionTypes[type] = type;
 } // These are acceptable actions for AS Router messages to have. They can show up
 // as call-to-action buttons in snippets, onboarding tour, etc.
 
 
 const ASRouterActions = {};
 
 for (const type of ["INSTALL_ADDON_FROM_URL", "OPEN_APPLICATIONS_MENU", "OPEN_PRIVATE_BROWSER_WINDOW", "OPEN_URL", "OPEN_ABOUT_PAGE", "OPEN_PREFERENCES_PAGE", "SHOW_FIREFOX_ACCOUNTS", "PIN_CURRENT_TAB"]) {
@@ -2702,17 +2702,17 @@ class OnboardingMessage extends react__W
 class ModalOverlayWrapper extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onKeyDown = this.onKeyDown.bind(this);
   }
 
   onKeyDown(event) {
     if (event.key === "Escape") {
-      this.props.onClose();
+      this.props.onClose(event);
     }
   }
 
   componentWillMount() {
     this.props.document.addEventListener("keydown", this.onKeyDown);
     this.props.document.body.classList.add("modal-open");
   }
 
@@ -3412,28 +3412,32 @@ class _Trailhead extends react__WEBPACK_
   onSubmit() {
     this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].UserEvent({
       event: "SUBMIT_EMAIL",
       ...this._getFormInfo()
     }));
     global.addEventListener("visibilitychange", this.closeModal);
   }
 
-  closeModal() {
+  closeModal(ev) {
     global.removeEventListener("visibilitychange", this.closeModal);
     this.props.document.body.classList.remove("welcome");
     this.props.document.getElementById("root").removeAttribute("aria-hidden");
     this.setState({
       isModalOpen: false
     });
-    this.revealCards();
-    this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].UserEvent({
-      event: "SKIPPED_SIGNIN",
-      ...this._getFormInfo()
-    }));
+    this.revealCards(); // If closeModal() was triggered by a visibilitychange event, the user actually
+    // submitted the email form so we don't send a SKIPPED_SIGNIN ping.
+
+    if (!ev || ev.type !== "visibilitychange") {
+      this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].UserEvent({
+        event: "SKIPPED_SIGNIN",
+        ...this._getFormInfo()
+      }));
+    }
   }
   /**
    * Report to telemetry additional information about the form submission.
    */
 
 
   _getFormInfo() {
     const value = {
@@ -3551,17 +3555,19 @@ class _Trailhead extends react__WEBPACK_
       className: item.id
     }, react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("h3", {
       "data-l10n-id": item.title.string_id
     }, this.getStringValue(item.title)), react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("p", {
       "data-l10n-id": item.text.string_id
     }, this.getStringValue(item.text))))), react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("a", {
       className: "trailheadLearn",
       "data-l10n-id": content.learn.text.string_id,
-      href: this.addUtmParams(content.learn.url)
+      href: this.addUtmParams(content.learn.url),
+      target: "_blank",
+      rel: "noopener noreferrer"
     }, this.getStringValue(content.learn.text))), react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", {
       className: "trailheadForm"
     }, react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("h3", {
       "data-l10n-id": content.form.title.string_id
     }, this.getStringValue(content.form.title)), react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("p", {
       "data-l10n-id": content.form.text.string_id
     }, this.getStringValue(content.form.text)), react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("form", {
       method: "get",
@@ -3612,27 +3618,31 @@ class _Trailhead extends react__WEBPACK_
     }), react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("p", {
       "data-l10n-id": "onboarding-join-form-email-error",
       className: "error"
     }), react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("input", {
       "data-l10n-id": content.form.email.string_id,
       placeholder: this.getStringValue(content.form.email),
       name: "email",
       type: "email",
-      required: "true",
+      required: "required",
       onInvalid: this.onInputInvalid,
       onChange: this.onInputChange
     }), react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("p", {
       className: "trailheadTerms",
       "data-l10n-id": "onboarding-join-form-legal"
     }, react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("a", {
       "data-l10n-name": "terms",
+      target: "_blank",
+      rel: "noopener noreferrer",
       href: this.addUtmParams("https://accounts.firefox.com/legal/terms")
     }), react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("a", {
       "data-l10n-name": "privacy",
+      target: "_blank",
+      rel: "noopener noreferrer",
       href: this.addUtmParams("https://accounts.firefox.com/legal/privacy")
     })), react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("button", {
       "data-l10n-id": content.form.button.string_id,
       type: "submit"
     }, this.getStringValue(content.form.button))))), react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("button", {
       className: "trailheadStart",
       "data-l10n-id": content.skipButton.string_id,
       onBlur: this.onStartBlur,
@@ -7902,25 +7912,25 @@ class DSCard_DSCard extends external_Rea
     }, external_React_default.a.createElement(DSImage_DSImage, {
       extraClassNames: "img",
       source: this.props.image_src,
       rawSource: this.props.raw_image_src
     })), external_React_default.a.createElement("div", {
       className: "meta"
     }, external_React_default.a.createElement("div", {
       className: "info-wrap"
-    }, external_React_default.a.createElement("header", {
+    }, external_React_default.a.createElement("p", {
+      className: "source"
+    }, this.props.source), external_React_default.a.createElement("header", {
       className: "title"
     }, this.props.title), this.props.excerpt && external_React_default.a.createElement("p", {
       className: "excerpt"
-    }, this.props.excerpt)), external_React_default.a.createElement("p", null, this.props.context && external_React_default.a.createElement("span", null, external_React_default.a.createElement("span", {
+    }, this.props.excerpt)), this.props.context && external_React_default.a.createElement("p", {
       className: "context"
-    }, this.props.context), external_React_default.a.createElement("br", null)), external_React_default.a.createElement("span", {
-      className: "source"
-    }, this.props.source))), external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
+    }, this.props.context)), external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
       campaignId: this.props.campaignId,
       rows: [{
         id: this.props.id,
         pos: this.props.pos
       }],
       dispatch: this.props.dispatch,
       source: this.props.type
     })), !this.props.placeholder && external_React_default.a.createElement(DSLinkMenu, {
@@ -8290,23 +8300,23 @@ class Hero_Hero extends external_React_d
     }, external_React_default.a.createElement(DSImage_DSImage, {
       extraClassNames: "img",
       source: heroRec.image_src,
       rawSource: heroRec.raw_image_src
     })), external_React_default.a.createElement("div", {
       className: "meta"
     }, external_React_default.a.createElement("div", {
       className: "header-and-excerpt"
-    }, external_React_default.a.createElement("header", null, heroRec.title), external_React_default.a.createElement("p", {
-      className: "excerpt"
-    }, heroRec.excerpt)), heroRec.context ? external_React_default.a.createElement("p", {
+    }, heroRec.context ? external_React_default.a.createElement("p", {
       className: "context"
     }, heroRec.context) : external_React_default.a.createElement("p", {
       className: "source"
-    }, heroRec.domain)), external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
+    }, heroRec.domain), external_React_default.a.createElement("header", null, heroRec.title), external_React_default.a.createElement("p", {
+      className: "excerpt"
+    }, heroRec.excerpt))), external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
       campaignId: heroRec.campaignId,
       rows: [{
         id: heroRec.id,
         pos: heroRec.pos
       }],
       dispatch: this.props.dispatch,
       source: this.props.type
     })), external_React_default.a.createElement(DSLinkMenu, {
@@ -8521,23 +8531,23 @@ const selectLayoutRender = (state, prefs
     return { ...component,
       data
     };
   };
 
   const renderLayout = () => {
     const renderedLayoutArray = [];
 
-    for (const row of layout.filter(r => r.components.length)) {
+    for (const row of layout.filter(r => r.components.filter(c => !filterArray.includes(c.type)).length)) {
       let components = [];
       renderedLayoutArray.push({ ...row,
         components
       });
 
-      for (const component of row.components.filter(c => !filterArray.includes(c.type))) {
+      for (const component of row.components) {
         if (component.feed) {
           const spocsConfig = component.spocs; // Are we still waiting on a feed/spocs, render what we have, and bail out early.
 
           if (!feeds.data[component.feed.url] || spocsConfig && spocsConfig.positions && spocsConfig.positions.length && !spocs.loaded) {
             return renderedLayoutArray;
           }
 
           components.push(handleComponent(component));
@@ -11904,17 +11914,17 @@ class CachedIterable {
 
     if (seen.length === 0 || seen[seen.length - 1].done === false) {
       seen.push(iterator.next());
     }
   }
 
 }
 // CONCATENATED MODULE: ./node_modules/fluent/src/fallback.js
-function _asyncIterator(iterable) { var method; if (typeof Symbol === "function") { if (Symbol.asyncIterator) { method = iterable[Symbol.asyncIterator]; if (method != null) return method.call(iterable); } if (Symbol.iterator) { method = iterable[Symbol.iterator]; if (method != null) return method.call(iterable); } } throw new TypeError("Object is not async iterable"); }
+function _asyncIterator(iterable) { var method; if (typeof Symbol !== "undefined") { if (Symbol.asyncIterator) { method = iterable[Symbol.asyncIterator]; if (method != null) return method.call(iterable); } if (Symbol.iterator) { method = iterable[Symbol.iterator]; if (method != null) return method.call(iterable); } } throw new TypeError("Object is not async iterable"); }
 
 /*
  * @overview
  *
  * Functions for managing ordered sequences of MessageContexts.
  *
  * An ordered iterable of MessageContext instances can represent the current
  * negotiated fallback chain of languages.  This iterable can be used to find
new file mode 100644
--- /dev/null
+++ b/browser/components/newtab/data/content/assets/sync-devices-trailhead.svg
@@ -0,0 +1,1 @@
+<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" viewBox="0 0 234.4 93.4"><style>.st0{fill:#592acb}.st4{fill:#f9f9fa}</style><g id="right-inner"><path d="M167 78.3c-5.5 0-10.9-1.4-15.6-4.2-1-.4-1.5-1.6-1.1-2.6.4-1 1.6-1.5 2.6-1.1.1.1.3.1.4.2 4.1 2.4 8.8 3.6 13.6 3.6 1.1 0 2 .9 2 2 .1 1.2-.8 2.1-1.9 2.1zm7.6-1c-1.1 0-2-.9-2-2 0-.9.6-1.7 1.5-1.9 4.6-1.2 8.8-3.7 12.2-7.1.8-.8 2.1-.8 2.9 0s.8 2.1 0 2.9c-3.9 3.9-8.7 6.7-14 8.1h-.6zm-28.3-7.6c-.5 0-1-.2-1.4-.6-3.9-3.9-6.7-8.7-8.1-14-.4-1 .2-2.2 1.2-2.6 1-.4 2.2.2 2.6 1.2 0 .1.1.2.1.3 1.3 4.6 3.7 8.8 7.1 12.2.8.8.8 2 0 2.8-.4.5-.9.7-1.5.7zm46-6c-1.1 0-2-.9-2-2 0-.4.1-.7.3-1 2.4-4.1 3.6-8.8 3.6-13.6V47c.1-1.1 1-1.9 2.1-1.9 1 .1 1.8.9 1.9 1.9 0 5.5-1.4 10.9-4.2 15.7-.3.6-1 1-1.7 1zM137.7 49c-1.1 0-2-.9-2-2 0-5.5 1.4-10.8 4.2-15.6.5-1 1.7-1.4 2.7-.9 1 .5 1.4 1.7.9 2.7 0 .1-.1.1-.1.2-2.4 4.1-3.6 8.8-3.6 13.6 0 1.1-.9 2-2.1 2 .1 0 0 0 0 0zm57.5-7.7c-.9 0-1.7-.6-1.9-1.5-1.3-4.6-3.7-8.8-7.1-12.2-.8-.8-.8-2.1 0-2.9.8-.8 2.1-.8 2.9 0 3.9 3.9 6.7 8.7 8.1 14 .3 1.1-.3 2.2-1.4 2.5-.2.1-.4.1-.6.1zm-48.9-12.9c-.5 0-1-.2-1.4-.6-.8-.8-.8-2.1 0-2.8 3.9-3.9 8.7-6.7 14-8.1 1.1-.2 2.1.6 2.3 1.7.1.9-.4 1.8-1.2 2.1-4.6 1.2-8.8 3.7-12.2 7.1-.5.3-1 .6-1.5.6zm35.2-4.8c-.3 0-.7-.1-1-.3-4.1-2.4-8.8-3.6-13.5-3.6h-.2c-1.1.1-2.1-.8-2.1-1.9-.1-1.1.8-2.1 1.9-2.1h.3c5.5 0 10.8 1.4 15.6 4.1 1 .5 1.3 1.8.8 2.7-.4.7-1.1 1.1-1.8 1.1z" class="st0"/><animateTransform attributeName="transform" attributeType="XML" dur="10s" from="360 167 47" repeatCount="indefinite" to="0 167 47" type="rotate"/></g><g id="right-outer"><path d="M167 92c-7.9 0-15.6-2.1-22.4-6-1.4-.8-1.9-2.6-1.1-3.9.8-1.4 2.6-1.9 3.9-1.1 5.9 3.4 12.7 5.2 19.6 5.2 1.6 0 2.9 1.3 2.9 2.9 0 1.6-1.3 2.9-2.9 2.9zm10.9-1.4c-1.6 0-2.9-1.3-2.9-2.9 0-1.3.9-2.5 2.1-2.8 6.6-1.8 12.7-5.3 17.6-10.2 1.1-1.2 2.9-1.2 4.1-.1 1.2 1.1 1.2 2.9.1 4.1l-.1.1c-5.6 5.6-12.5 9.6-20.1 11.7-.3 0-.5.1-.8.1zm-40.6-10.9c-.8 0-1.5-.3-2-.8-5.6-5.6-9.6-12.5-11.7-20.1-.4-1.6.6-3.1 2.2-3.5 1.5-.3 3 .5 3.4 2 1.8 6.6 5.3 12.7 10.2 17.6 1.1 1.1 1.1 3 0 4.1-.6.4-1.4.7-2.1.7zm66.2-8.8c-1.6 0-2.9-1.3-2.9-2.9 0-.5.1-1 .4-1.4 3.4-6 5.2-12.7 5.2-19.6v-.2c0-1.6 1.3-2.9 2.9-2.9s2.9 1.3 2.9 2.9v.2c0 7.9-2.1 15.7-6 22.5-.5.9-1.5 1.4-2.5 1.4zm-78.6-21c-1.6 0-2.9-1.3-2.9-2.9 0-7.9 2.1-15.6 6-22.4.8-1.4 2.6-1.9 4-1.1 1.4.8 1.9 2.6 1.1 4-3.4 5.9-5.2 12.7-5.2 19.5-.1 1.5-1.3 2.9-3 2.9zm82.7-11.1c-1.3 0-2.5-.9-2.8-2.1-1.8-6.6-5.3-12.7-10.2-17.5-1.2-1-1.4-2.8-.4-4.1s2.8-1.4 4.1-.4c.1.1.3.2.4.4 5.6 5.6 9.7 12.5 11.7 20.1.4 1.5-.5 3.1-2 3.5-.3.1-.5.1-.8.1zm-70.5-18.6c-.8 0-1.5-.3-2.1-.8-1.1-1.1-1.1-3 0-4.1 5.6-5.6 12.5-9.6 20.1-11.7 1.5-.4 3.1.5 3.5 2.1.4 1.5-.5 3-2 3.5-6.6 1.8-12.7 5.3-17.5 10.2-.4.5-1.2.8-2 .8zm50.8-6.9c-.5 0-1-.1-1.4-.4-5.9-3.4-12.7-5.2-19.5-5.2h-.1c-1.6 0-2.9-1.3-2.9-2.9s1.3-2.9 2.9-2.9h.1c7.9 0 15.6 2 22.4 6 1.4.8 1.9 2.6 1.1 3.9-.6 1-1.5 1.5-2.6 1.5z" class="st0"/><animateTransform attributeName="transform" attributeType="XML" dur="10s" from="0 167 47" repeatCount="indefinite" to="360 167 47" type="rotate"/></g><g id="left-inner"><path d="M79 78.3c-5.5 0-10.9-1.4-15.6-4.2-1-.4-1.5-1.6-1.1-2.6.4-1 1.6-1.5 2.6-1.1.1.1.3.1.4.2 4.1 2.4 8.8 3.6 13.6 3.6 1.1 0 2 .9 2 2 .1 1.2-.8 2.1-1.9 2.1zm7.6-1c-1.1 0-2-.9-2-2 0-.9.6-1.7 1.5-1.9 4.6-1.2 8.8-3.7 12.2-7.1.8-.8 2.1-.8 2.9 0 .8.8.8 2.1 0 2.9-3.9 3.9-8.7 6.7-14 8.1h-.6zm-28.3-7.6c-.5 0-1-.2-1.4-.6-3.9-3.9-6.7-8.7-8.1-14-.4-1 .2-2.2 1.2-2.6 1-.4 2.2.2 2.6 1.2 0 .1.1.2.1.3 1.3 4.6 3.7 8.8 7.1 12.2.8.8.8 2 0 2.8-.4.5-.9.7-1.5.7zm46-6c-1.1 0-2-.9-2-2 0-.4.1-.7.3-1 2.4-4.1 3.6-8.8 3.6-13.6V47c.1-1.1 1-1.9 2.1-1.9 1 .1 1.8.9 1.9 1.9 0 5.5-1.4 10.9-4.2 15.7-.3.6-1 1-1.7 1zM49.7 49c-1.1 0-2-.9-2-2 0-5.5 1.4-10.8 4.2-15.6.5-1 1.7-1.4 2.7-.9s1.4 1.7.9 2.7c0 .1-.1.1-.1.2-2.4 4.1-3.6 8.8-3.6 13.6 0 1.1-.9 2-2.1 2 .1 0 0 0 0 0zm57.5-7.7c-.9 0-1.7-.6-1.9-1.5-1.3-4.6-3.7-8.8-7.1-12.2-.8-.8-.8-2.1 0-2.9.8-.8 2.1-.8 2.9 0 3.9 3.9 6.7 8.7 8.1 14 .3 1.1-.3 2.2-1.4 2.5-.2.1-.4.1-.6.1zM58.3 28.4c-.5 0-1-.2-1.4-.6-.8-.8-.8-2.1 0-2.8 3.9-3.9 8.7-6.7 14-8.1 1.1-.2 2.1.6 2.3 1.7.1.9-.4 1.8-1.2 2.1-4.6 1.2-8.8 3.7-12.2 7.1-.5.3-1 .6-1.5.6zm35.2-4.8c-.3 0-.7-.1-1-.3-4.1-2.4-8.8-3.6-13.5-3.6h-.2c-1.1.1-2.1-.8-2.1-1.9-.1-1.1.8-2.1 1.9-2.1h.3c5.5 0 10.8 1.4 15.6 4.1 1 .5 1.3 1.8.8 2.7-.4.7-1.1 1.1-1.8 1.1z" class="st0"/><animateTransform attributeName="transform" attributeType="XML" dur="10s" from="360 79 47" repeatCount="indefinite" to="0 79 47" type="rotate"/></g><g id="left-outer"><path d="M79 92c-7.9 0-15.6-2.1-22.4-6-1.4-.8-1.9-2.6-1.1-3.9.8-1.4 2.6-1.9 3.9-1.1 5.9 3.4 12.7 5.2 19.6 5.2 1.6 0 2.9 1.3 2.9 2.9 0 1.6-1.3 2.9-2.9 2.9zm10.9-1.4c-1.6 0-2.9-1.3-2.9-2.9 0-1.3.9-2.5 2.1-2.8 6.6-1.8 12.7-5.3 17.6-10.2 1.1-1.2 2.9-1.2 4.1-.1 1.2 1.1 1.2 2.9.1 4.1l-.1.1c-5.6 5.6-12.5 9.6-20.1 11.7-.3 0-.5.1-.8.1zM49.3 79.7c-.8 0-1.5-.3-2-.8-5.6-5.6-9.6-12.5-11.7-20.1-.4-1.6.6-3.1 2.2-3.5 1.5-.3 3 .5 3.4 2C43 63.9 46.5 70 51.4 74.9c1.1 1.1 1.1 3 0 4.1-.6.4-1.4.7-2.1.7zm66.2-8.8c-1.6 0-2.9-1.3-2.9-2.9 0-.5.1-1 .4-1.4 3.4-6 5.2-12.7 5.2-19.6v-.2c0-1.6 1.3-2.9 2.9-2.9 1.6 0 2.9 1.3 2.9 2.9v.2c0 7.9-2.1 15.7-6 22.5-.5.9-1.5 1.4-2.5 1.4zm-78.6-21c-1.6 0-2.9-1.3-2.9-2.9 0-7.9 2.1-15.6 6-22.4.8-1.4 2.6-1.9 4-1.1 1.4.8 1.9 2.6 1.1 4-3.4 5.9-5.2 12.7-5.2 19.5-.1 1.5-1.3 2.9-3 2.9zm82.7-11.1c-1.3 0-2.5-.9-2.8-2.1-1.8-6.6-5.3-12.7-10.2-17.5-1.2-1-1.4-2.8-.4-4.1 1-1.2 2.8-1.4 4.1-.4.1.1.3.2.4.4 5.6 5.6 9.7 12.5 11.7 20.1.4 1.5-.5 3.1-2 3.5-.3.1-.5.1-.8.1zM49.1 20.2c-.8 0-1.5-.3-2.1-.8-1.1-1.1-1.1-3 0-4.1 5.6-5.6 12.5-9.6 20.1-11.7 1.5-.4 3.1.5 3.5 2.1.4 1.5-.5 3-2 3.5C62 11 55.9 14.5 51.1 19.4c-.4.5-1.2.8-2 .8zm50.8-6.9c-.5 0-1-.1-1.4-.4C92.6 9.6 85.8 7.8 79 7.8h-.1c-1.6 0-2.9-1.3-2.9-2.9S77.3 2 78.9 2h.1c7.9 0 15.6 2 22.4 6 1.4.8 1.9 2.6 1.1 3.9-.6.9-1.5 1.4-2.6 1.4z" class="st0"/><animateTransform attributeName="transform" attributeType="XML" dur="10s" from="0 79 47" repeatCount="indefinite" to="360 79 47" type="rotate"/></g><path fill="#ff848b" d="M27.1 68.3h26.5V76H27.1z"/><linearGradient id="SVGID_1_" x1="-13.76" x2="75.674" y1="104.809" y2="15.375" gradientTransform="translate(.932 -.95)" gradientUnits="userSpaceOnUse"><stop offset=".165" stop-color="#b833e1"/><stop offset=".958" stop-color="#ff4f5e"/></linearGradient><path fill="url(#SVGID_1_)" d="M78.9 70.6h-1.7V23c0-1.9-1.5-3.4-3.4-3.4H6.9c-1.9 0-3.4 1.5-3.4 3.4v47.6H1.7c-.9 0-1.7.8-1.7 1.7v3.5c0 .9.8 1.7 1.7 1.7h77.2c.9 0 1.7-.8 1.7-1.7v-3.5c0-.9-.8-1.7-1.7-1.7zM47.2 74H33.4v-3.4h13.7V74z"/><path fill="#bc24bc" d="M72 24.4H8.6v41.9H72V24.4z" opacity=".5"/><path d="M57 41.9c-.4 0-.7.3-.7.7v.8c-1.8-1.8-4.7-1.9-6.5-.1-.6.6-1.1 1.4-1.3 2.2-.1.4.1.7.5.8h.2c.3 0 .6-.2.6-.5.4-1.5 1.7-2.5 3.2-2.5 1 0 2 .5 2.6 1.3h-1.3c-.4 0-.7.3-.7.6 0 .4.3.7.6.7H57c.4 0 .7-.3.7-.7v-2.7c0-.3-.3-.6-.7-.6zm.1 5c-.4-.1-.7.1-.8.5-.4 1.5-1.7 2.5-3.2 2.5-1 0-2-.5-2.6-1.3h1.3c.4 0 .6-.3.6-.7 0-.3-.3-.6-.6-.6h-2.7c-.4 0-.7.3-.7.7v2.7c0 .4.3.7.6.7.4 0 .7-.3.7-.6v-.9c1.8 1.8 4.7 1.9 6.5.1.6-.6 1.1-1.4 1.3-2.2.1-.5-.1-.8-.4-.9zM28 41.5c-2.8 0-5.1 2.3-5.1 5.1s2.3 5.1 5.1 5.1 5.1-2.3 5.1-5.1-2.3-5.1-5.1-5.1zm0 8.9c-2.1 0-3.8-1.7-3.8-3.8s1.7-3.8 3.8-3.8 3.8 1.7 3.8 3.8c0 2.1-1.7 3.8-3.8 3.8zm2.2-3.8H28v-2.2c0-.2-.1-.3-.3-.3-.2 0-.3.1-.3.3v2.5c0 .2.1.3.3.3h2.5c.2 0 .3-.1.3-.3s-.1-.3-.3-.3zm7.7 4.9c-.4 0-.7-.3-.7-.7v-.1l.4-2.7-1.8-2c-.3-.3-.2-.7 0-1 .1-.1.2-.1.3-.2l2.5-.5 1.2-2.4c.2-.3.6-.5.9-.3l.3.3 1.2 2.4 2.5.5c.4.1.6.4.6.8 0 .1-.1.2-.2.3L43.4 48l.4 2.7c.1.4-.2.7-.6.8-.1 0-.3 0-.4-.1l-2.3-1.2-2.3 1.2c-.1 0-.2.1-.3.1zm-.5-5.5l1.5 1.6-.3 2.2 1.9-1 1.9 1-.3-2.2 1.5-1.6-2.1-.4-1-1.9-1 1.9-2.1.4z" class="st4"/><linearGradient id="SVGID_2_" x1="157.53" x2="261.245" y1="152.614" y2="48.899" gradientTransform="translate(-89.178 -48.95)" gradientUnits="userSpaceOnUse"><stop offset=".28" stop-color="#7542e5"/><stop offset=".417" stop-color="#824deb"/><stop offset=".789" stop-color="#a067fa"/><stop offset="1" stop-color="#ab71ff"/></linearGradient><path fill="url(#SVGID_2_)" d="M135.8 20.7h-24.6c-3.4 0-6.1 2.8-6.1 6.1v43.3c0 3.4 2.8 6.1 6.1 6.1h24.6c3.4 0 6.1-2.8 6.1-6.1V26.8c0-3.3-2.7-6.1-6.1-6.1z"/><path fill="#ab71ff" d="M120 67.2h6.9v3.4H120z"/><path d="M127.5 51.5c-.4 0-.7.3-.7.7v.8c-1.8-1.8-4.7-1.9-6.5-.1-.6.6-1.1 1.4-1.3 2.2-.1.4.1.7.5.8h.2c.3 0 .6-.2.6-.5.4-1.5 1.7-2.5 3.2-2.5 1 0 2 .5 2.6 1.3h-1.3c-.4 0-.7.3-.7.6 0 .4.3.7.6.7h2.8c.4 0 .7-.3.7-.7v-2.7c0-.3-.3-.6-.7-.6zm0 5c-.4-.1-.7.1-.8.5-.4 1.5-1.7 2.5-3.2 2.5-1 0-2-.5-2.6-1.3h1.3c.4 0 .6-.3.6-.7 0-.3-.3-.6-.6-.6h-2.7c-.4 0-.7.3-.7.7v2.7c0 .4.3.7.6.7.4 0 .7-.3.7-.6v-.9c1.8 1.8 4.7 1.9 6.5.1.6-.6 1.1-1.4 1.3-2.2.2-.5 0-.8-.4-.9zm-4-29.4c-2.8 0-5.1 2.3-5.1 5.1s2.3 5.1 5.1 5.1 5.1-2.3 5.1-5.1-2.3-5.1-5.1-5.1zm0 8.9c-2.1 0-3.8-1.7-3.8-3.8 0-2.1 1.7-3.8 3.8-3.8 2.1 0 3.8 1.7 3.8 3.8 0 2.1-1.7 3.8-3.8 3.8zm2.2-3.8h-2.2V30c0-.2-.1-.3-.3-.3-.2 0-.3.1-.3.3v2.5c0 .2.1.3.3.3h2.5c.2 0 .3-.1.3-.3s-.1-.3-.3-.3zm-4.8 16.9c-.4 0-.7-.3-.7-.7v-.1l.4-2.7-1.8-1.9c-.3-.3-.2-.7 0-1 .1-.1.2-.1.3-.2l2.5-.5 1.2-2.4c.2-.3.6-.5.9-.3l.3.3 1.2 2.4 2.5.5c.4.1.6.4.6.8 0 .1-.1.2-.2.3l-1.8 1.9.4 2.7c.1.4-.2.7-.6.8-.1 0-.3 0-.4-.1l-2.3-1.2-2.3 1.2c0 .2-.1.2-.2.2zm-.5-5.5l1.5 1.6-.3 2.2 1.9-1 1.9 1-.3-2.2 1.5-1.6-2.1-.4-1-1.9-1 1.9-2.1.4z" class="st4"/><path fill="#7542e5" d="M135.5 23.7h-24c-1.9 0-3.4 1.5-3.4 3.4v37.1H139V27.1c-.1-1.9-1.6-3.4-3.5-3.4z" opacity=".5"/><linearGradient id="SVGID_3_" x1="258.012" x2="323.576" y1="130.654" y2="65.09" gradientTransform="translate(-89.178 -48.95)" gradientUnits="userSpaceOnUse"><stop offset=".432" stop-color="#00b3f4"/><stop offset=".609" stop-color="#00bbf6"/><stop offset=".891" stop-color="#00d2fc"/><stop offset="1" stop-color="#0df"/></linearGradient><path fill="url(#SVGID_3_)" d="M229.8 22.2h-55.6c-2.5 0-4.6 2.1-4.6 4.6v43.3c0 2.5 2.1 4.6 4.6 4.6h55.6c2.5 0 4.6-2.1 4.6-4.6V26.9c0-2.6-2-4.7-4.6-4.7z"/><path fill="#0df" d="M227.3 44.2h4.3v8.5h-4.3z"/><path fill="#0090ed" d="M225.4 25.2h-51.1c-.9 0-1.7.7-1.7 1.7V70c0 .9.7 1.7 1.7 1.7h51.1V25.2z" opacity=".5"/><path d="M127.5 51.5c-.4 0-.7.3-.7.7v.8c-1.8-1.8-4.7-1.9-6.5-.1-.6.6-1.1 1.4-1.3 2.2-.1.4.1.7.5.8h.2c.3 0 .6-.2.6-.5.4-1.5 1.7-2.5 3.2-2.5 1 0 2 .5 2.6 1.3h-1.3c-.4 0-.7.3-.7.6 0 .4.3.7.6.7h2.8c.4 0 .7-.3.7-.7v-2.7c0-.3-.3-.6-.7-.6zm0 5c-.4-.1-.7.1-.8.5-.4 1.5-1.7 2.5-3.2 2.5-1 0-2-.5-2.6-1.3h1.3c.4 0 .6-.3.6-.7 0-.3-.3-.6-.6-.6h-2.7c-.4 0-.7.3-.7.7v2.7c0 .4.3.7.6.7.4 0 .7-.3.7-.6v-.9c1.8 1.8 4.7 1.9 6.5.1.6-.6 1.1-1.4 1.3-2.2.2-.5 0-.8-.4-.9zm-4-29.4c-2.8 0-5.1 2.3-5.1 5.1s2.3 5.1 5.1 5.1 5.1-2.3 5.1-5.1-2.3-5.1-5.1-5.1zm0 8.9c-2.1 0-3.8-1.7-3.8-3.8 0-2.1 1.7-3.8 3.8-3.8 2.1 0 3.8 1.7 3.8 3.8 0 2.1-1.7 3.8-3.8 3.8zm2.2-3.8h-2.2V30c0-.2-.1-.3-.3-.3-.2 0-.3.1-.3.3v2.5c0 .2.1.3.3.3h2.5c.2 0 .3-.1.3-.3s-.1-.3-.3-.3zm-4.8 16.9c-.4 0-.7-.3-.7-.7v-.1l.4-2.7-1.8-1.9c-.3-.3-.2-.7 0-1 .1-.1.2-.1.3-.2l2.5-.5 1.2-2.4c.2-.3.6-.5.9-.3l.3.3 1.2 2.4 2.5.5c.4.1.6.4.6.8 0 .1-.1.2-.2.3l-1.8 1.9.4 2.7c.1.4-.2.7-.6.8-.1 0-.3 0-.4-.1l-2.3-1.2-2.3 1.2c0 .2-.1.2-.2.2zm-.5-5.5l1.5 1.6-.3 2.2 1.9-1 1.9 1-.3-2.2 1.5-1.6-2.1-.4-1-1.9-1 1.9-2.1.4zm94 .2c-.4 0-.7.3-.7.7v.8c-1.8-1.8-4.7-1.9-6.5-.1-.6.6-1.1 1.4-1.3 2.2-.1.4.1.7.5.8h.2c.3 0 .6-.2.6-.5.4-1.5 1.7-2.5 3.2-2.5 1 0 2 .5 2.6 1.3h-1.3c-.4 0-.7.3-.7.6 0 .4.3.7.6.7h2.8c.4 0 .7-.3.7-.7v-2.7c0-.3-.3-.6-.7-.6zm.1 5c-.4-.1-.7.1-.8.5-.4 1.5-1.7 2.5-3.2 2.5-1 0-2-.5-2.6-1.3h1.3c.4 0 .6-.3.6-.7 0-.3-.3-.6-.6-.6h-2.7c-.4 0-.7.3-.7.7v2.7c0 .4.3.7.6.7.4 0 .7-.3.7-.6v-.9c1.8 1.8 4.7 1.9 6.5.1.6-.6 1.1-1.4 1.3-2.2.1-.5-.1-.8-.4-.9zm-29.1-5.4c-2.8 0-5.1 2.3-5.1 5.1s2.3 5.1 5.1 5.1 5.1-2.3 5.1-5.1-2.3-5.1-5.1-5.1zm0 8.9c-2.1 0-3.8-1.7-3.8-3.8 0-2.1 1.7-3.8 3.8-3.8 2.1 0 3.8 1.7 3.8 3.8 0 2.1-1.7 3.8-3.8 3.8zm2.2-3.8h-2.2v-2.2c0-.2-.1-.3-.3-.3-.2 0-.3.1-.3.3v2.5c0 .2.1.3.3.3h2.5c.2 0 .3-.1.3-.3s-.1-.3-.3-.3zm7.7 4.9c-.4 0-.7-.3-.7-.7v-.1l.4-2.7-1.8-1.9c-.3-.3-.2-.7 0-1 .1-.1.2-.1.3-.2l2.5-.5 1.2-2.4c.2-.3.6-.5.9-.3l.3.3 1.2 2.4 2.5.5c.4.1.6.4.6.8 0 .1-.1.2-.2.3l-1.8 1.9.4 2.7c.1.4-.2.7-.6.8-.1 0-.3 0-.4-.1l-2.3-1.2-2.3 1.2c0 .2-.1.2-.2.2zm-.5-5.5l1.5 1.6-.3 2.2 1.9-1 1.9 1-.3-2.2 1.5-1.6-2.1-.4-1-1.9-1 1.9-2.1.4z" class="st4"/></svg>
\ No newline at end of file
--- a/browser/components/newtab/docs/v2-system-addon/data_events.md
+++ b/browser/components/newtab/docs/v2-system-addon/data_events.md
@@ -1048,8 +1048,25 @@ This reports a failure in the Remote Set
   "addon_version": "20180710100040",
   "locale": "en-US",
   "user_prefs": 7,
   "event": ["ASR_RS_NO_MESSAGES" | "ASR_RS_ERROR"],
   // The value is set to the ID of the message provider. For example: remote-cfr, remote-onboarding, etc.
   "value": "REMOTE_PROVIDER_ID"
 }
 ```
+
+## Trailhead experiment enrollment ping
+
+This reports an enrollment ping when a user gets enrolled in a Trailhead experiment. Note that this ping is only collected through the Mozilla Events telemetry pipeline.
+
+```js
+{
+  "category": "activity_stream",
+  "method": "enroll",
+  "object": "preference_study"
+  "value": "activity-stream-firstup-trailhead-interrupts",
+  "extra_keys": {
+    "experimentType": "as-firstrun",
+    "branch": ["supercharge" | "join" | "sync" | "privacy" ...]
+  }
+}
+```
--- a/browser/components/newtab/lib/ASRouter.jsm
+++ b/browser/components/newtab/lib/ASRouter.jsm
@@ -37,16 +37,17 @@ ChromeUtils.defineModuleGetter(this, "Cl
   "resource://normandy/lib/ClientEnvironment.jsm");
 ChromeUtils.defineModuleGetter(this, "Sampling",
   "resource://gre/modules/components-utils/Sampling.jsm");
 
 const TRAILHEAD_CONFIG = {
   OVERRIDE_PREF: "trailhead.firstrun.branches",
   DID_SEE_ABOUT_WELCOME_PREF: "trailhead.firstrun.didSeeAboutWelcome",
   INTERRUPTS_EXPERIMENT_PREF: "trailhead.firstrun.interruptsExperiment",
+  TRIPLETS_ENROLLED_PREF: "trailhead.firstrun.tripletsEnrolled",
   BRANCHES: {
     interrupts: [
       ["control"],
       ["join"],
       ["sync"],
       ["nofirstrun"],
       ["cards"],
     ],
@@ -727,38 +728,56 @@ class _ASRouter {
     } else {
       // If the user is not in a trailhead-compabtible locale, return the control experience and no experiment.
       interrupt = "control";
     }
 
     return {experiment, interrupt, triplet};
   }
 
+  // Dispatch a TRAILHEAD_ENROLL_EVENT action
+  _sendTrailheadEnrollEvent(data) {
+    this.dispatchToAS({
+      type: at.TRAILHEAD_ENROLL_EVENT,
+      data,
+    });
+  }
+
   async setupTrailhead() {
     // Don't initialize
     if (this.state.trailheadInitialized || !Services.prefs.getBoolPref(TRAILHEAD_CONFIG.DID_SEE_ABOUT_WELCOME_PREF, false)) {
       return;
     }
 
     const {experiment, interrupt, triplet} = await this._generateTrailheadBranches();
     await this.setState({trailheadInitialized: true, trailheadInterrupt: interrupt, trailheadTriplet: triplet});
 
     if (experiment) {
+      // In order for ping centre to pick this up, it MUST contain a substring activity-stream
+      const experimentName = `activity-stream-firstrun-trailhead-${experiment}`;
+
       TelemetryEnvironment.setExperimentActive(
-        // In order for ping centre to pick this up, it MUST start with activity-stream
-        `activity-stream-firstrun-trailhead-${experiment}`,
+        experimentName,
         experiment === "interrupts" ? interrupt : triplet,
         {type: "as-firstrun"}
       );
 
       // On the first time setting the interrupts experiment, expose the branch
-      // for normandy to target for survey study.
+      // for normandy to target for survey study, and send out the enrollment ping.
       if (experiment === "interrupts" &&
           !Services.prefs.prefHasUserValue(TRAILHEAD_CONFIG.INTERRUPTS_EXPERIMENT_PREF)) {
         Services.prefs.setStringPref(TRAILHEAD_CONFIG.INTERRUPTS_EXPERIMENT_PREF, interrupt);
+        this._sendTrailheadEnrollEvent({experiment: experimentName, type: "as-firstrun", branch: interrupt});
+      }
+
+      // On the first time setting the triplets experiment, send out the enrollment ping.
+      if (experiment === "triplets" &&
+          !Services.prefs.getBoolPref(TRAILHEAD_CONFIG.TRIPLETS_ENROLLED_PREF, false)) {
+        Services.prefs.setBoolPref(TRAILHEAD_CONFIG.TRIPLETS_ENROLLED_PREF, true);
+        this._sendTrailheadEnrollEvent({experiment: experimentName, type: "as-firstrun", branch: triplet});
       }
     }
   }
 
   // Return an object containing targeting parameters used to select messages
   _getMessagesContext() {
     const {previousSessionEnd, trailheadInterrupt, trailheadTriplet} = this.state;
 
--- a/browser/components/newtab/lib/OnboardingMessageProvider.jsm
+++ b/browser/components/newtab/lib/OnboardingMessageProvider.jsm
@@ -338,17 +338,17 @@ const ONBOARDING_MESSAGES = async () => 
     content: {
       title: {string_id: "onboarding-send-tabs-title"},
       text: {string_id: "onboarding-send-tabs-text"},
       icon: "sendtab",
       primary_button: {
         label: {string_id: "onboarding-send-tabs-button"},
         action: {
           type: "OPEN_URL",
-          data: {args: "https://blog.mozilla.org/firefox/send-tabs-a-better-way/", where: "tabshifted"},
+          data: {args: "https://support.mozilla.org/kb/send-tab-firefox-desktop-mobile", where: "tabshifted"},
         },
       },
     },
     targeting: "trailheadTriplet == 'multidevice'",
     trigger: {id: "showOnboarding"},
   },
   {
     id: "TRAILHEAD_CARD_8",
--- a/browser/components/newtab/lib/TelemetryFeed.jsm
+++ b/browser/components/newtab/lib/TelemetryFeed.jsm
@@ -658,16 +658,24 @@ this.TelemetryFeed = class TelemetryFeed
     let event = this.createASRouterEvent(action);
     this.sendASRouterEvent(event);
   }
 
   handleUndesiredEvent(action) {
     this.sendEvent(this.createUndesiredEvent(action));
   }
 
+  handleTrailheadEnrollEvent(action) {
+    // Unlike `sendUTEvent`, we always send the event if AS's telemetry is enabled
+    // regardless of `this.eventTelemetryEnabled`.
+    if (this.telemetryEnabled) {
+      this.utEvents.sendTrailheadEnrollEvent(action.data);
+    }
+  }
+
   async sendPageTakeoverData() {
     if (this.telemetryEnabled) {
       const value = {};
       let newtabAffected = false;
       let homeAffected = false;
 
       // Check whether or not about:home and about:newtab are set to a custom URL.
       // If so, classify them.
@@ -759,16 +767,19 @@ this.TelemetryFeed = class TelemetryFeed
         this.handleUserEvent(action);
         break;
       case at.AS_ROUTER_TELEMETRY_USER_EVENT:
         this.handleASRouterUserEvent(action);
         break;
       case at.TELEMETRY_PERFORMANCE_EVENT:
         this.sendEvent(this.createPerformanceEvent(action));
         break;
+      case at.TRAILHEAD_ENROLL_EVENT:
+        this.handleTrailheadEnrollEvent(action);
+        break;
       case at.UNINIT:
         this.uninit();
         break;
     }
   }
 
   /**
    * Handle impression stats actions from Discovery Stream. The data will be
--- a/browser/components/newtab/lib/UTEventReporting.jsm
+++ b/browser/components/newtab/lib/UTEventReporting.jsm
@@ -13,16 +13,17 @@ const {Services} = ChromeUtils.import("r
   */
 const EXTRAS_FIELD_NAMES = ["addon_version", "session_id", "page", "user_prefs", "action_position"];
 
 this.UTEventReporting = class UTEventReporting {
   constructor() {
     Services.telemetry.setEventRecordingEnabled("activity_stream", true);
     this.sendUserEvent = this.sendUserEvent.bind(this);
     this.sendSessionEndEvent = this.sendSessionEndEvent.bind(this);
+    this.sendTrailheadEnrollEvent = this.sendTrailheadEnrollEvent.bind(this);
   }
 
   _createExtras(data) {
     // Make a copy of the given data and delete/modify it as needed.
     let utExtras = Object.assign({}, data);
     for (let field of Object.keys(utExtras)) {
       if (EXTRAS_FIELD_NAMES.includes(field)) {
         utExtras[field] = String(utExtras[field]);
@@ -48,14 +49,27 @@ this.UTEventReporting = class UTEventRep
     Services.telemetry.recordEvent(
       "activity_stream",
       "end",
       "session",
       String(data.session_duration),
       this._createExtras(data));
   }
 
+  sendTrailheadEnrollEvent(data) {
+    Services.telemetry.recordEvent(
+      "activity_stream",
+      "enroll",
+      "preference_study",
+      data.experiment,
+      {
+        experimentType: data.type,
+        branch: data.branch,
+      }
+    );
+  }
+
   uninit() {
     Services.telemetry.setEventRecordingEnabled("activity_stream", false);
   }
 };
 
 const EXPORTED_SYMBOLS = ["UTEventReporting"];
--- a/browser/components/newtab/locales-src/he/strings.properties
+++ b/browser/components/newtab/locales-src/he/strings.properties
@@ -29,18 +29,18 @@ type_label_downloaded=התקבל
 # LOCALIZATION NOTE (menu_action_bookmark): Bookmark is a verb, as in "Add to
 # bookmarks"
 menu_action_bookmark=הוספת סימנייה
 menu_action_remove_bookmark=הסרת סימנייה
 menu_action_open_new_window=פתיחה בחלון חדש
 menu_action_open_private_window=פתיחה בלשונית פרטית חדשה
 menu_action_dismiss=הסרה
 menu_action_delete=מחיקה מההיסטוריה
-menu_action_pin=הצמדה
-menu_action_unpin=ביטול הצמדה
+menu_action_pin=נעיצה
+menu_action_unpin=ביטול נעיצה
 confirm_history_delete_p1=למחוק כל עותק של העמוד הזה מההיסטוריה שלך?
 # LOCALIZATION NOTE (confirm_history_delete_notice_p2): this string is displayed in
 # the same dialog as confirm_history_delete_p1. "This action" refers to deleting a
 # page from history.
 confirm_history_delete_notice_p2=לא ניתן לבטל פעולה זו.
 menu_action_save_to_pocket=שמירה ל־Pocket
 menu_action_delete_pocket=מחיקה מ־Pocket
 menu_action_archive_pocket=העברה לארכיון ב־Pocket
@@ -88,16 +88,17 @@ section_disclaimer_topstories_buttontext=בסדר, הבנתי
 # for a "Firefox Home" section. "Firefox" should be treated as a brand and kept
 # in English, while "Home" should be localized matching the about:preferences
 # sidebar mozilla-central string for the panel that has preferences related to
 # what is shown for the homepage, new windows, and new tabs.
 prefs_home_header=תוכן מסך הבית של Firefox
 prefs_home_description=בחירת תוכן שיוצג במסך הבית של Firefox.
 
 prefs_content_discovery_header=מסך הבית של Firefox
+
 prefs_content_discovery_description=גילוי תוכן במסך הבית של Firefox מאפשר לך לגלות מאמרים רלוונטים ובאיכות גבוהה מכל רחבי הרשת.
 prefs_content_discovery_button=השבתת גילוי תוכן
 
 # LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
 # plural forms used in a drop down of multiple row options (1 row, 2 rows).
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 prefs_section_rows_option=שורה אחת;{num} שורות
 prefs_search_header=חיפוש ברשת
@@ -146,16 +147,17 @@ topsites_form_image_validation=טעינת התמונה נכשלה. נא לנסות כתובת שונה.
 # trending stories section and precedes a list of links to popular topics.
 pocket_read_more=נושאים פופולריים:
 # LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
 # end of the list of popular topic links.
 pocket_read_even_more=צפייה בחדשות נוספות
 pocket_more_reccommendations=המלצות נוספות
 pocket_how_it_works=איך זה עובד
 pocket_cta_button=קבלת Pocket
+pocket_cta_text=שמירת הסיפורים שאהבת ב־Pocket על מנת למלא את מחשבתך בקריאה מרתקת.
 
 highlights_empty_state=ניתן להתחיל בגלישה ואנו נציג בפניך מספר כתבות, סרטונים ועמודים שונים מעולים בהם ביקרת לאחרונה או שהוספת לסימניות.
 # LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
 # in the space that would have shown a few stories, this is shown instead.
 # {provider} is replaced by the name of the content provider for this section.
 topstories_empty_state=התעדכנת בכל הסיפורים. כדאי לנסות שוב מאוחר יותר כדי לקבל עוד סיפורים מובילים מאת {provider}. לא רוצה לחכות? ניתן לבחור נושא נפוץ כדי למצוא עוד סיפורים נפלאים מרחבי הרשת.
 
 # LOCALIZATION NOTE (manual_migration_explanation2): This message is shown to encourage users to
--- a/browser/components/newtab/locales-src/it/strings.properties
+++ b/browser/components/newtab/locales-src/it/strings.properties
@@ -97,18 +97,18 @@ section_menu_action_manage_section=Gesti
 section_menu_action_manage_webext=Gestisci estensione
 section_menu_action_add_topsite=Aggiungi sito principale
 section_menu_action_add_search_engine=Aggiungi motore di ricerca
 section_menu_action_move_up=Sposta su
 section_menu_action_move_down=Sposta giù
 section_menu_action_privacy_notice=Informativa sulla privacy
 firstrun_title=Porta Firefox con te
 firstrun_content=Ritrova segnalibri, cronologia, password e altre impostazioni su tutti i tuoi dispositivi.
-firstrun_learn_more_link=Scopri di più sull’account Firefox
-firstrun_form_header=Inserisci email
+firstrun_learn_more_link=Ulteriori informazioni sull’account Firefox
+firstrun_form_header=Inserisci la tua email
 firstrun_form_sub_header=per utilizzare Firefox Sync.
 firstrun_email_input_placeholder=Email
 firstrun_invalid_input=Inserire un indirizzo email valido
 firstrun_extra_legal_links=Proseguendo, accetto le {terms} e l’{privacy}.
 firstrun_terms_of_service=condizioni di utilizzo del servizio
 firstrun_privacy_notice=informativa sulla privacy
 firstrun_continue_to_login=Continua
 firstrun_skip_login=Ignora questo passaggio
--- a/browser/components/newtab/locales-src/zh-TW/strings.properties
+++ b/browser/components/newtab/locales-src/zh-TW/strings.properties
@@ -88,16 +88,17 @@ section_disclaimer_topstories_buttontext=好的,知道了
 # for a "Firefox Home" section. "Firefox" should be treated as a brand and kept
 # in English, while "Home" should be localized matching the about:preferences
 # sidebar mozilla-central string for the panel that has preferences related to
 # what is shown for the homepage, new windows, and new tabs.
 prefs_home_header=Firefox 首頁內容
 prefs_home_description=選擇要在您的 Firefox 首頁顯示哪些內容。
 
 prefs_content_discovery_header=Firefox 首頁
+
 prefs_content_discovery_description=Firefox Home 的內容探索功能可隨您上網,為您尋找高品質而與您有關的文章。
 prefs_content_discovery_button=關閉內容探索功能
 
 # LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
 # plural forms used in a drop down of multiple row options (1 row, 2 rows).
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 prefs_section_rows_option={num} 行
 prefs_search_header=網頁搜尋
@@ -185,17 +186,17 @@ section_menu_action_add_topsite=新增熱門網站
 section_menu_action_add_search_engine=新增搜尋引擎
 section_menu_action_move_up=上移
 section_menu_action_move_down=下移
 section_menu_action_privacy_notice=隱私權公告
 
 # LOCALIZATION NOTE (firstrun_*). These strings are displayed only once, on the
 # firstrun of the browser, they give an introduction to Firefox and Sync.
 firstrun_title=Firefox 隨身帶著走
-firstrun_content=在您的任何裝置上取得書籤、瀏覽紀錄、密碼及其他設定。
+firstrun_content=在您的各種裝置上同步書籤、瀏覽紀錄、登入資訊及其他設定。
 firstrun_learn_more_link=了解 Firefox Accounts 的更多資訊
 
 # LOCALIZATION NOTE (firstrun_form_header and firstrun_form_sub_header):
 # firstrun_form_sub_header is a continuation of firstrun_form_header, they are one sentence.
 # firstrun_form_header is displayed more boldly as the call to action.
 firstrun_form_header=輸入您的電子郵件地址
 firstrun_form_sub_header=繼續前往 Firefox Sync
 
--- a/browser/components/newtab/prerendered/locales/he/activity-stream-strings.js
+++ b/browser/components/newtab/prerendered/locales/he/activity-stream-strings.js
@@ -12,18 +12,18 @@ window.gActivityStreamStrings = {
   "type_label_pocket": "נשמר ל־Pocket",
   "type_label_downloaded": "התקבל",
   "menu_action_bookmark": "הוספת סימנייה",
   "menu_action_remove_bookmark": "הסרת סימנייה",
   "menu_action_open_new_window": "פתיחה בחלון חדש",
   "menu_action_open_private_window": "פתיחה בלשונית פרטית חדשה",
   "menu_action_dismiss": "הסרה",
   "menu_action_delete": "מחיקה מההיסטוריה",
-  "menu_action_pin": "הצמדה",
-  "menu_action_unpin": "ביטול הצמדה",
+  "menu_action_pin": "נעיצה",
+  "menu_action_unpin": "ביטול נעיצה",
   "confirm_history_delete_p1": "למחוק כל עותק של העמוד הזה מההיסטוריה שלך?",
   "confirm_history_delete_notice_p2": "לא ניתן לבטל פעולה זו.",
   "menu_action_save_to_pocket": "שמירה ל־Pocket",
   "menu_action_delete_pocket": "מחיקה מ־Pocket",
   "menu_action_archive_pocket": "העברה לארכיון ב־Pocket",
   "menu_action_show_file_mac_os": "הצגה ב־Finder",
   "menu_action_show_file_windows": "פתיחת תיקייה מכילה",
   "menu_action_show_file_linux": "פתיחת תיקייה מכילה",
@@ -73,17 +73,17 @@ window.gActivityStreamStrings = {
   "topsites_form_cancel_button": "ביטול",
   "topsites_form_url_validation": "נדרשת כתובת תקינה",
   "topsites_form_image_validation": "טעינת התמונה נכשלה. נא לנסות כתובת שונה.",
   "pocket_read_more": "נושאים פופולריים:",
   "pocket_read_even_more": "צפייה בחדשות נוספות",
   "pocket_more_reccommendations": "המלצות נוספות",
   "pocket_how_it_works": "איך זה עובד",
   "pocket_cta_button": "קבלת Pocket",
-  "pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
+  "pocket_cta_text": "שמירת הסיפורים שאהבת ב־Pocket על מנת למלא את מחשבתך בקריאה מרתקת.",
   "highlights_empty_state": "ניתן להתחיל בגלישה ואנו נציג בפניך מספר כתבות, סרטונים ועמודים שונים מעולים בהם ביקרת לאחרונה או שהוספת לסימניות.",
   "topstories_empty_state": "התעדכנת בכל הסיפורים. כדאי לנסות שוב מאוחר יותר כדי לקבל עוד סיפורים מובילים מאת {provider}. לא רוצה לחכות? ניתן לבחור נושא נפוץ כדי למצוא עוד סיפורים נפלאים מרחבי הרשת.",
   "error_fallback_default_info": "אופס, משהו השתבש בעת טעינת התוכן הזה.",
   "error_fallback_default_refresh_suggestion": "נא לרענן את הדף כדי לנסות שוב.",
   "section_menu_action_remove_section": "הסרת מדור",
   "section_menu_action_collapse_section": "צמצום מדור",
   "section_menu_action_expand_section": "הרחבת מדור",
   "section_menu_action_manage_section": "ניהול מדור",
--- a/browser/components/newtab/prerendered/locales/it/activity-stream-strings.js
+++ b/browser/components/newtab/prerendered/locales/it/activity-stream-strings.js
@@ -90,18 +90,18 @@ window.gActivityStreamStrings = {
   "section_menu_action_manage_webext": "Gestisci estensione",
   "section_menu_action_add_topsite": "Aggiungi sito principale",
   "section_menu_action_add_search_engine": "Aggiungi motore di ricerca",
   "section_menu_action_move_up": "Sposta su",
   "section_menu_action_move_down": "Sposta giù",
   "section_menu_action_privacy_notice": "Informativa sulla privacy",
   "firstrun_title": "Porta Firefox con te",
   "firstrun_content": "Ritrova segnalibri, cronologia, password e altre impostazioni su tutti i tuoi dispositivi.",
-  "firstrun_learn_more_link": "Scopri di più sull’account Firefox",
-  "firstrun_form_header": "Inserisci email",
+  "firstrun_learn_more_link": "Ulteriori informazioni sull’account Firefox",
+  "firstrun_form_header": "Inserisci la tua email",
   "firstrun_form_sub_header": "per utilizzare Firefox Sync.",
   "firstrun_email_input_placeholder": "Email",
   "firstrun_invalid_input": "Inserire un indirizzo email valido",
   "firstrun_extra_legal_links": "Proseguendo, accetto le {terms} e l’{privacy}.",
   "firstrun_terms_of_service": "condizioni di utilizzo del servizio",
   "firstrun_privacy_notice": "informativa sulla privacy",
   "firstrun_continue_to_login": "Continua",
   "firstrun_skip_login": "Ignora questo passaggio",
--- a/browser/components/newtab/prerendered/locales/zh-TW/activity-stream-strings.js
+++ b/browser/components/newtab/prerendered/locales/zh-TW/activity-stream-strings.js
@@ -89,17 +89,17 @@ window.gActivityStreamStrings = {
   "section_menu_action_manage_section": "管理段落",
   "section_menu_action_manage_webext": "管理擴充套件",
   "section_menu_action_add_topsite": "新增熱門網站",
   "section_menu_action_add_search_engine": "新增搜尋引擎",
   "section_menu_action_move_up": "上移",
   "section_menu_action_move_down": "下移",
   "section_menu_action_privacy_notice": "隱私權公告",
   "firstrun_title": "Firefox 隨身帶著走",
-  "firstrun_content": "在您的任何裝置上取得書籤、瀏覽紀錄、密碼及其他設定。",
+  "firstrun_content": "在您的各種裝置上同步書籤、瀏覽紀錄、登入資訊及其他設定。",
   "firstrun_learn_more_link": "了解 Firefox Accounts 的更多資訊",
   "firstrun_form_header": "輸入您的電子郵件地址",
   "firstrun_form_sub_header": "繼續前往 Firefox Sync",
   "firstrun_email_input_placeholder": "電子郵件",
   "firstrun_invalid_input": "必須輸入有效的電子郵件地址",
   "firstrun_extra_legal_links": "若繼續,代表您同意{terms}及{privacy}。",
   "firstrun_terms_of_service": "服務條款",
   "firstrun_privacy_notice": "隱私權公告",
--- a/browser/components/newtab/test/schemas/pings.js
+++ b/browser/components/newtab/test/schemas/pings.js
@@ -242,16 +242,29 @@ export const ASRouterEventPing = Joi.obj
 export const UTSessionPing = Joi.array().items(
   Joi.string().required().valid("activity_stream"),
   Joi.string().required().valid("end"),
   Joi.string().required().valid("session"),
   Joi.string().required(),
   eventsTelemetryExtraKeys
 );
 
+export const trailheadEnrollExtraKeys = Joi.object().keys({
+  experimentType: Joi.string().required(),
+  branch: Joi.string().required(),
+}).options({allowUnknown: false});
+
+export const UTTrailheadEnrollPing = Joi.array().items(
+  Joi.string().required().valid("activity_stream"),
+  Joi.string().required().valid("enroll"),
+  Joi.string().required().valid("preference_study"),
+  Joi.string().required(),
+  trailheadEnrollExtraKeys
+);
+
 export function chaiAssertions(_chai, utils) {
   const {Assertion} = _chai;
 
   Assertion.addMethod("validate", function(schema, schemaName) {
     const {error} = Joi.validate(this._obj, schema, {allowUnknown: false});
     this.assert(
       !error,
       `Expected to be ${schemaName ? `a valid ${schemaName}` : "valid"} but there were errors: ${error}`
--- a/browser/components/newtab/test/unit/asrouter/ASRouter.test.js
+++ b/browser/components/newtab/test/unit/asrouter/ASRouter.test.js
@@ -1596,23 +1596,28 @@ describe("ASRouter", () => {
 
       assert.propertyVal(Router._getMessagesContext(), "trailheadInterrupt", "join");
       assert.propertyVal(Router._getMessagesContext(), "trailheadTriplet", "privacy");
     });
 
     describe(".setupTrailhead", () => {
       let getBoolPrefStub;
       let setStringPrefStub;
+      let setBoolPrefStub;
 
       beforeEach(() => {
-        getBoolPrefStub = sandbox.stub(global.Services.prefs, "getBoolPref").withArgs(TRAILHEAD_CONFIG.DID_SEE_ABOUT_WELCOME_PREF).returns(true);
+        getBoolPrefStub = sandbox.stub(global.Services.prefs, "getBoolPref");
+        getBoolPrefStub.withArgs(TRAILHEAD_CONFIG.DID_SEE_ABOUT_WELCOME_PREF).returns(true);
+        getBoolPrefStub.withArgs(TRAILHEAD_CONFIG.TRIPLETS_ENROLLED_PREF).returns(false);
         setStringPrefStub = sandbox.stub(global.Services.prefs, "setStringPref");
+        setBoolPrefStub = sandbox.stub(global.Services.prefs, "setBoolPref");
       });
 
-      const configWithExperiment = {experiment: "interrupts", interrupt: "join", triplet: "privacy"};
+      const configWithInterruptsExperiment = {experiment: "interrupts", interrupt: "join", triplet: "privacy"};
+      const configWithTripletsExperiment = {experiment: "triplets", interrupt: "join", triplet: "privacy"};
       const configWithoutExperiment = {experiment: "", interrupt: "control", triplet: ""};
 
       it("should generates an experiment/branch configuration and update Router.state", async () => {
         const config = configWithoutExperiment;
         sandbox.stub(Router, "_generateTrailheadBranches").resolves(config);
 
         await Router.setupTrailhead();
 
@@ -1632,24 +1637,45 @@ describe("ASRouter", () => {
       it("should return early if DID_SEE_ABOUT_WELCOME_PREF is false", async () => {
         getBoolPrefStub.withArgs(TRAILHEAD_CONFIG.DID_SEE_ABOUT_WELCOME_PREF).returns(false);
 
         await Router.setupTrailhead();
 
         sandbox.spy(Router, "setState");
         assert.notCalled(Router.setState);
       });
-      it("should set active experiment if one is defined", async () => {
-        sandbox.stub(Router, "_generateTrailheadBranches").resolves(configWithExperiment);
+      it("should set active interrupts experiment if one is defined", async () => {
+        sandbox.stub(Router, "_generateTrailheadBranches").resolves(configWithInterruptsExperiment);
         sandbox.stub(global.TelemetryEnvironment, "setExperimentActive");
+        sandbox.spy(Router, "_sendTrailheadEnrollEvent");
 
         await Router.setupTrailhead();
 
         assert.calledOnce(global.TelemetryEnvironment.setExperimentActive);
         assert.calledWith(setStringPrefStub, TRAILHEAD_CONFIG.INTERRUPTS_EXPERIMENT_PREF, "join");
+        assert.calledWith(Router._sendTrailheadEnrollEvent, {
+          experiment: "activity-stream-firstrun-trailhead-interrupts",
+          type: "as-firstrun",
+          branch: "join",
+        });
+      });
+      it("should set active triplets experiment if one is defined", async () => {
+        sandbox.stub(Router, "_generateTrailheadBranches").resolves(configWithTripletsExperiment);
+        sandbox.stub(global.TelemetryEnvironment, "setExperimentActive");
+        sandbox.spy(Router, "_sendTrailheadEnrollEvent");
+
+        await Router.setupTrailhead();
+
+        assert.calledOnce(global.TelemetryEnvironment.setExperimentActive);
+        assert.calledWith(setBoolPrefStub, TRAILHEAD_CONFIG.TRIPLETS_ENROLLED_PREF, true);
+        assert.calledWith(Router._sendTrailheadEnrollEvent, {
+          experiment: "activity-stream-firstrun-trailhead-triplets",
+          type: "as-firstrun",
+          branch: "privacy",
+        });
       });
       it("should not set an active experiment if no experiment is defined", async () => {
         sandbox.stub(Router, "_generateTrailheadBranches").resolves(configWithoutExperiment);
         sandbox.stub(global.TelemetryEnvironment, "setExperimentActive");
 
         await Router.setupTrailhead();
 
         assert.notCalled(global.TelemetryEnvironment.setExperimentActive);
--- a/browser/components/newtab/test/unit/asrouter/templates/Trailhead.test.jsx
+++ b/browser/components/newtab/test/unit/asrouter/templates/Trailhead.test.jsx
@@ -58,16 +58,21 @@ describe("<Trailhead>", () => {
     assert.ok(skipButton.exists());
     skipButton.simulate("click");
 
     assert.calledOnce(dispatch);
     assert.isUserEventAction(dispatch.firstCall.args[0]);
     assert.calledWith(dispatch, ac.UserEvent({event: at.SKIPPED_SIGNIN, value: {has_flow_params: false}}));
   });
 
+  it("should NOT emit UserEvent SKIPPED_SIGNIN when closeModal is triggered by visibilitychange event", () => {
+    wrapper.instance().closeModal({type: "visibilitychange"});
+    assert.notCalled(dispatch);
+  });
+
   it("should emit UserEvent SUBMIT_EMAIL when you submit the form", () => {
     let form = wrapper.find("form");
     assert.ok(form.exists());
     form.simulate("submit");
 
     assert.calledOnce(dispatch);
     assert.isUserEventAction(dispatch.firstCall.args[0]);
     assert.calledWith(dispatch, ac.UserEvent({event: at.SUBMIT_EMAIL, value: {has_flow_params: false}}));
--- a/browser/components/newtab/test/unit/content-src/lib/selectLayoutRender.test.js
+++ b/browser/components/newtab/test/unit/content-src/lib/selectLayoutRender.test.js
@@ -348,9 +348,28 @@ describe("selectLayoutRender", () => {
     store.dispatch({type: at.DISCOVERY_STREAM_FEED_UPDATE, data: {feed: {data: {recommendations: [{name: "rec"}]}}, url: "foo3.com"}});
     store.dispatch({type: at.DISCOVERY_STREAM_FEED_UPDATE, data: {feed: {data: {recommendations: []}}, url: "foo4.com"}});
     store.dispatch({type: at.DISCOVERY_STREAM_SPOCS_UPDATE, data: fakeSpocsData});
 
     const {layoutRender} = selectLayoutRender(store.getState().DiscoveryStream, {}, []);
 
     assert.deepEqual(layoutRender[0].components[2].data.recommendations[0], {name: "rec", pos: 0});
   });
+  it("should not render a row if no components exist after filter in that row", () => {
+    const fakeLayout = [{
+      width: 3,
+      components: [
+        {type: "TopSites"},
+      ],
+    }, {
+      width: 3,
+      components: [
+        {type: "Message"},
+      ],
+    }];
+    store.dispatch({type: at.DISCOVERY_STREAM_LAYOUT_UPDATE, data: {layout: fakeLayout}});
+
+    const {layoutRender} = selectLayoutRender(store.getState().DiscoveryStream, {"feeds.topsites": true}, []);
+
+    assert.equal(layoutRender[0].components[0].type, "TopSites");
+    assert.equal(layoutRender[1], undefined);
+  });
 });
--- a/browser/components/newtab/test/unit/lib/TelemetryFeed.test.js
+++ b/browser/components/newtab/test/unit/lib/TelemetryFeed.test.js
@@ -24,17 +24,22 @@ describe("TelemetryFeed", () => {
   let expectedUserPrefs;
   let browser = {getAttribute() { return "true"; }};
   let instance;
   let clock;
   let fakeHomePageUrl;
   let fakeHomePage;
   let fakeExtensionSettingsStore;
   class PingCentre {sendPing() {} uninit() {} sendStructuredIngestionPing() {}}
-  class UTEventReporting {sendUserEvent() {} sendSessionEndEvent() {} uninit() {}}
+  class UTEventReporting {
+    sendUserEvent() {}
+    sendSessionEndEvent() {}
+    sendTrailheadEnrollEvent() {}
+    uninit() {}
+  }
   class PerfService {
     getMostRecentAbsMarkStartByName() { return 1234; }
     mark() {}
     absNow() { return 123; }
     get timeOrigin() { return 123456; }
   }
   const perfService = new PerfService();
   const {
@@ -1127,16 +1132,25 @@ describe("TelemetryFeed", () => {
       ];
       const action = ac.DiscoveryStreamSpocsFill({spoc_fills: spocFills});
 
       instance.onAction(action);
 
       assert.calledWith(eventCreator, action.data);
       assert.calledWith(sendEvent, eventCreator.returnValue);
     });
+    it("should call .handleTrailheadEnrollEvent on a TRAILHEAD_ENROLL_EVENT action", () => {
+      const data = {experiment: "foo", type: "bar", branch: "baz"};
+      const action = {type: at.TRAILHEAD_ENROLL_EVENT, data};
+      sandbox.spy(instance, "handleTrailheadEnrollEvent");
+
+      instance.onAction(action);
+
+      assert.calledWith(instance.handleTrailheadEnrollEvent, action);
+    });
   });
   describe("#handlePagePrerendered", () => {
     it("should not throw if there is no session for the given port ID", () => {
       assert.doesNotThrow(() => instance.handlePagePrerendered("doesn't exist"));
     });
     it("should set the session as prerendered on a PAGE_PRERENDERED action", () => {
       const session = {perf: {}};
       sandbox.stub(instance.sessions, "get").returns(session);
@@ -1358,9 +1372,31 @@ describe("TelemetryFeed", () => {
       FakePrefs.prototype.prefs[STRUCTURED_INGESTION_ENDPOINT_PREF] = fakeEndpoint;
       sandbox.stub(global.gUUIDGenerator, "generateUUID").returns(fakeUUID);
       const feed = new TelemetryFeed();
       const url = feed._generateStructuredIngestionEndpoint("testPingType", "1");
 
       assert.equal(url, `${fakeEndpoint}/testPingType/1/${fakeUUIDWithoutBraces}`);
     });
   });
+  describe("#handleTrailheadEnrollEvent", () => {
+    it("should send a TRAILHEAD_ENROLL_EVENT if the telemetry is enabled", () => {
+      FakePrefs.prototype.prefs[TELEMETRY_PREF] = true;
+      const data = {experiment: "foo", type: "bar", branch: "baz"};
+      instance = new TelemetryFeed();
+      sandbox.stub(instance.utEvents, "sendTrailheadEnrollEvent");
+
+      instance.handleTrailheadEnrollEvent({data});
+
+      assert.calledWith(instance.utEvents.sendTrailheadEnrollEvent, data);
+    });
+    it("should not send TRAILHEAD_ENROLL_EVENT if the telemetry is disabled", () => {
+      FakePrefs.prototype.prefs[TELEMETRY_PREF] = false;
+      const data = {experiment: "foo", type: "bar", branch: "baz"};
+      instance = new TelemetryFeed();
+      sandbox.stub(instance.utEvents, "sendTrailheadEnrollEvent");
+
+      instance.handleTrailheadEnrollEvent({data});
+
+      assert.notCalled(instance.utEvents.sendTrailheadEnrollEvent);
+    });
+  });
 });
--- a/browser/components/newtab/test/unit/lib/UTEventReporting.test.js
+++ b/browser/components/newtab/test/unit/lib/UTEventReporting.test.js
@@ -1,10 +1,11 @@
 import {
   UTSessionPing,
+  UTTrailheadEnrollPing,
   UTUserEventPing,
 } from "test/schemas/pings";
 import {GlobalOverrider} from "test/unit/utils";
 import {UTEventReporting} from "lib/UTEventReporting.jsm";
 
 const FAKE_EVENT_PING_PC = {
   "event": "CLICK",
   "source": "TOP_SITES",
@@ -35,16 +36,27 @@ const FAKE_EVENT_PING_UT = [
 const FAKE_SESSION_PING_UT = [
   "activity_stream", "end", "session", "1234", {
     "addon_version": "123",
     "user_prefs": "63",
     "session_id": "abc",
     "page": "about:newtab",
   },
 ];
+const FAKE_TRAILHEAD_ENROLL_EVENT = {
+  experiment: "activity-stream-trailhead-firstrun-interrupts",
+  type: "as-firstrun",
+  branch: "supercharge",
+};
+const FAKE_TRAILHEAD_ENROLL_EVENT_UT = [
+  "activity_stream", "enroll", "preference_study", "activity-stream-trailhead-firstrun-interrupts", {
+    experimentType: "as-firstrun",
+    branch: "supercharge",
+  },
+];
 
 describe("UTEventReporting", () => {
   let globals;
   let sandbox;
   let utEvents;
 
   beforeEach(() => {
     globals = new GlobalOverrider();
@@ -74,16 +86,26 @@ describe("UTEventReporting", () => {
       utEvents.sendSessionEndEvent(FAKE_SESSION_PING_PC);
       assert.calledWithExactly(global.Services.telemetry.recordEvent, ...FAKE_SESSION_PING_UT);
 
       let ping = global.Services.telemetry.recordEvent.firstCall.args;
       assert.validate(ping, UTSessionPing);
     });
   });
 
+  describe("#sendTrailheadEnrollEvent()", () => {
+    it("should queue up the correct data to send to Events Telemetry", async () => {
+      utEvents.sendTrailheadEnrollEvent(FAKE_TRAILHEAD_ENROLL_EVENT);
+      assert.calledWithExactly(global.Services.telemetry.recordEvent, ...FAKE_TRAILHEAD_ENROLL_EVENT_UT);
+
+      let ping = global.Services.telemetry.recordEvent.firstCall.args;
+      assert.validate(ping, UTTrailheadEnrollPing);
+    });
+  });
+
   describe("#uninit()", () => {
     it("should call setEventRecordingEnabled with a false value", () => {
       assert.equal(global.Services.telemetry.setEventRecordingEnabled.firstCall.args[0], "activity_stream");
       assert.equal(global.Services.telemetry.setEventRecordingEnabled.firstCall.args[1], true);
 
       utEvents.uninit();
       assert.equal(global.Services.telemetry.setEventRecordingEnabled.secondCall.args[0], "activity_stream");
       assert.equal(global.Services.telemetry.setEventRecordingEnabled.secondCall.args[1], false);
--- a/devtools/server/actors/animation-type-longhand.js
+++ b/devtools/server/actors/animation-type-longhand.js
@@ -274,16 +274,17 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [
     "order",
     "perspective-origin",
     "shape-outside",
     "stroke-dasharray",
     "transform",
     "transform-origin",
     "-moz-window-transform",
     "-moz-window-transform-origin",
+    "-webkit-line-clamp",
   ])],
   ["coord", new Set([
     "border-bottom-left-radius",
     "border-bottom-right-radius",
     "border-top-left-radius",
     "border-top-right-radius",
     "border-start-start-radius",
     "border-start-end-radius",
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -2312,16 +2312,30 @@ exports.CSS_PROPERTIES = {
       "space-between",
       "space-evenly",
       "start",
       "stretch",
       "unsafe",
       "unset"
     ]
   },
+  "-webkit-line-clamp": {
+    "isInherited": false,
+    "subproperties": [
+      "-webkit-line-clamp"
+    ],
+    "supports": [],
+    "values": [
+      "inherit",
+      "initial",
+      "none",
+      "revert",
+      "unset"
+    ]
+  },
   "-webkit-mask": {
     "isInherited": false,
     "subproperties": [
       "mask-mode",
       "mask-repeat",
       "mask-clip",
       "mask-origin",
       "mask-composite",
@@ -3099,16 +3113,17 @@ exports.CSS_PROPERTIES = {
       "-moz-appearance",
       "-moz-binding",
       "-moz-orient",
       "will-change",
       "shape-image-threshold",
       "shape-margin",
       "shape-outside",
       "touch-action",
+      "-webkit-line-clamp",
       "color",
       "column-width",
       "column-count",
       "column-fill",
       "column-rule-width",
       "column-rule-color",
       "column-span",
       "column-rule-style",
@@ -10513,16 +10528,20 @@ exports.PREFERENCES = [
     "touch-action",
     "layout.css.touch_action.enabled"
   ],
   [
     "transform-box",
     "svg.transform-box.enabled"
   ],
   [
+    "-webkit-line-clamp",
+    "layout.css.webkit-line-clamp.enabled"
+  ],
+  [
     "overflow-clip-box-block",
     "layout.css.overflow-clip-box.enabled"
   ],
   [
     "overflow-clip-box-inline",
     "layout.css.overflow-clip-box.enabled"
   ],
   [
--- a/docshell/base/nsDocShellEditorData.h
+++ b/docshell/base/nsDocShellEditorData.h
@@ -17,22 +17,23 @@
 class nsIDocShell;
 class nsEditingSession;
 
 class nsDocShellEditorData {
  public:
   explicit nsDocShellEditorData(nsIDocShell* aOwningDocShell);
   ~nsDocShellEditorData();
 
-  nsresult MakeEditable(bool aWaitForUriLoad);
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult MakeEditable(bool aWaitForUriLoad);
   bool GetEditable();
   nsEditingSession* GetEditingSession();
   mozilla::HTMLEditor* GetHTMLEditor() const { return mHTMLEditor; }
-  nsresult SetHTMLEditor(mozilla::HTMLEditor* aHTMLEditor);
-  void TearDownEditor();
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
+  SetHTMLEditor(mozilla::HTMLEditor* aHTMLEditor);
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY void TearDownEditor();
   nsresult DetachFromWindow();
   nsresult ReattachToWindow(nsIDocShell* aDocShell);
   bool WaitingForLoad() const { return mMakeEditable; }
 
  protected:
   void EnsureEditingSession();
 
   // The doc shell that owns us. Weak ref, since it always outlives us.
--- a/dom/base/VisualViewport.cpp
+++ b/dom/base/VisualViewport.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/ToString.h"
 #include "nsIScrollableFrame.h"
 #include "nsIDocShell.h"
 #include "nsPresContext.h"
 #include "nsRefreshDriver.h"
+#include "DocumentInlines.h"
 
 #define VVP_LOG(...)
 // #define VVP_LOG(...) printf_stderr("VVP: " __VA_ARGS__)
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 VisualViewport::VisualViewport(nsPIDOMWindowInner* aWindow)
@@ -55,16 +56,24 @@ void VisualViewport::GetEventTargetParen
     }
   }
   aVisitor.SetParentTarget(parentTarget, false);
 }
 
 CSSSize VisualViewport::VisualViewportSize() const {
   CSSSize size = CSSSize(0, 0);
 
+  // Flush layout, as that may affect the answer below (e.g. scrollbars
+  // may have appeared, decreasing the available viewport size).
+  RefPtr<const VisualViewport> kungFuDeathGrip(this);
+  if (Document* doc = GetDocument()) {
+    doc->FlushPendingNotifications(FlushType::Layout);
+  }
+
+  // Fetch the pres shell after the layout flush, as it might have destroyed it.
   if (PresShell* presShell = GetPresShell()) {
     if (presShell->IsVisualViewportSizeSet()) {
       size = CSSRect::FromAppUnits(presShell->GetVisualViewportSize());
     } else {
       nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
       if (sf) {
         size = CSSRect::FromAppUnits(sf->GetScrollPortRect().Size());
       }
@@ -116,37 +125,38 @@ double VisualViewport::PageTop() const {
 double VisualViewport::OffsetLeft() const {
   return PageLeft() - LayoutViewportOffset().X();
 }
 
 double VisualViewport::OffsetTop() const {
   return PageTop() - LayoutViewportOffset().Y();
 }
 
-PresShell* VisualViewport::GetPresShell() const {
+Document* VisualViewport::GetDocument() const {
   nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
   if (!window) {
     return nullptr;
   }
 
   nsIDocShell* docShell = window->GetDocShell();
   if (!docShell) {
     return nullptr;
   }
 
-  return docShell->GetPresShell();
+  return docShell->GetDocument();
+}
+
+PresShell* VisualViewport::GetPresShell() const {
+  RefPtr<Document> document = GetDocument();
+  return document ? document->GetPresShell() : nullptr;
 }
 
 nsPresContext* VisualViewport::GetPresContext() const {
-  PresShell* presShell = GetPresShell();
-  if (!presShell) {
-    return nullptr;
-  }
-
-  return presShell->GetPresContext();
+  RefPtr<Document> document = GetDocument();
+  return document ? document->GetPresContext() : nullptr;
 }
 
 /* ================= Resize event handling ================= */
 
 void VisualViewport::PostResizeEvent() {
   VVP_LOG("%p: PostResizeEvent (pre-existing: %d)\n", this, !!mResizeEvent);
   nsPresContext* presContext = GetPresContext();
   if (mResizeEvent && mResizeEvent->HasPresContext(presContext)) {
--- a/dom/base/VisualViewport.h
+++ b/dom/base/VisualViewport.h
@@ -23,18 +23,18 @@ namespace dom {
 class VisualViewport final : public mozilla::DOMEventTargetHelper {
  public:
   explicit VisualViewport(nsPIDOMWindowInner* aWindow);
 
   double OffsetLeft() const;
   double OffsetTop() const;
   double PageLeft() const;
   double PageTop() const;
-  double Width() const;
-  double Height() const;
+  MOZ_CAN_RUN_SCRIPT double Width() const;
+  MOZ_CAN_RUN_SCRIPT double Height() const;
   double Scale() const;
   IMPL_EVENT_HANDLER(resize)
   IMPL_EVENT_HANDLER(scroll)
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
   void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
 
@@ -83,19 +83,20 @@ class VisualViewport final : public mozi
     // in fact change or not.
     const nsPoint mPrevVisualOffset;
     const nsPoint mPrevLayoutOffset;
   };
 
  private:
   virtual ~VisualViewport();
 
-  CSSSize VisualViewportSize() const;
+  MOZ_CAN_RUN_SCRIPT CSSSize VisualViewportSize() const;
   CSSPoint VisualViewportOffset() const;
   CSSPoint LayoutViewportOffset() const;
+  Document* GetDocument() const;
   PresShell* GetPresShell() const;
   nsPresContext* GetPresContext() const;
 
   void FireResizeEvent();
   void FireScrollEvent();
 
   RefPtr<VisualViewportResizeEvent> mResizeEvent;
   RefPtr<VisualViewportScrollEvent> mScrollEvent;
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -239,18 +239,21 @@ class nsFrameLoader final : public nsStu
       JSContext* aCx, const nsAString& aMessage,
       mozilla::dom::ipc::StructuredCloneData& aData,
       JS::Handle<JSObject*> aCpows, nsIPrincipal* aPrincipal) override;
 
   /**
    * Called from the layout frame associated with this frame loader;
    * this notifies us to hook up with the widget and view.
    */
-  bool Show(int32_t marginWidth, int32_t marginHeight, int32_t scrollbarPrefX,
-            int32_t scrollbarPrefY, nsSubDocumentFrame* frame);
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY bool Show(int32_t marginWidth,
+                                        int32_t marginHeight,
+                                        int32_t scrollbarPrefX,
+                                        int32_t scrollbarPrefY,
+                                        nsSubDocumentFrame* frame);
 
   void MaybeShowFrame();
 
   /**
    * Called when the margin properties of the containing frame are changed.
    */
   void MarginsChanged(uint32_t aMarginWidth, uint32_t aMarginHeight);
 
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -1229,21 +1229,21 @@ nsresult nsTextEditorState::BindToFrame(
 
     nsContentUtils::AddScriptRunner(
         new PrepareEditorEvent(*this, content, currentValue));
   }
 
   return NS_OK;
 }
 
-struct PreDestroyer {
+struct MOZ_STACK_CLASS PreDestroyer {
   void Init(TextEditor* aTextEditor) { mTextEditor = aTextEditor; }
-  ~PreDestroyer() {
+  MOZ_CAN_RUN_SCRIPT ~PreDestroyer() {
     if (mTextEditor) {
-      mTextEditor->PreDestroy(true);
+      MOZ_KnownLive(mTextEditor)->PreDestroy(true);
     }
   }
   void Swap(RefPtr<TextEditor>& aTextEditor) {
     return mTextEditor.swap(aTextEditor);
   }
 
  private:
   RefPtr<TextEditor> mTextEditor;
@@ -1912,17 +1912,18 @@ HTMLInputElement* nsTextEditorState::Get
   }
 
   return nullptr;
 }
 
 void nsTextEditorState::DestroyEditor() {
   // notify the editor that we are going away
   if (mEditorInitialized) {
-    mTextEditor->PreDestroy(true);
+    RefPtr<TextEditor> textEditor = mTextEditor;
+    textEditor->PreDestroy(true);
     mEditorInitialized = false;
   }
 }
 
 void nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame) {
   NS_ENSURE_TRUE_VOID(mBoundFrame);
 
   // If it was, however, it should be unbounded from the same frame.
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -137,20 +137,20 @@ class HTMLInputElement;
 class RestoreSelectionState;
 
 class nsTextEditorState : public mozilla::SupportsWeakPtr<nsTextEditorState> {
  public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsTextEditorState)
   explicit nsTextEditorState(nsITextControlElement* aOwningElement);
   static nsTextEditorState* Construct(nsITextControlElement* aOwningElement,
                                       nsTextEditorState** aReusedState);
-  ~nsTextEditorState();
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY ~nsTextEditorState();
 
   void Traverse(nsCycleCollectionTraversalCallback& cb);
-  void Unlink();
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY void Unlink();
 
   void PrepareForReuse() {
     Unlink();
     mValue.reset();
     mValueBeingSet.Truncate();
     mTextCtrlElement = nullptr;
   }
 
@@ -372,18 +372,18 @@ class nsTextEditorState : public mozilla
 
   // not copy constructible
   nsTextEditorState(const nsTextEditorState&);
   // not assignable
   void operator=(const nsTextEditorState&);
 
   void ValueWasChanged(bool aNotify);
 
-  void DestroyEditor();
-  void Clear();
+  MOZ_CAN_RUN_SCRIPT void DestroyEditor();
+  MOZ_CAN_RUN_SCRIPT void Clear();
 
   nsresult InitializeRootNode();
 
   void FinishedRestoringSelection();
 
   mozilla::dom::HTMLInputElement* GetParentNumberControl(nsFrame* aFrame) const;
 
   bool EditorHasComposition();
--- a/dom/smil/SMILCSSProperty.cpp
+++ b/dom/smil/SMILCSSProperty.cpp
@@ -102,21 +102,16 @@ void SMILCSSProperty::ClearAnimValue() {
   // Put empty string in override style for our property
   mElement->SMILOverrideStyle()->SetPropertyValue(mPropID, EmptyString(),
                                                   nullptr);
 }
 
 // Based on http://www.w3.org/TR/SVG/propidx.html
 // static
 bool SMILCSSProperty::IsPropertyAnimatable(nsCSSPropertyID aPropID) {
-  // Bug 1353918: Drop this check
-  if (!Servo_Property_IsAnimatable(aPropID)) {
-    return false;
-  }
-
   // NOTE: Right now, Gecko doesn't recognize the following properties from
   // the SVG Property Index:
   //   alignment-baseline
   //   baseline-shift
   //   color-profile
   //   color-rendering
   //   glyph-orientation-horizontal
   //   glyph-orientation-vertical
--- a/dom/smil/SMILCSSProperty.h
+++ b/dom/smil/SMILCSSProperty.h
@@ -48,19 +48,16 @@ class SMILCSSProperty : public SMILAttr 
   virtual nsresult SetAnimValue(const SMILValue& aValue) override;
   virtual void ClearAnimValue() override;
 
   /**
    * Utility method - returns true if the given property is supported for
    * SMIL animation.
    *
    * @param   aProperty  The property to check for animation support.
-   * @param   aBackend   The style backend to check for animation support.
-   *                     This is a temporary measure until the Servo backend
-   *                     supports all animatable properties (bug 1353918).
    * @return  true if the given property is supported for SMIL animation, or
    *          false otherwise
    */
   static bool IsPropertyAnimatable(nsCSSPropertyID aPropID);
 
  protected:
   nsCSSPropertyID mPropID;
   // Using non-refcounted pointer for mElement -- we know mElement will stay
--- a/dom/webidl/CSSStyleSheet.webidl
+++ b/dom/webidl/CSSStyleSheet.webidl
@@ -19,9 +19,17 @@ interface CSSStyleSheet : StyleSheet {
   [Throws, NeedsSubjectPrincipal]
   readonly attribute CSSRuleList cssRules;
   [ChromeOnly, BinaryName="parsingModeDOM"]
   readonly attribute CSSStyleSheetParsingMode parsingMode;
   [Throws, NeedsSubjectPrincipal]
   unsigned long insertRule(DOMString rule, optional unsigned long index = 0);
   [Throws, NeedsSubjectPrincipal]
   void deleteRule(unsigned long index);
+
+  // Non-standard WebKit things.
+  [Throws, NeedsSubjectPrincipal, BinaryName="cssRules"]
+  readonly attribute CSSRuleList rules;
+  [Throws, NeedsSubjectPrincipal, BinaryName="deleteRule"]
+  void removeRule(optional unsigned long index = 0);
+  [Throws, NeedsSubjectPrincipal]
+  long addRule(optional DOMString selector = "undefined", optional DOMString style = "undefined", optional unsigned long index);
 };
--- a/editor/composer/nsEditingSession.cpp
+++ b/editor/composer/nsEditingSession.cpp
@@ -351,17 +351,18 @@ nsEditingSession::SetupEditorOnWindow(mo
 
   // now init the state maintainer
   // This allows notification of error state
   //  even if we don't create an editor
   rv = mComposerCommandsUpdater->Init(window);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (mEditorStatus != eEditorCreationInProgress) {
-    mComposerCommandsUpdater->NotifyDocumentCreated();
+    RefPtr<ComposerCommandsUpdater> updater = mComposerCommandsUpdater;
+    updater->NotifyDocumentCreated();
     return NS_ERROR_FAILURE;
   }
 
   // Create editor and do other things
   //  only if we haven't found some error above,
   nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
   RefPtr<PresShell> presShell = docShell->GetPresShell();
--- a/editor/libeditor/CSSEditUtils.cpp
+++ b/editor/libeditor/CSSEditUtils.cpp
@@ -807,18 +807,19 @@ int32_t CSSEditUtils::SetCSSEquivalentTo
   nsTArray<nsString> cssValueArray;
   GenerateCSSDeclarationsFromHTMLStyle(aElement, aHTMLProperty, aAttribute,
                                        aValue, cssPropertyArray, cssValueArray,
                                        false);
 
   // set the individual CSS inline styles
   size_t count = cssPropertyArray.Length();
   for (size_t index = 0; index < count; index++) {
-    nsresult rv = SetCSSProperty(*aElement, *cssPropertyArray[index],
-                                 cssValueArray[index], aSuppressTransaction);
+    nsresult rv =
+        SetCSSProperty(*aElement, MOZ_KnownLive(*cssPropertyArray[index]),
+                       cssValueArray[index], aSuppressTransaction);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return 0;
     }
   }
   return count;
 }
 
 // Remove from aNode the CSS inline style equivalent to
--- a/editor/libeditor/CSSEditUtils.h
+++ b/editor/libeditor/CSSEditUtils.h
@@ -91,24 +91,27 @@ class CSSEditUtils final {
    *
    * @param aElement       [IN] A DOM element.
    * @param aProperty      [IN] An atom containing the CSS property to set.
    * @param aValue         [IN] A string containing the value of the CSS
    *                            property.
    * @param aSuppressTransaction [IN] A boolean indicating, when true,
    *                                  that no transaction should be recorded.
    */
-  nsresult SetCSSProperty(dom::Element& aElement, nsAtom& aProperty,
-                          const nsAString& aValue, bool aSuppressTxn = false);
-  nsresult SetCSSPropertyPixels(dom::Element& aElement, nsAtom& aProperty,
-                                int32_t aIntValue);
-  MOZ_CAN_RUN_SCRIPT
-  nsresult RemoveCSSProperty(dom::Element& aElement, nsAtom& aProperty,
-                             const nsAString& aPropertyValue,
-                             bool aSuppressTxn = false);
+  MOZ_CAN_RUN_SCRIPT nsresult SetCSSProperty(dom::Element& aElement,
+                                             nsAtom& aProperty,
+                                             const nsAString& aValue,
+                                             bool aSuppressTxn = false);
+  MOZ_CAN_RUN_SCRIPT nsresult SetCSSPropertyPixels(dom::Element& aElement,
+                                                   nsAtom& aProperty,
+                                                   int32_t aIntValue);
+  MOZ_CAN_RUN_SCRIPT nsresult RemoveCSSProperty(dom::Element& aElement,
+                                                nsAtom& aProperty,
+                                                const nsAString& aPropertyValue,
+                                                bool aSuppressTxn = false);
 
   /**
    * Gets the specified/computed style value of a CSS property for a given
    * node (or its element ancestor if it is not an element).
    *
    * @param aNode          [IN] A DOM node.
    * @param aProperty      [IN] An atom containing the CSS property to get.
    * @param aPropertyValue [OUT] The retrieved value of the property.
@@ -239,20 +242,19 @@ class CSSEditUtils final {
    * @param aAttribute     [IN] An atom to an attribute name or nullptr
    *                            if irrelevant.
    * @param aValue         [IN] The attribute value.
    * @param aSuppressTransaction [IN] A boolean indicating, when true,
    *                                  that no transaction should be recorded.
    *
    * @return               The number of CSS properties set by the call.
    */
-  int32_t SetCSSEquivalentToHTMLStyle(dom::Element* aElement, nsAtom* aProperty,
-                                      nsAtom* aAttribute,
-                                      const nsAString* aValue,
-                                      bool aSuppressTransaction);
+  MOZ_CAN_RUN_SCRIPT int32_t SetCSSEquivalentToHTMLStyle(
+      dom::Element* aElement, nsAtom* aProperty, nsAtom* aAttribute,
+      const nsAString* aValue, bool aSuppressTransaction);
 
   /**
    * Removes from the node the CSS inline styles equivalent to the HTML style.
    *
    * @param aElement       [IN] A DOM Element (must not be null).
    * @param aHTMLProperty  [IN] An atom containing an HTML property.
    * @param aAttribute     [IN] An atom to an attribute name or nullptr if
    *                            irrelevant.
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -697,17 +697,19 @@ EditorBase::DoTransaction(nsITransaction
 
 nsresult EditorBase::DoTransactionInternal(nsITransaction* aTxn) {
   if (mPlaceholderBatch && !mPlaceholderTransaction) {
     mPlaceholderTransaction = PlaceholderTransaction::Create(
         *this, mPlaceholderName, std::move(mSelState));
     MOZ_ASSERT(mSelState.isNothing());
 
     // We will recurse, but will not hit this case in the nested call
-    DoTransactionInternal(mPlaceholderTransaction);
+    RefPtr<PlaceholderTransaction> placeholderTransaction =
+        mPlaceholderTransaction;
+    DoTransactionInternal(placeholderTransaction);
 
     if (mTransactionManager) {
       nsCOMPtr<nsITransaction> topTransaction =
           mTransactionManager->PeekUndoStack();
       nsCOMPtr<nsIAbsorbingTransaction> topAbsorbingTransaction =
           do_QueryInterface(topTransaction);
       if (topAbsorbingTransaction) {
         RefPtr<PlaceholderTransaction> topPlaceholderTransaction =
@@ -3873,18 +3875,18 @@ void EditorBase::EndUpdateViewBatch() {
   }
 
   // We may need to show resizing handles or update existing ones after
   // all transactions are done. This way of doing is preferred to DOM
   // mutation events listeners because all the changes the user can apply
   // to a document may result in multiple events, some of them quite hard
   // to listen too (in particular when an ancestor of the selection is
   // changed but the selection itself is not changed).
-  DebugOnly<nsresult> rv = htmlEditor->RefereshEditingUI();
-  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RefereshEditingUI() failed");
+  DebugOnly<nsresult> rv = MOZ_KnownLive(htmlEditor)->RefreshEditingUI();
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RefreshEditingUI() failed");
 }
 
 TextComposition* EditorBase::GetComposition() const { return mComposition; }
 
 EditorRawDOMPoint EditorBase::GetCompositionStartPoint() const {
   return mComposition ? EditorRawDOMPoint(mComposition->GetStartRef())
                       : EditorRawDOMPoint();
 }
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -175,26 +175,26 @@ class EditorBase : public nsIEditor,
   virtual nsresult Init(Document& doc, Element* aRoot,
                         nsISelectionController* aSelCon, uint32_t aFlags,
                         const nsAString& aInitialValue);
 
   /**
    * PostCreate should be called after Init, and is the time that the editor
    * tells its documentStateObservers that the document has been created.
    */
-  nsresult PostCreate();
+  MOZ_CAN_RUN_SCRIPT nsresult PostCreate();
 
   /**
    * PreDestroy is called before the editor goes away, and gives the editor a
    * chance to tell its documentStateObservers that the document is going away.
    * @param aDestroyingFrames set to true when the frames being edited
    * are being destroyed (so there is no need to modify any nsISelections,
    * nor is it safe to do so)
    */
-  virtual void PreDestroy(bool aDestroyingFrames);
+  MOZ_CAN_RUN_SCRIPT virtual void PreDestroy(bool aDestroyingFrames);
 
   bool IsInitialized() const { return !!mDocument; }
   bool Destroyed() const { return mDidPreDestroy; }
 
   Document* GetDocument() const { return mDocument; }
 
   PresShell* GetPresShell() const {
     return mDocument ? mDocument->GetPresShell() : nullptr;
@@ -1400,17 +1400,17 @@ class EditorBase : public nsIEditor,
    * @param aRightNode  The node which will be inserted the contents of
    *                    aLeftNode.
    * @return            The point of the first child of the last right node.
    */
   MOZ_CAN_RUN_SCRIPT
   EditorDOMPoint JoinNodesDeepWithTransaction(nsIContent& aLeftNode,
                                               nsIContent& aRightNode);
 
-  nsresult DoTransactionInternal(nsITransaction* aTxn);
+  MOZ_CAN_RUN_SCRIPT nsresult DoTransactionInternal(nsITransaction* aTxn);
 
   virtual bool IsBlockNode(nsINode* aNode);
 
   /**
    * Set outOffset to the offset of aChild in the parent.
    * Returns the parent of aChild.
    */
   static nsINode* GetNodeLocation(nsINode* aChild, int32_t* aOffset);
@@ -1778,27 +1778,27 @@ class EditorBase : public nsIEditor,
    * manager batches.
    */
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void BeginPlaceholderTransaction(nsAtom* aTransactionName);
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void EndPlaceholderTransaction();
 
   void BeginUpdateViewBatch();
-  void EndUpdateViewBatch();
+  MOZ_CAN_RUN_SCRIPT void EndUpdateViewBatch();
 
   /**
    * Used by AutoTransactionBatch.  After calling BeginTransactionInternal(),
    * all transactions will be treated as an atomic transaction.  I.e.,
    * two or more transactions are undid once.
    * XXX What's the difference with PlaceholderTransaction? Should we always
    *     use it instead?
    */
   void BeginTransactionInternal();
-  void EndTransactionInternal();
+  MOZ_CAN_RUN_SCRIPT void EndTransactionInternal();
 
  protected:  // Shouldn't be used by friend classes
   /**
    * The default destructor. This should suffice. Should this be pure virtual
    * for someone to derive from the EditorBase later? I don't believe so.
    */
   virtual ~EditorBase();
 
@@ -1855,39 +1855,39 @@ class EditorBase : public nsIEditor,
   void FireInputEvent();
   MOZ_CAN_RUN_SCRIPT
   void FireInputEvent(EditAction aEditAction, const nsAString& aData,
                       dom::DataTransfer* aDataTransfer);
 
   /**
    * Called after a transaction is done successfully.
    */
-  void DoAfterDoTransaction(nsITransaction* aTxn);
+  MOZ_CAN_RUN_SCRIPT void DoAfterDoTransaction(nsITransaction* aTxn);
 
   /**
    * Called after a transaction is undone successfully.
    */
 
-  void DoAfterUndoTransaction();
+  MOZ_CAN_RUN_SCRIPT void DoAfterUndoTransaction();
 
   /**
    * Called after a transaction is redone successfully.
    */
-  void DoAfterRedoTransaction();
+  MOZ_CAN_RUN_SCRIPT void DoAfterRedoTransaction();
 
   /**
    * Tell the doc state listeners that the doc state has changed.
    */
   enum TDocumentListenerNotification {
     eDocumentCreated,
     eDocumentToBeDestroyed,
     eDocumentStateChanged
   };
-  nsresult NotifyDocumentListeners(
-      TDocumentListenerNotification aNotificationType);
+  MOZ_CAN_RUN_SCRIPT nsresult
+  NotifyDocumentListeners(TDocumentListenerNotification aNotificationType);
 
   /**
    * Make the given selection span the entire document.
    */
   MOZ_CAN_RUN_SCRIPT
   virtual nsresult SelectEntireDocument() = 0;
 
   /**
@@ -2043,27 +2043,29 @@ class EditorBase : public nsIEditor,
   nsresult SetTextDirectionTo(TextDirection aTextDirection);
 
  protected:  // helper classes which may be used by friends
   /**
    * Stack based helper class for calling EditorBase::EndTransactionInternal().
    */
   class MOZ_RAII AutoTransactionBatch final {
    public:
-    explicit AutoTransactionBatch(
+    MOZ_CAN_RUN_SCRIPT explicit AutoTransactionBatch(
         EditorBase& aEditorBase MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : mEditorBase(aEditorBase) {
       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-      mEditorBase->BeginTransactionInternal();
+      mEditorBase.BeginTransactionInternal();
     }
 
-    ~AutoTransactionBatch() { mEditorBase->EndTransactionInternal(); }
+    MOZ_CAN_RUN_SCRIPT ~AutoTransactionBatch() {
+      MOZ_KnownLive(mEditorBase).EndTransactionInternal();
+    }
 
    protected:
-    OwningNonNull<EditorBase> mEditorBase;
+    EditorBase& mEditorBase;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   };
 
   /**
    * Stack based helper class for batching a collection of transactions inside
    * a placeholder transaction.
    */
   class MOZ_RAII AutoPlaceholderBatch final {
@@ -2180,24 +2182,26 @@ class EditorBase : public nsIEditor,
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   };
 
   /***************************************************************************
    * stack based helper class for batching reflow and paint requests.
    */
   class MOZ_RAII AutoUpdateViewBatch final {
    public:
-    explicit AutoUpdateViewBatch(
+    MOZ_CAN_RUN_SCRIPT explicit AutoUpdateViewBatch(
         EditorBase& aEditorBase MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : mEditorBase(aEditorBase) {
       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
       mEditorBase.BeginUpdateViewBatch();
     }
 
-    ~AutoUpdateViewBatch() { mEditorBase.EndUpdateViewBatch(); }
+    MOZ_CAN_RUN_SCRIPT ~AutoUpdateViewBatch() {
+      MOZ_KnownLive(mEditorBase).EndUpdateViewBatch();
+    }
 
    protected:
     EditorBase& mEditorBase;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   };
 
  protected:
   enum Tristate { eTriUnset, eTriFalse, eTriTrue };
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -401,29 +401,31 @@ class MOZ_STACK_CLASS SplitRangeOffFromN
 /***************************************************************************
  * stack based helper class for calling EditorBase::EndTransaction() after
  * EditorBase::BeginTransaction().  This shouldn't be used in editor classes
  * or helper classes while an edit action is being handled.  Use
  * AutoTransactionBatch in such cases since it uses non-virtual internal
  * methods.
  ***************************************************************************/
 class MOZ_RAII AutoTransactionBatchExternal final {
- private:
-  OwningNonNull<EditorBase> mEditorBase;
-  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-
  public:
-  explicit AutoTransactionBatchExternal(
+  MOZ_CAN_RUN_SCRIPT explicit AutoTransactionBatchExternal(
       EditorBase& aEditorBase MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mEditorBase(aEditorBase) {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    mEditorBase->BeginTransaction();
+    mEditorBase.BeginTransaction();
   }
 
-  ~AutoTransactionBatchExternal() { mEditorBase->EndTransaction(); }
+  MOZ_CAN_RUN_SCRIPT ~AutoTransactionBatchExternal() {
+    MOZ_KnownLive(mEditorBase).EndTransaction();
+  }
+
+ private:
+  EditorBase& mEditorBase;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class MOZ_STACK_CLASS AutoRangeArray final {
  public:
   explicit AutoRangeArray(dom::Selection* aSelection) {
     if (!aSelection) {
       return;
     }
--- a/editor/libeditor/HTMLAbsPositionEditor.cpp
+++ b/editor/libeditor/HTMLAbsPositionEditor.cpp
@@ -272,18 +272,19 @@ nsresult HTMLEditor::RefreshGrabberInter
       *mAbsolutelyPositionedObject, mPositionedObjectX, mPositionedObjectY,
       mPositionedObjectWidth, mPositionedObjectHeight,
       mPositionedObjectBorderLeft, mPositionedObjectBorderTop,
       mPositionedObjectMarginLeft, mPositionedObjectMarginTop);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  RefPtr<Element> grabber = mGrabber.get();
   SetAnonymousElementPosition(mPositionedObjectX + 12, mPositionedObjectY - 14,
-                              mGrabber);
+                              grabber);
   return NS_OK;
 }
 
 void HTMLEditor::HideGrabberInternal() {
   if (NS_WARN_IF(!mAbsolutelyPositionedObject)) {
     return;
   }
 
@@ -359,28 +360,33 @@ nsresult HTMLEditor::StartMoving() {
 
   // now, let's create the resizing shadow
   mPositioningShadow =
       CreateShadow(*parentContent, *mAbsolutelyPositionedObject);
   if (NS_WARN_IF(!mPositioningShadow) ||
       NS_WARN_IF(!mAbsolutelyPositionedObject)) {
     return NS_ERROR_FAILURE;
   }
+  RefPtr<Element> positioningShadow = mPositioningShadow.get();
+  RefPtr<Element> absolutelyPositionedObject = mAbsolutelyPositionedObject;
   nsresult rv =
-      SetShadowPosition(*mPositioningShadow, *mAbsolutelyPositionedObject,
+      SetShadowPosition(*positioningShadow, *absolutelyPositionedObject,
                         mPositionedObjectX, mPositionedObjectY);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   // make the shadow appear
   mPositioningShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true);
 
   // position it
-  mCSSEditUtils->SetCSSPropertyPixels(*mPositioningShadow, *nsGkAtoms::width,
+  positioningShadow = mPositioningShadow.get();
+  mCSSEditUtils->SetCSSPropertyPixels(*positioningShadow, *nsGkAtoms::width,
                                       mPositionedObjectWidth);
-  mCSSEditUtils->SetCSSPropertyPixels(*mPositioningShadow, *nsGkAtoms::height,
+  mCSSEditUtils->SetCSSPropertyPixels(*positioningShadow, *nsGkAtoms::height,
                                       mPositionedObjectHeight);
 
   mIsMoving = true;
   return NS_OK;  // XXX Looks like nobody refers this result
 }
 
 void HTMLEditor::SnapToGrid(int32_t& newX, int32_t& newY) {
   if (mSnapToGridEnabled && mGridSize) {
@@ -419,17 +425,17 @@ nsresult HTMLEditor::EndMoving() {
         static_cast<HTMLEditorEventListener*>(mEventListener.get())
             ->ListenToMouseMoveEventForGrabber(false);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                          "Failed to remove mousemove event listener");
   }
 
   mGrabberClicked = false;
   mIsMoving = false;
-  nsresult rv = RefereshEditingUI();
+  nsresult rv = RefreshEditingUI();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 nsresult HTMLEditor::SetFinalPosition(int32_t aX, int32_t aY) {
   nsresult rv = EndMoving();
   NS_ENSURE_SUCCESS(rv, rv);
--- a/editor/libeditor/HTMLAnonymousNodeEditor.cpp
+++ b/editor/libeditor/HTMLAnonymousNodeEditor.cpp
@@ -309,24 +309,24 @@ void HTMLEditor::HideAnonymousEditingUIs
 
 NS_IMETHODIMP
 HTMLEditor::CheckSelectionStateForAnonymousButtons() {
   AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  nsresult rv = RefereshEditingUI();
+  nsresult rv = RefreshEditingUI();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditorBase::ToGenericNSResult(rv);
   }
   return NS_OK;
 }
 
-nsresult HTMLEditor::RefereshEditingUI() {
+nsresult HTMLEditor::RefreshEditingUI() {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   // First, we need to remove unnecessary editing UI now since some of them
   // may be disabled while them are visible.
   HideAnonymousEditingUIsIfUnnecessary();
 
   // early way out if all contextual UI extensions are disabled
   if (!IsObjectResizerEnabled() && !IsAbsolutePositionEditorEnabled() &&
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -6165,18 +6165,18 @@ nsresult HTMLEditRules::AlignContentsAtS
     if (HTMLEditUtils::IsListItem(curNode) || HTMLEditUtils::IsList(curNode)) {
       AutoEditorDOMPointOffsetInvalidator lockChild(atCurNode);
       rv = RemoveAlignment(*curNode, aAlignType, true);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       if (useCSS) {
         HTMLEditorRef().mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
-            curNode->AsElement(), nullptr, nsGkAtoms::align, &aAlignType,
-            false);
+            MOZ_KnownLive(curNode->AsElement()), nullptr, nsGkAtoms::align,
+            &aAlignType, false);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         curDiv = nullptr;
         continue;
       }
       if (HTMLEditUtils::IsList(atCurNode.GetContainer())) {
         // If we don't use CSS, add a content to list element: they have to
@@ -10461,17 +10461,18 @@ nsresult HTMLEditRules::AlignBlock(Eleme
   return NS_OK;
 }
 
 nsresult HTMLEditRules::ChangeMarginStart(Element& aElement, bool aIncrease) {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   nsAtom& marginProperty = MarginPropertyAtomForIndent(aElement);
   nsAutoString value;
-  CSSEditUtils::GetSpecifiedProperty(aElement, marginProperty, value);
+  CSSEditUtils::GetSpecifiedProperty(aElement, MOZ_KnownLive(marginProperty),
+                                     value);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   float f;
   RefPtr<nsAtom> unit;
   CSSEditUtils::ParseLength(value, &f, getter_AddRefs(unit));
   if (!f) {
     nsAutoString defaultLengthUnit;
@@ -10498,18 +10499,18 @@ nsresult HTMLEditRules::ChangeMarginStar
   } else if (nsGkAtoms::percentage == unit) {
     f += NS_EDITOR_INDENT_INCREMENT_PERCENT * multiplier;
   }
 
   if (0 < f) {
     nsAutoString newValue;
     newValue.AppendFloat(f);
     newValue.Append(nsDependentAtomString(unit));
-    HTMLEditorRef().mCSSEditUtils->SetCSSProperty(aElement, marginProperty,
-                                                  newValue);
+    HTMLEditorRef().mCSSEditUtils->SetCSSProperty(
+        aElement, MOZ_KnownLive(marginProperty), newValue);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     return NS_OK;
   }
 
   HTMLEditorRef().mCSSEditUtils->RemoveCSSProperty(
       aElement, MOZ_KnownLive(marginProperty), value);
@@ -10933,18 +10934,18 @@ nsresult HTMLEditRules::WillRelativeChan
   if (NS_WARN_IF(!element)) {
     return NS_ERROR_FAILURE;
   }
 
   {
     AutoSelectionRestorer restoreSelectionLater(HTMLEditorRef());
 
     int32_t zIndex;
-    nsresult rv =
-        HTMLEditorRef().RelativeChangeElementZIndex(*element, aChange, &zIndex);
+    nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                      .RelativeChangeElementZIndex(*element, aChange, &zIndex);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -357,30 +357,30 @@ HTMLEditor::NotifySelectionChanged(Docum
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   if (mTypeInState) {
     RefPtr<TypeInState> typeInState = mTypeInState;
     typeInState->OnSelectionChange(*aSelection);
 
     // We used a class which derived from nsISelectionListener to call
-    // HTMLEditor::RefereshEditingUI().  The lifetime of the class was
+    // HTMLEditor::RefreshEditingUI().  The lifetime of the class was
     // exactly same as mTypeInState.  So, call it only when mTypeInState
     // is not nullptr.
     if ((aReason & (nsISelectionListener::MOUSEDOWN_REASON |
                     nsISelectionListener::KEYPRESS_REASON |
                     nsISelectionListener::SELECTALL_REASON)) &&
         aSelection) {
       // the selection changed and we need to check if we have to
       // hide and/or redisplay resizing handles
       // FYI: This is an XPCOM method.  So, the caller, Selection, guarantees
       //      the lifetime of this instance.  So, don't need to grab this with
       //      local variable.
-      DebugOnly<nsresult> rv = RefereshEditingUI();
-      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RefereshEditingUI() failed");
+      DebugOnly<nsresult> rv = RefreshEditingUI();
+      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RefreshEditingUI() failed");
     }
   }
 
   if (mComposerCommandsUpdater) {
     RefPtr<ComposerCommandsUpdater> updater = mComposerCommandsUpdater;
     updater->OnSelectionChange();
   }
 
@@ -4375,19 +4375,19 @@ nsresult HTMLEditor::SetCSSBackgroundCol
         if (blockParent && cachedBlockParent != blockParent) {
           cachedBlockParent = blockParent;
           mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
               blockParent, nullptr, nsGkAtoms::bgcolor, &aColor, false);
         }
       } else if (startNode == endNode &&
                  startNode->IsHTMLElement(nsGkAtoms::body) && isCollapsed) {
         // No block in the document, let's apply the background to the body
-        mCSSEditUtils->SetCSSEquivalentToHTMLStyle(startNode->AsElement(),
-                                                   nullptr, nsGkAtoms::bgcolor,
-                                                   &aColor, false);
+        mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
+            MOZ_KnownLive(startNode->AsElement()), nullptr, nsGkAtoms::bgcolor,
+            &aColor, false);
       } else if (startNode == endNode && (endOffset - startOffset == 1 ||
                                           (!startOffset && !endOffset))) {
         // A unique node is selected, let's also apply the background color to
         // the containing block, possibly the node itself
         nsCOMPtr<nsIContent> selectedNode = range->GetChildAtStartOffset();
         nsCOMPtr<Element> blockParent = GetBlock(*selectedNode);
         if (blockParent && cachedBlockParent != blockParent) {
           cachedBlockParent = blockParent;
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -116,17 +116,17 @@ class HTMLEditor final : public TextEdit
 
   // nsISelectionListener overrides
   NS_DECL_NSISELECTIONLISTENER
 
   HTMLEditor();
 
   nsHTMLDocument* GetHTMLDocument() const;
 
-  virtual void PreDestroy(bool aDestroyingFrames) override;
+  MOZ_CAN_RUN_SCRIPT virtual void PreDestroy(bool aDestroyingFrames) override;
 
   bool GetReturnInParagraphCreatesNewParagraph();
 
   // TextEditor overrides
   MOZ_CAN_RUN_SCRIPT
   virtual nsresult Init(Document& aDoc, Element* aRoot,
                         nsISelectionController* aSelCon, uint32_t aFlags,
                         const nsAString& aValue) override;
@@ -213,27 +213,28 @@ class HTMLEditor final : public TextEdit
 
   /**
    * event callback when a mouse button is pressed
    * @param aX      [IN] horizontal position of the pointer
    * @param aY      [IN] vertical position of the pointer
    * @param aTarget [IN] the element triggering the event
    * @param aMouseEvent [IN] the event
    */
-  nsresult OnMouseDown(int32_t aX, int32_t aY, Element* aTarget,
-                       dom::Event* aMouseEvent);
+  MOZ_CAN_RUN_SCRIPT nsresult OnMouseDown(int32_t aX, int32_t aY,
+                                          Element* aTarget,
+                                          dom::Event* aMouseEvent);
 
   /**
    * event callback when a mouse button is released
    * @param aX      [IN] horizontal position of the pointer
    * @param aY      [IN] vertical position of the pointer
    * @param aTarget [IN] the element triggering the event
    */
-  MOZ_CAN_RUN_SCRIPT
-  nsresult OnMouseUp(int32_t aX, int32_t aY, Element* aTarget);
+  MOZ_CAN_RUN_SCRIPT nsresult OnMouseUp(int32_t aX, int32_t aY,
+                                        Element* aTarget);
 
   /**
    * event callback when the mouse pointer is moved
    * @param aMouseEvent [IN] the event
    */
   MOZ_CAN_RUN_SCRIPT nsresult OnMouseMove(dom::MouseEvent* aMouseEvent);
 
   /**
@@ -246,72 +247,72 @@ class HTMLEditor final : public TextEdit
     // TODO: removal of mCSSAware and use only the presence of mCSSEditUtils
     return mCSSAware && mCSSEditUtils && mCSSEditUtils->IsCSSPrefChecked();
   }
 
   /**
    * Enable/disable object resizers for <img> elements, <table> elements,
    * absolute positioned elements (required absolute position editor enabled).
    */
-  void EnableObjectResizer(bool aEnable) {
+  MOZ_CAN_RUN_SCRIPT void EnableObjectResizer(bool aEnable) {
     if (mIsObjectResizingEnabled == aEnable) {
       return;
     }
 
     AutoEditActionDataSetter editActionData(
         *this, EditAction::eEnableOrDisableResizer);
     if (NS_WARN_IF(!editActionData.CanHandle())) {
       return;
     }
 
     mIsObjectResizingEnabled = aEnable;
-    RefereshEditingUI();
+    RefreshEditingUI();
   }
   bool IsObjectResizerEnabled() const { return mIsObjectResizingEnabled; }
 
   /**
    * Enable/disable inline table editor, e.g., adding new row or column,
    * removing existing row or column.
    */
-  void EnableInlineTableEditor(bool aEnable) {
+  MOZ_CAN_RUN_SCRIPT void EnableInlineTableEditor(bool aEnable) {
     if (mIsInlineTableEditingEnabled == aEnable) {
       return;
     }
 
     AutoEditActionDataSetter editActionData(
         *this, EditAction::eEnableOrDisableInlineTableEditingUI);
     if (NS_WARN_IF(!editActionData.CanHandle())) {
       return;
     }
 
     mIsInlineTableEditingEnabled = aEnable;
-    RefereshEditingUI();
+    RefreshEditingUI();
   }
   bool IsInlineTableEditorEnabled() const {
     return mIsInlineTableEditingEnabled;
   }
 
   /**
    * Enable/disable absolute position editor, resizing absolute positioned
    * elements (required object resizers enabled) or positioning them with
    * dragging grabber.
    */
-  void EnableAbsolutePositionEditor(bool aEnable) {
+  MOZ_CAN_RUN_SCRIPT void EnableAbsolutePositionEditor(bool aEnable) {
     if (mIsAbsolutelyPositioningEnabled == aEnable) {
       return;
     }
 
     AutoEditActionDataSetter editActionData(
         *this, EditAction::eEnableOrDisableAbsolutePositionEditor);
     if (NS_WARN_IF(!editActionData.CanHandle())) {
       return;
     }
 
     mIsAbsolutelyPositioningEnabled = aEnable;
-    RefereshEditingUI();
+    RefreshEditingUI();
   }
   bool IsAbsolutePositionEditorEnabled() const {
     return mIsAbsolutelyPositioningEnabled;
   }
 
   // non-virtual methods of interface methods
 
   /**
@@ -687,18 +688,19 @@ class HTMLEditor final : public TextEdit
 
   /**
    * adds aChange to the z-index of an arbitrary element.
    * @param aElement [IN] the element
    * @param aChange  [IN] relative change to apply to current z-index of
    *                      the element
    * @param aReturn  [OUT] the new z-index of the element
    */
-  nsresult RelativeChangeElementZIndex(Element& aElement, int32_t aChange,
-                                       int32_t* aReturn);
+  MOZ_CAN_RUN_SCRIPT nsresult RelativeChangeElementZIndex(Element& aElement,
+                                                          int32_t aChange,
+                                                          int32_t* aReturn);
 
   virtual bool IsBlockNode(nsINode* aNode) override;
   using EditorBase::IsBlockNode;
 
   /**
    * returns true if aParentTag can contain a child of type aChildTag.
    */
   virtual bool TagCanContainTag(nsAtom& aParentTag,
@@ -1540,16 +1542,17 @@ class HTMLEditor final : public TextEdit
   nsresult GetLastCellInRow(nsINode* aRowNode, nsINode** aCellNode);
 
   static nsresult GetCellFromRange(nsRange* aRange, Element** aCell);
 
   /**
    * This sets background on the appropriate container element (table, cell,)
    * or calls into nsTextEditor to set the page background.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult SetCSSBackgroundColorWithTransaction(const nsAString& aColor);
   MOZ_CAN_RUN_SCRIPT
   nsresult SetHTMLBackgroundColorWithTransaction(const nsAString& aColor);
 
   virtual void InitializeSelectionAncestorLimit(
       nsIContent& aAncestorLimit) override;
 
   /**
@@ -2155,17 +2158,18 @@ class HTMLEditor final : public TextEdit
 
   /**
    * sets the position of an element; warning it does NOT check if the
    * element is already positioned or not and that's on purpose.
    * @param aElement [IN] the element
    * @param aX       [IN] the x position in pixels.
    * @param aY       [IN] the y position in pixels.
    */
-  void SetTopAndLeft(Element& aElement, int32_t aX, int32_t aY);
+  MOZ_CAN_RUN_SCRIPT void SetTopAndLeft(Element& aElement, int32_t aX,
+                                        int32_t aY);
 
   /**
    * Reset a selected cell or collapsed selection (the caret) after table
    * editing.
    *
    * @param aTable      A table in the document.
    * @param aRow        The row ...
    * @param aCol        ... and column defining the cell where we will try to
@@ -2188,21 +2192,21 @@ class HTMLEditor final : public TextEdit
 
   void RemoveListenerAndDeleteRef(const nsAString& aEvent,
                                   nsIDOMEventListener* aListener,
                                   bool aUseCapture, ManualNACPtr aElement,
                                   PresShell* aPresShell);
   void DeleteRefToAnonymousNode(ManualNACPtr aContent, PresShell* aPresShell);
 
   /**
-   * RefereshEditingUI() may refresh editing UIs for current Selection, focus,
+   * RefreshEditingUI() may refresh editing UIs for current Selection, focus,
    * etc.  If this shows or hides some UIs, it causes reflow.  So, this is
    * not safe method.
    */
-  nsresult RefereshEditingUI();
+  MOZ_CAN_RUN_SCRIPT nsresult RefreshEditingUI();
 
   /**
    * Returns the offset of an element's frame to its absolute containing block.
    */
   nsresult GetElementOrigin(Element& aElement, int32_t& aX, int32_t& aY);
   nsresult GetPositionAndDimensions(Element& aElement, int32_t& aX, int32_t& aY,
                                     int32_t& aW, int32_t& aH,
                                     int32_t& aBorderLeft, int32_t& aBorderTop,
@@ -2213,73 +2217,75 @@ class HTMLEditor final : public TextEdit
   void UpdateRootElement();
 
   /**
    * SetAllResizersPosition() moves all resizers to proper position.
    * If the resizers are hidden or replaced with another set of resizers
    * while this is running, this returns error.  So, callers shouldn't
    * keep handling the resizers if this returns error.
    */
-  nsresult SetAllResizersPosition();
+  MOZ_CAN_RUN_SCRIPT nsresult SetAllResizersPosition();
 
   /**
    * Shows active resizers around an element's frame
    * @param aResizedElement [IN] a DOM Element
    */
-  nsresult ShowResizersInternal(Element& aResizedElement);
+  MOZ_CAN_RUN_SCRIPT nsresult ShowResizersInternal(Element& aResizedElement);
 
   /**
    * Hide resizers if they are visible.  If this is called while there is no
    * visible resizers, this does not return error, but does nothing.
    */
   nsresult HideResizersInternal();
 
   /**
    * RefreshResizersInternal() moves resizers to proper position.  This does
    * nothing if there is no resizing target.
    */
-  nsresult RefreshResizersInternal();
+  MOZ_CAN_RUN_SCRIPT nsresult RefreshResizersInternal();
 
   ManualNACPtr CreateResizer(int16_t aLocation, nsIContent& aParentContent);
-  void SetAnonymousElementPosition(int32_t aX, int32_t aY, Element* aResizer);
+  MOZ_CAN_RUN_SCRIPT void SetAnonymousElementPosition(int32_t aX, int32_t aY,
+                                                      Element* aResizer);
 
   ManualNACPtr CreateShadow(nsIContent& aParentContent,
                             Element& aOriginalObject);
 
   /**
    * SetShadowPosition() moves the shadow element to proper position.
    *
    * @param aShadowElement      Must be mResizingShadow or mPositioningShadow.
    * @param aElement            The element which has the shadow.
    * @param aElementX           Left of aElement.
    * @param aElementY           Top of aElement.
    */
-  nsresult SetShadowPosition(Element& aShadowElement, Element& aElement,
-                             int32_t aElementLeft, int32_t aElementTop);
+  MOZ_CAN_RUN_SCRIPT nsresult SetShadowPosition(Element& aShadowElement,
+                                                Element& aElement,
+                                                int32_t aElementLeft,
+                                                int32_t aElementTop);
 
   ManualNACPtr CreateResizingInfo(nsIContent& aParentContent);
-  nsresult SetResizingInfoPosition(int32_t aX, int32_t aY, int32_t aW,
-                                   int32_t aH);
+  MOZ_CAN_RUN_SCRIPT nsresult SetResizingInfoPosition(int32_t aX, int32_t aY,
+                                                      int32_t aW, int32_t aH);
 
   enum class ResizeAt {
     eX,
     eY,
     eWidth,
     eHeight,
   };
   int32_t GetNewResizingIncrement(int32_t aX, int32_t aY, ResizeAt aResizeAt);
 
-  nsresult StartResizing(Element* aHandle);
+  MOZ_CAN_RUN_SCRIPT nsresult StartResizing(Element* aHandle);
   int32_t GetNewResizingX(int32_t aX, int32_t aY);
   int32_t GetNewResizingY(int32_t aX, int32_t aY);
   int32_t GetNewResizingWidth(int32_t aX, int32_t aY);
   int32_t GetNewResizingHeight(int32_t aX, int32_t aY);
   void HideShadowAndInfo();
-  MOZ_CAN_RUN_SCRIPT
-  void SetFinalSize(int32_t aX, int32_t aY);
+  MOZ_CAN_RUN_SCRIPT void SetFinalSize(int32_t aX, int32_t aY);
   void SetResizeIncrements(int32_t aX, int32_t aY, int32_t aW, int32_t aH,
                            bool aPreserveRatio);
 
   /**
    * HideAnonymousEditingUIs() forcibly hides all editing UIs (resizers,
    * inline-table-editing UI, absolute positioning UI).
    */
   void HideAnonymousEditingUIs();
@@ -2290,91 +2296,93 @@ class HTMLEditor final : public TextEdit
    */
   void HideAnonymousEditingUIsIfUnnecessary();
 
   /**
    * sets the z-index of an element.
    * @param aElement [IN] the element
    * @param aZorder  [IN] the z-index
    */
-  void SetZIndex(Element& aElement, int32_t aZorder);
+  MOZ_CAN_RUN_SCRIPT void SetZIndex(Element& aElement, int32_t aZorder);
 
   /**
    * shows a grabber attached to an arbitrary element. The grabber is an image
    * positioned on the left hand side of the top border of the element. Draggin
    * and dropping it allows to change the element's absolute position in the
    * document. See chrome://editor/content/images/grabber.gif
    * @param aElement [IN] the element
    */
-  nsresult ShowGrabberInternal(Element& aElement);
+  MOZ_CAN_RUN_SCRIPT nsresult ShowGrabberInternal(Element& aElement);
 
   /**
    * Setting grabber to proper position for current mAbsolutelyPositionedObject.
    * For example, while an element has grabber, the element may be resized
    * or repositioned by script or something.  Then, you need to reset grabber
    * position with this.
    */
-  nsresult RefreshGrabberInternal();
+  MOZ_CAN_RUN_SCRIPT nsresult RefreshGrabberInternal();
 
   /**
    * hide the grabber if it shown.
    */
   void HideGrabberInternal();
 
   /**
    * CreateGrabberInternal() creates a grabber for moving aParentContent.
    * This sets mGrabber to the new grabber.  If this returns true, it's
    * always non-nullptr.  Otherwise, i.e., the grabber is hidden during
    * creation, this returns false.
    */
   bool CreateGrabberInternal(nsIContent& aParentContent);
 
-  nsresult StartMoving();
-  nsresult SetFinalPosition(int32_t aX, int32_t aY);
+  MOZ_CAN_RUN_SCRIPT nsresult StartMoving();
+  MOZ_CAN_RUN_SCRIPT nsresult SetFinalPosition(int32_t aX, int32_t aY);
   void AddPositioningOffset(int32_t& aX, int32_t& aY);
   void SnapToGrid(int32_t& newX, int32_t& newY);
   nsresult GrabberClicked();
-  nsresult EndMoving();
+  MOZ_CAN_RUN_SCRIPT nsresult EndMoving();
   nsresult GetTemporaryStyleForFocusedPositionedElement(Element& aElement,
                                                         nsAString& aReturn);
 
   /**
    * Shows inline table editing UI around a <table> element which contains
    * aCellElement.  This returns error if creating UI is hidden during this,
    * or detects another set of UI during this.  In such case, callers
    * shouldn't keep handling anything for the UI.
    *
    * @param aCellElement    Must be an <td> or <th> element.
    */
-  nsresult ShowInlineTableEditingUIInternal(Element& aCellElement);
+  MOZ_CAN_RUN_SCRIPT nsresult
+  ShowInlineTableEditingUIInternal(Element& aCellElement);
 
   /**
    * Hide all inline table editing UI.
    */
   void HideInlineTableEditingUIInternal();
 
   /**
    * RefreshInlineTableEditingUIInternal() moves inline table editing UI to
    * proper position.  This returns error if the UI is hidden or replaced
    * during moving.
    */
-  nsresult RefreshInlineTableEditingUIInternal();
+  MOZ_CAN_RUN_SCRIPT nsresult RefreshInlineTableEditingUIInternal();
 
   /**
    * IsEmptyTextNode() returns true if aNode is a text node and does not have
    * any visible characters.
    */
   bool IsEmptyTextNode(nsINode& aNode);
 
-  bool IsSimpleModifiableNode(nsIContent* aContent, nsAtom* aProperty,
-                              nsAtom* aAttribute, const nsAString* aValue);
-  MOZ_CAN_RUN_SCRIPT
-  nsresult SetInlinePropertyOnNodeImpl(nsIContent& aNode, nsAtom& aProperty,
-                                       nsAtom* aAttribute,
-                                       const nsAString& aValue);
+  MOZ_CAN_RUN_SCRIPT bool IsSimpleModifiableNode(nsIContent* aContent,
+                                                 nsAtom* aProperty,
+                                                 nsAtom* aAttribute,
+                                                 const nsAString* aValue);
+  MOZ_CAN_RUN_SCRIPT nsresult
+  SetInlinePropertyOnNodeImpl(nsIContent& aNode, nsAtom& aProperty,
+                              nsAtom* aAttribute, const nsAString& aValue);
   typedef enum { eInserted, eAppended } InsertedOrAppended;
   void DoContentInserted(nsIContent* aChild, InsertedOrAppended);
 
   /**
    * Returns an anonymous Element of type aTag,
    * child of aParentContent. If aIsCreatedHidden is true, the class
    * "hidden" is added to the created element. If aAnonClass is not
    * the empty string, it becomes the value of the attribute "_moz_anonclass"
--- a/editor/libeditor/HTMLEditorDocumentCommands.cpp
+++ b/editor/libeditor/HTMLEditorDocumentCommands.cpp
@@ -169,43 +169,43 @@ nsresult SetDocumentStateCommand::DoComm
       if (NS_WARN_IF(!htmlEditor)) {
         return NS_ERROR_INVALID_ARG;
       }
       ErrorResult error;
       bool enabled = aParams->GetBool(STATE_ATTRIBUTE, error);
       if (NS_WARN_IF(error.Failed())) {
         return error.StealNSResult();
       }
-      htmlEditor->EnableObjectResizer(enabled);
+      MOZ_KnownLive(htmlEditor)->EnableObjectResizer(enabled);
       return NS_OK;
     }
     case Command::ToggleInlineTableEditor: {
       HTMLEditor* htmlEditor = aTextEditor.AsHTMLEditor();
       if (NS_WARN_IF(!htmlEditor)) {
         return NS_ERROR_INVALID_ARG;
       }
       ErrorResult error;
       bool enabled = aParams->GetBool(STATE_ATTRIBUTE, error);
       if (NS_WARN_IF(error.Failed())) {
         return error.StealNSResult();
       }
-      htmlEditor->EnableInlineTableEditor(enabled);
+      MOZ_KnownLive(htmlEditor)->EnableInlineTableEditor(enabled);
       return NS_OK;
     }
     case Command::ToggleAbsolutePositionEditor: {
       HTMLEditor* htmlEditor = aTextEditor.AsHTMLEditor();
       if (NS_WARN_IF(!htmlEditor)) {
         return NS_ERROR_INVALID_ARG;
       }
       ErrorResult error;
       bool enabled = aParams->GetBool(STATE_ATTRIBUTE, error);
       if (NS_WARN_IF(error.Failed())) {
         return error.StealNSResult();
       }
-      htmlEditor->EnableAbsolutePositionEditor(enabled);
+      MOZ_KnownLive(htmlEditor)->EnableAbsolutePositionEditor(enabled);
       return NS_OK;
     }
     default:
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
 nsresult SetDocumentStateCommand::GetCommandStateParams(
--- a/editor/libeditor/HTMLEditorObjectResizer.cpp
+++ b/editor/libeditor/HTMLEditorObjectResizer.cpp
@@ -142,48 +142,55 @@ nsresult HTMLEditor::SetAllResizersPosit
   // While moving each resizer, mutation event listener may hide the resizers.
   // And in worst case, new resizers may be recreated.  So, we need to store
   // all resizers here, and then, if we detect a resizer is removed or replaced,
   // we should do nothing anymore.
   // FYI: Note that only checking if mTopLeftHandle is replaced is enough.
   //      We're may be in hot path if user resizes an element a lot.  So,
   //      we should just add-ref mTopLeftHandle.
   RefPtr<Element> topLeftHandle = mTopLeftHandle.get();
-  SetAnonymousElementPosition(x - rw, y - rh, mTopLeftHandle);
+  SetAnonymousElementPosition(x - rw, y - rh, topLeftHandle);
   if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) {
     return NS_ERROR_FAILURE;
   }
-  SetAnonymousElementPosition(x + w / 2 - rw, y - rh, mTopHandle);
+  RefPtr<Element> topHandle = mTopHandle.get();
+  SetAnonymousElementPosition(x + w / 2 - rw, y - rh, topHandle);
   if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) {
     return NS_ERROR_FAILURE;
   }
-  SetAnonymousElementPosition(x + w - rw - 1, y - rh, mTopRightHandle);
+  RefPtr<Element> topRightHandle = mTopRightHandle.get();
+  SetAnonymousElementPosition(x + w - rw - 1, y - rh, topRightHandle);
   if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) {
     return NS_ERROR_FAILURE;
   }
 
-  SetAnonymousElementPosition(x - rw, y + h / 2 - rh, mLeftHandle);
+  RefPtr<Element> leftHandle = mLeftHandle.get();
+  SetAnonymousElementPosition(x - rw, y + h / 2 - rh, leftHandle);
   if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) {
     return NS_ERROR_FAILURE;
   }
-  SetAnonymousElementPosition(x + w - rw - 1, y + h / 2 - rh, mRightHandle);
+  RefPtr<Element> rightHandle = mRightHandle.get();
+  SetAnonymousElementPosition(x + w - rw - 1, y + h / 2 - rh, rightHandle);
   if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) {
     return NS_ERROR_FAILURE;
   }
 
-  SetAnonymousElementPosition(x - rw, y + h - rh - 1, mBottomLeftHandle);
+  RefPtr<Element> bottomLeftHandle = mBottomLeftHandle.get();
+  SetAnonymousElementPosition(x - rw, y + h - rh - 1, bottomLeftHandle);
   if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) {
     return NS_ERROR_FAILURE;
   }
-  SetAnonymousElementPosition(x + w / 2 - rw, y + h - rh - 1, mBottomHandle);
+  RefPtr<Element> bottomHandle = mBottomHandle.get();
+  SetAnonymousElementPosition(x + w / 2 - rw, y + h - rh - 1, bottomHandle);
   if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) {
     return NS_ERROR_FAILURE;
   }
+  RefPtr<Element> bottomRightHandle = mBottomRightHandle.get();
   SetAnonymousElementPosition(x + w - rw - 1, y + h - rh - 1,
-                              mBottomRightHandle);
+                              bottomRightHandle);
   if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -219,17 +226,19 @@ nsresult HTMLEditor::RefreshResizersInte
   rv = SetAllResizersPosition();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(
       mResizingShadow,
       "SetAllResizersPosition() should return error if resizers are hidden");
-  rv = SetShadowPosition(*mResizingShadow, *mResizedObject, mResizedObjectX,
+  RefPtr<Element> resizingShadow = mResizingShadow.get();
+  RefPtr<Element> resizedObject = mResizedObject;
+  rv = SetShadowPosition(*resizingShadow, *resizedObject, mResizedObjectX,
                          mResizedObjectY);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult HTMLEditor::ShowResizersInternal(Element& aResizedElement) {
@@ -374,17 +383,18 @@ nsresult HTMLEditor::ShowResizersInterna
     }
     if (NS_WARN_IF(mResizingShadow) ||
         NS_WARN_IF(mResizedObject != &aResizedElement)) {
       return NS_ERROR_FAILURE;
     }
     mResizingShadow = std::move(newShadow);
 
     // and set its position
-    rv = SetShadowPosition(*mResizingShadow, aResizedElement, mResizedObjectX,
+    RefPtr<Element> resizingShadow = mResizingShadow.get();
+    rv = SetShadowPosition(*resizingShadow, aResizedElement, mResizedObjectX,
                            mResizedObjectY);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       if (NS_WARN_IF(mBottomRightHandle.get() != createdBottomRightNalde)) {
         return NS_ERROR_FAILURE;
       }
       break;
     }
 
@@ -582,19 +592,20 @@ nsresult HTMLEditor::StartResizing(Eleme
   } else if (locationStr.Equals(kBottomRight)) {
     SetResizeIncrements(0, 0, 1, 1, preserveRatio);
   }
 
   // make the shadow appear
   mResizingShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true);
 
   // position it
-  mCSSEditUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::width,
+  RefPtr<Element> resizingShadow = mResizingShadow.get();
+  mCSSEditUtils->SetCSSPropertyPixels(*resizingShadow, *nsGkAtoms::width,
                                       mResizedObjectWidth);
-  mCSSEditUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::height,
+  mCSSEditUtils->SetCSSPropertyPixels(*resizingShadow, *nsGkAtoms::height,
                                       mResizedObjectHeight);
 
   // add a mouse move listener to the editor
   if (NS_WARN_IF(!mEventListener)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   nsresult rv = static_cast<HTMLEditorEventListener*>(mEventListener.get())
                     ->ListenToMouseMoveEventForResizers(true);
@@ -720,19 +731,20 @@ nsresult HTMLEditor::SetResizingInfoPosi
   } else {
     // should only occur when mActivatedHandle is one of the 3 bottom-side
     // handles, but this is a reasonable default if it isn't any of them (?)
     infoYPosition = aY + aH;
   }
 
   // Offset info box by 20 so it's not directly under the mouse cursor.
   const int mouseCursorOffset = 20;
-  mCSSEditUtils->SetCSSPropertyPixels(*mResizingInfo, *nsGkAtoms::left,
+  RefPtr<Element> resizingInfo = mResizingInfo.get();
+  mCSSEditUtils->SetCSSPropertyPixels(*resizingInfo, *nsGkAtoms::left,
                                       infoXPosition + mouseCursorOffset);
-  mCSSEditUtils->SetCSSPropertyPixels(*mResizingInfo, *nsGkAtoms::top,
+  mCSSEditUtils->SetCSSPropertyPixels(*resizingInfo, *nsGkAtoms::top,
                                       infoYPosition + mouseCursorOffset);
 
   nsCOMPtr<nsIContent> textInfo = mResizingInfo->GetFirstChild();
   ErrorResult erv;
   if (textInfo) {
     mResizingInfo->RemoveChild(*textInfo, erv);
     NS_ENSURE_TRUE(!erv.Failed(), erv.StealNSResult());
     textInfo = nullptr;
@@ -882,23 +894,23 @@ nsresult HTMLEditor::OnMouseMove(MouseEv
     int32_t clientX = aMouseEvent->ClientX();
     int32_t clientY = aMouseEvent->ClientY();
 
     int32_t newX = GetNewResizingX(clientX, clientY);
     int32_t newY = GetNewResizingY(clientX, clientY);
     int32_t newWidth = GetNewResizingWidth(clientX, clientY);
     int32_t newHeight = GetNewResizingHeight(clientX, clientY);
 
-    mCSSEditUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::left,
+    RefPtr<Element> resizingShadow = mResizingShadow.get();
+    mCSSEditUtils->SetCSSPropertyPixels(*resizingShadow, *nsGkAtoms::left,
                                         newX);
-    mCSSEditUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::top,
-                                        newY);
-    mCSSEditUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::width,
+    mCSSEditUtils->SetCSSPropertyPixels(*resizingShadow, *nsGkAtoms::top, newY);
+    mCSSEditUtils->SetCSSPropertyPixels(*resizingShadow, *nsGkAtoms::width,
                                         newWidth);
-    mCSSEditUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::height,
+    mCSSEditUtils->SetCSSPropertyPixels(*resizingShadow, *nsGkAtoms::height,
                                         newHeight);
 
     return SetResizingInfoPosition(newX, newY, newWidth, newHeight);
   }
 
   AutoEditActionDataSetter editActionData(*this, EditAction::eMoveElement);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return NS_ERROR_NOT_INITIALIZED;
@@ -923,19 +935,20 @@ nsresult HTMLEditor::OnMouseMove(MouseEv
     int32_t clientX = aMouseEvent->ClientX();
     int32_t clientY = aMouseEvent->ClientY();
 
     int32_t newX = mPositionedObjectX + clientX - mOriginalX;
     int32_t newY = mPositionedObjectY + clientY - mOriginalY;
 
     SnapToGrid(newX, newY);
 
-    mCSSEditUtils->SetCSSPropertyPixels(*mPositioningShadow, *nsGkAtoms::left,
+    RefPtr<Element> positioningShadow = mPositioningShadow.get();
+    mCSSEditUtils->SetCSSPropertyPixels(*positioningShadow, *nsGkAtoms::left,
                                         newX);
-    mCSSEditUtils->SetCSSPropertyPixels(*mPositioningShadow, *nsGkAtoms::top,
+    mCSSEditUtils->SetCSSPropertyPixels(*positioningShadow, *nsGkAtoms::top,
                                         newY);
   }
   return NS_OK;
 }
 
 void HTMLEditor::SetFinalSize(int32_t aX, int32_t aY) {
   if (!mResizedObject) {
     // paranoia
@@ -987,21 +1000,21 @@ void HTMLEditor::SetFinalSize(int32_t aX
     }
 
     if (setHeight &&
         resizedObject->HasAttr(kNameSpaceID_None, nsGkAtoms::height)) {
       RemoveAttributeWithTransaction(*resizedObject, *nsGkAtoms::height);
     }
 
     if (setWidth) {
-      mCSSEditUtils->SetCSSPropertyPixels(*mResizedObject, *nsGkAtoms::width,
+      mCSSEditUtils->SetCSSPropertyPixels(*resizedObject, *nsGkAtoms::width,
                                           width);
     }
     if (setHeight) {
-      mCSSEditUtils->SetCSSPropertyPixels(*mResizedObject, *nsGkAtoms::height,
+      mCSSEditUtils->SetCSSPropertyPixels(*resizedObject, *nsGkAtoms::height,
                                           height);
     }
   } else {
     // we use HTML size and remove all equivalent CSS properties
 
     // we set the CSS width and height to remove it later,
     // triggering an immediate reflow; otherwise, we have problems
     // with asynchronous reflow
--- a/editor/nsIDocumentStateListener.idl
+++ b/editor/nsIDocumentStateListener.idl
@@ -3,14 +3,15 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 [scriptable, uuid(050cdc00-3b8e-11d3-9ce4-a458f454fcbc)]
 interface nsIDocumentStateListener : nsISupports
 {
-
-	void        NotifyDocumentCreated();
-	void        NotifyDocumentWillBeDestroyed();
-	void        NotifyDocumentStateChanged(in boolean nowDirty);
-
+  [can_run_script]
+  void NotifyDocumentCreated();
+  [can_run_script]
+  void NotifyDocumentWillBeDestroyed();
+  [can_run_script]
+  void NotifyDocumentStateChanged(in boolean nowDirty);
 };
--- a/editor/nsIEditor.idl
+++ b/editor/nsIEditor.idl
@@ -116,44 +116,47 @@ interface nsIEditor  : nsISupports
 
   /** Sets the current 'Save' document character set */
   [can_run_script]  // setter only
   attribute ACString documentCharacterSet;
 
   /** to be used ONLY when we need to override the doc's modification
     * state (such as when it's saved).
     */
+  [can_run_script]
   void resetModificationCount();
 
   /** Gets the modification count of the document we are editing.
     * @return the modification count of the document being edited.
     *         Zero means unchanged.
     */
   long getModificationCount();
 
   /** called each time we modify the document.
     * Increments the modification count of the document.
     * @param  aModCount  the number of modifications by which
     *                    to increase or decrease the count
     */
+  [can_run_script]
   void incrementModificationCount(in long aModCount);
 
   /* ------------ Transaction methods -------------- */
 
   /** transactionManager Get the transaction manager the editor is using.
     */
   readonly attribute nsITransactionManager transactionManager;
 
   /** doTransaction() fires a transaction.
     * It is provided here so clients can create their own transactions.
     * If a transaction manager is present, it is used.
     * Otherwise, the transaction is just executed directly.
     *
     * @param aTxn the transaction to execute
     */
+  [can_run_script]
   void doTransaction(in nsITransaction txn);
 
 
   /** turn the undo system on or off
     * @param aEnable  if PR_TRUE, the undo system is turned on if available
     *                 if PR_FALSE the undo system is turned off if it
     *                 was previously on
     * @return         if aEnable is PR_TRUE, returns NS_OK if
@@ -215,16 +218,17 @@ interface nsIEditor  : nsISupports
   void beginTransaction();
 
   /** endTransaction is a signal to the editor that the caller is
     * finished updating the content model.<br>
     * beginUpdate must be called before endTransaction is called.<br>
     * Calls to beginTransaction can be nested, as long as endTransaction
     * is called once per beginTransaction.
     */
+  [can_run_script]
   void endTransaction();
 
   /**
    * While setting the flag with this method to false, CreateElementTransaction,
    * DeleteRangeTransaction, DeleteTextTransaction, InsertNodeTransaction,
    * InsertTextTransaction and SplitNodeTransaction won't change Selection
    * after modifying the DOM tree.
    * Note that calling this with false does not guarantee that Selection won't
--- a/editor/nsIHTMLAbsPosEditor.idl
+++ b/editor/nsIHTMLAbsPosEditor.idl
@@ -7,16 +7,17 @@
 #include "domstubs.idl"
 
 [scriptable, builtinclass, uuid(91375f52-20e6-4757-9835-eb04fabe5498)]
 interface nsIHTMLAbsPosEditor : nsISupports
 {
   /**
    * true if Absolute Positioning handling is enabled in the editor
    */
+  [can_run_script]  // Setter only.
   attribute boolean absolutePositioningEnabled;
 
 
   /* Utility methods */
 
   /**
    * true if Snap To Grid is enabled in the editor.
    */
@@ -30,10 +31,11 @@ interface nsIHTMLAbsPosEditor : nsISuppo
 
   /* Other */
 
   /**
    * refreshes the grabber if it shown, possibly updating its position or
    * even hiding it.
    * FYI: Current user in script is only BlueGriffon.
    */
+  [can_run_script]
   void refreshGrabber();
 };
--- a/editor/nsIHTMLEditor.idl
+++ b/editor/nsIHTMLEditor.idl
@@ -408,16 +408,17 @@ interface nsIHTMLEditor : nsISupports
 
   /**
    * checkSelectionStateForAnonymousButtons() may refresh editing UI such as
    * resizers, inline-table-editing UI, absolute positioning UI for current
    * Selection and focus state.  When this method shows or hides UI, the
    * editor (and/or its document/window) could be broken by mutation observers.
    * FYI: Current user in script is only BlueGriffon.
    */
+  [can_run_script]
   void checkSelectionStateForAnonymousButtons();
 
   boolean isAnonymousElement(in Element aElement);
 
   /**
    * A boolean indicating if a return key pressed in a paragraph creates
    * another paragraph or just inserts a <br> at the caret
    *
--- a/editor/nsIHTMLInlineTableEditor.idl
+++ b/editor/nsIHTMLInlineTableEditor.idl
@@ -10,20 +10,22 @@
 interface nsIHTMLInlineTableEditor : nsISupports
 {
   /**
    * boolean indicating if inline table editing is enabled in the editor.
    * When inline table editing is enabled, and when the selection is
    * contained in a table cell, special buttons allowing to add/remove
    * a line/column are available on the cell's border.
    */
+  [can_run_script]  // Setter only.
   attribute boolean inlineTableEditingEnabled;
 
   /**
    * Refresh already visible inline table editing UI.
    * If inline table editing UI is not visible, this does nothing.
    * If the set of inline table editing UI is hidden or replaced with new
    * one while this is called, this throws an exception.
    * FYI: Current user in script is only BlueGriffon.
    */
+  [can_run_script]
   void refreshInlineTableEditingUI();
 };
 
--- a/editor/nsIHTMLObjectResizer.idl
+++ b/editor/nsIHTMLObjectResizer.idl
@@ -21,24 +21,26 @@ interface nsIHTMLObjectResizer : nsISupp
   const short eRight = 4;
   const short eBottomLeft = 5;
   const short eBottom = 6;
   const short eBottomRight = 7;
 
   /**
    * a boolean indicating if object resizing is enabled in the editor
    */
+  [can_run_script]  // Setter only.
   attribute boolean objectResizingEnabled;
 
   /**
    * Hide resizers if they are visible.  If this is called while there is no
    * visible resizers, this does not throw exception, just does nothing.
    */
   void hideResizers();
 
   /**
    * Refresh positions of resizers.  If you change size of target of resizers,
    * you need to refresh position of resizers with calling this.
    * FYI: Current user in script is only BlueGriffon.
    */
+  [can_run_script]
   void refreshResizers();
 };
 
--- a/editor/spellchecker/TextServicesDocument.cpp
+++ b/editor/spellchecker/TextServicesDocument.cpp
@@ -1084,19 +1084,20 @@ nsresult TextServicesDocument::InsertTex
 
     nsresult rv = SetSelection(mSelStartOffset, 0);
 
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // AutoTransactionBatchExternal grabs mTextEditor, so, we don't need to grab
   // the instance with local variable here.
-  AutoTransactionBatchExternal treatAsOneTransaction(*mTextEditor);
-
-  nsresult rv = mTextEditor->InsertTextAsAction(aText);
+  RefPtr<TextEditor> textEditor = mTextEditor;
+  AutoTransactionBatchExternal treatAsOneTransaction(*textEditor);
+
+  nsresult rv = textEditor->InsertTextAsAction(aText);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   int32_t strLength = aText.Length();
 
   OffsetEntry* itEntry;
   OffsetEntry* entry = mOffsetTable[mSelStartIndex];
--- a/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.cpp
+++ b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.cpp
@@ -35,9 +35,25 @@ RemoteSpellcheckEngineChild::SetCurrentD
       },
       [spellChecker](ResponseRejectReason&& aReason) {
         spellChecker->mCurrentDictionary.Truncate();
         return GenericPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
                                                __func__);
       });
 }
 
+RefPtr<CheckWordPromise> RemoteSpellcheckEngineChild::CheckWords(
+    const nsTArray<nsString>& aWords) {
+  RefPtr<mozSpellChecker> kungFuDeathGrip = mOwner;
+
+  return SendCheckAsync(aWords)->Then(
+      GetMainThreadSerialEventTarget(), __func__,
+      [kungFuDeathGrip](nsTArray<bool>&& aIsMisspelled) {
+        return CheckWordPromise::CreateAndResolve(std::move(aIsMisspelled),
+                                                  __func__);
+      },
+      [kungFuDeathGrip](mozilla::ipc::ResponseRejectReason&& aReason) {
+        return CheckWordPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
+                                                 __func__);
+      });
+}
+
 }  // namespace mozilla
--- a/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.h
+++ b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.h
@@ -17,15 +17,17 @@ class RemoteSpellcheckEngineChild
     : public mozilla::PRemoteSpellcheckEngineChild {
  public:
   explicit RemoteSpellcheckEngineChild(mozSpellChecker* aOwner);
   virtual ~RemoteSpellcheckEngineChild();
 
   RefPtr<GenericPromise> SetCurrentDictionaryFromList(
       const nsTArray<nsString>& aList);
 
+  RefPtr<CheckWordPromise> CheckWords(const nsTArray<nsString>& aWords);
+
  private:
   mozSpellChecker* mOwner;
 };
 
 }  // namespace mozilla
 
 #endif  // RemoteSpellcheckEngineChild_h_
--- a/extensions/spellcheck/src/mozSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozSpellChecker.cpp
@@ -112,26 +112,17 @@ nsresult mozSpellChecker::NextMisspelled
     selOffset = 0;
   }
   return NS_OK;
 }
 
 RefPtr<CheckWordPromise> mozSpellChecker::CheckWords(
     const nsTArray<nsString>& aWords) {
   if (XRE_IsContentProcess()) {
-    return mEngine->SendCheckAsync(aWords)->Then(
-        GetMainThreadSerialEventTarget(), __func__,
-        [](nsTArray<bool>&& aIsMisspelled) {
-          return CheckWordPromise::CreateAndResolve(std::move(aIsMisspelled),
-                                                    __func__);
-        },
-        [](mozilla::ipc::ResponseRejectReason&& aReason) {
-          return CheckWordPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
-                                                   __func__);
-        });
+    return mEngine->CheckWords(aWords);
   }
 
   nsTArray<bool> misspells;
   misspells.SetCapacity(aWords.Length());
   for (auto& word : aWords) {
     bool misspelled;
     nsresult rv = CheckWord(word, &misspelled, nullptr);
     if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -121,16 +121,37 @@ gfxUserFontEntry::gfxUserFontEntry(
   mStyleRange = aStyle;
   mFeatureSettings.AppendElements(aFeatureSettings);
   mVariationSettings.AppendElements(aVariationSettings);
   mLanguageOverride = aLanguageOverride;
   mCharacterMap = aUnicodeRanges;
   mRangeFlags = aRangeFlags;
 }
 
+void gfxUserFontEntry::UpdateAttributes(
+    WeightRange aWeight, StretchRange aStretch, SlantStyleRange aStyle,
+    const nsTArray<gfxFontFeature>& aFeatureSettings,
+    const nsTArray<gfxFontVariation>& aVariationSettings,
+    uint32_t aLanguageOverride, gfxCharacterMap* aUnicodeRanges,
+    StyleFontDisplay aFontDisplay, RangeFlags aRangeFlags) {
+  // Remove the entry from the user font cache, if present there, as the cache
+  // key may no longer be correct with the new attributes.
+  gfxUserFontSet::UserFontCache::ForgetFont(this);
+
+  mFontDisplay = aFontDisplay;
+  mWeightRange = aWeight;
+  mStretchRange = aStretch;
+  mStyleRange = aStyle;
+  mFeatureSettings = aFeatureSettings;
+  mVariationSettings = aVariationSettings;
+  mLanguageOverride = aLanguageOverride;
+  mCharacterMap = aUnicodeRanges;
+  mRangeFlags = aRangeFlags;
+}
+
 gfxUserFontEntry::~gfxUserFontEntry() {
   // Assert that we don't drop any gfxUserFontEntry objects during a Servo
   // traversal, since PostTraversalTask objects can hold raw pointers to
   // gfxUserFontEntry objects.
   MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
 }
 
 bool gfxUserFontEntry::Matches(
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -142,16 +142,17 @@ class gfxUserFontFamily : public gfxFont
   friend class gfxUserFontSet;
 
   explicit gfxUserFontFamily(const nsACString& aName) : gfxFontFamily(aName) {}
 
   virtual ~gfxUserFontFamily();
 
   // add the given font entry to the end of the family's list
   void AddFontEntry(gfxFontEntry* aFontEntry) {
+    MOZ_ASSERT(!mIsSimpleFamily, "not valid for user-font families");
     // keep ref while removing existing entry
     RefPtr<gfxFontEntry> fe = aFontEntry;
     // remove existing entry, if already present
     mAvailableFonts.RemoveElement(aFontEntry);
     // insert at the beginning so that the last-defined font is the first
     // one in the fontlist used for matching, as per CSS Fonts spec
     mAvailableFonts.InsertElementAt(0, aFontEntry);
 
@@ -164,16 +165,21 @@ class gfxUserFontFamily : public gfxFont
       ToLowerCase(thisName);
       ToLowerCase(entryName);
       MOZ_ASSERT(thisName.Equals(entryName));
 #endif
     }
     ResetCharacterMap();
   }
 
+  void RemoveFontEntry(gfxFontEntry* aFontEntry) {
+    MOZ_ASSERT(!mIsSimpleFamily, "not valid for user-font families");
+    mAvailableFonts.RemoveElement(aFontEntry);
+  }
+
   // Remove all font entries from the family
   void DetachFontEntries() { mAvailableFonts.Clear(); }
 };
 
 class gfxUserFontEntry;
 class gfxOTSContext;
 
 class gfxUserFontSet {
@@ -536,16 +542,27 @@ class gfxUserFontEntry : public gfxFontE
                    const nsTArray<gfxFontFeature>& aFeatureSettings,
                    const nsTArray<gfxFontVariation>& aVariationSettings,
                    uint32_t aLanguageOverride, gfxCharacterMap* aUnicodeRanges,
                    mozilla::StyleFontDisplay aFontDisplay,
                    RangeFlags aRangeFlags);
 
   virtual ~gfxUserFontEntry();
 
+  // Update the attributes of the entry to the given values, without disturbing
+  // the associated platform font entry or in-progress downloads.
+  void UpdateAttributes(WeightRange aWeight, StretchRange aStretch,
+                        SlantStyleRange aStyle,
+                        const nsTArray<gfxFontFeature>& aFeatureSettings,
+                        const nsTArray<gfxFontVariation>& aVariationSettings,
+                        uint32_t aLanguageOverride,
+                        gfxCharacterMap* aUnicodeRanges,
+                        mozilla::StyleFontDisplay aFontDisplay,
+                        RangeFlags aRangeFlags);
+
   // Return whether the entry matches the given list of attributes
   bool Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                WeightRange aWeight, StretchRange aStretch,
                SlantStyleRange aStyle,
                const nsTArray<gfxFontFeature>& aFeatureSettings,
                const nsTArray<gfxFontVariation>& aVariationSettings,
                uint32_t aLanguageOverride, gfxCharacterMap* aUnicodeRanges,
                mozilla::StyleFontDisplay aFontDisplay, RangeFlags aRangeFlags);
@@ -585,19 +602,19 @@ class gfxUserFontEntry : public gfxFontE
 
   // load the font - starts the loading of sources which continues until
   // a valid font resource is found or all sources fail
   void Load();
 
   // methods to expose some information to FontFaceSet::UserFontSet
   // since we can't make that class a friend
   void SetLoader(nsFontFaceLoader* aLoader) { mLoader = aLoader; }
-  nsFontFaceLoader* GetLoader() { return mLoader; }
-  gfxFontSrcPrincipal* GetPrincipal() { return mPrincipal; }
-  uint32_t GetSrcIndex() { return mSrcIndex; }
+  nsFontFaceLoader* GetLoader() const { return mLoader; }
+  gfxFontSrcPrincipal* GetPrincipal() const { return mPrincipal; }
+  uint32_t GetSrcIndex() const { return mSrcIndex; }
   void GetFamilyNameAndURIForLogging(nsACString& aFamilyName, nsACString& aURI);
 
   gfxFontEntry* Clone() const override {
     MOZ_ASSERT_UNREACHABLE("cannot Clone user fonts");
     return nullptr;
   }
 
 #ifdef DEBUG
--- a/layout/base/ShapeUtils.cpp
+++ b/layout/base/ShapeUtils.cpp
@@ -11,117 +11,102 @@
 #include "nsCSSRendering.h"
 #include "nsMargin.h"
 #include "nsStyleCoord.h"
 #include "nsStyleStruct.h"
 #include "SVGContentUtils.h"
 
 namespace mozilla {
 
-nscoord ShapeUtils::ComputeShapeRadius(const StyleShapeRadius aType,
+nscoord ShapeUtils::ComputeShapeRadius(const StyleShapeRadius& aType,
                                        const nscoord aCenter,
                                        const nscoord aPosMin,
                                        const nscoord aPosMax) {
+  MOZ_ASSERT(aType.IsFarthestSide() || aType.IsClosestSide());
   nscoord dist1 = std::abs(aPosMin - aCenter);
   nscoord dist2 = std::abs(aPosMax - aCenter);
   nscoord length = 0;
-  switch (aType) {
-    case StyleShapeRadius::FarthestSide:
-      length = dist1 > dist2 ? dist1 : dist2;
-      break;
-    case StyleShapeRadius::ClosestSide:
-      length = dist1 > dist2 ? dist2 : dist1;
-      break;
+  if (aType.IsFarthestSide()) {
+    length = dist1 > dist2 ? dist1 : dist2;
+  } else {
+    length = dist1 > dist2 ? dist2 : dist1;
   }
   return length;
 }
 
 nsPoint ShapeUtils::ComputeCircleOrEllipseCenter(
     const StyleBasicShape& aBasicShape, const nsRect& aRefBox) {
-  MOZ_ASSERT(aBasicShape.GetShapeType() == StyleBasicShapeType::Circle ||
-                 aBasicShape.GetShapeType() == StyleBasicShapeType::Ellipse,
+  MOZ_ASSERT(aBasicShape.IsCircle() || aBasicShape.IsEllipse(),
              "The basic shape must be circle() or ellipse!");
 
+  const auto& position = aBasicShape.IsCircle()
+                             ? aBasicShape.AsCircle().position
+                             : aBasicShape.AsEllipse().position;
+
   nsPoint topLeft, anchor;
   nsSize size(aRefBox.Size());
-  nsImageRenderer::ComputeObjectAnchorPoint(aBasicShape.GetPosition(), size,
-                                            size, &topLeft, &anchor);
+  nsImageRenderer::ComputeObjectAnchorPoint(position, size, size, &topLeft,
+                                            &anchor);
   return anchor + aRefBox.TopLeft();
 }
 
 nscoord ShapeUtils::ComputeCircleRadius(const StyleBasicShape& aBasicShape,
                                         const nsPoint& aCenter,
                                         const nsRect& aRefBox) {
-  MOZ_ASSERT(aBasicShape.GetShapeType() == StyleBasicShapeType::Circle,
-             "The basic shape must be circle()!");
+  MOZ_ASSERT(aBasicShape.IsCircle(), "The basic shape must be circle()!");
+  const auto& radius = aBasicShape.AsCircle().radius;
+  if (radius.IsLength()) {
+    return radius.AsLength().Resolve([&] {
+      // We resolve percent <shape-radius> value for circle() as defined here:
+      // https://drafts.csswg.org/css-shapes/#funcdef-circle
+      double referenceLength = SVGContentUtils::ComputeNormalizedHypotenuse(
+          aRefBox.width, aRefBox.height);
+      return NSToCoordRound(referenceLength);
+    });
+  }
 
-  const nsTArray<nsStyleCoord>& coords = aBasicShape.Coordinates();
-  MOZ_ASSERT(coords.Length() == 1, "wrong number of arguments");
-  nscoord r = 0;
-  if (coords[0].GetUnit() == eStyleUnit_Enumerated) {
-    const auto styleShapeRadius = coords[0].GetEnumValue<StyleShapeRadius>();
-    nscoord horizontal = ComputeShapeRadius(styleShapeRadius, aCenter.x,
-                                            aRefBox.x, aRefBox.XMost());
-    nscoord vertical = ComputeShapeRadius(styleShapeRadius, aCenter.y,
-                                          aRefBox.y, aRefBox.YMost());
-    r = styleShapeRadius == StyleShapeRadius::FarthestSide
-            ? std::max(horizontal, vertical)
-            : std::min(horizontal, vertical);
-  } else {
-    // We resolve percent <shape-radius> value for circle() as defined here:
-    // https://drafts.csswg.org/css-shapes/#funcdef-circle
-    double referenceLength = SVGContentUtils::ComputeNormalizedHypotenuse(
-        aRefBox.width, aRefBox.height);
-    r = coords[0].ComputeCoordPercentCalc(NSToCoordRound(referenceLength));
-  }
-  return r;
+  nscoord horizontal =
+      ComputeShapeRadius(radius, aCenter.x, aRefBox.x, aRefBox.XMost());
+  nscoord vertical =
+      ComputeShapeRadius(radius, aCenter.y, aRefBox.y, aRefBox.YMost());
+  return radius.IsFarthestSide() ? std::max(horizontal, vertical)
+                                 : std::min(horizontal, vertical);
 }
 
 nsSize ShapeUtils::ComputeEllipseRadii(const StyleBasicShape& aBasicShape,
                                        const nsPoint& aCenter,
                                        const nsRect& aRefBox) {
-  MOZ_ASSERT(aBasicShape.GetShapeType() == StyleBasicShapeType::Ellipse,
-             "The basic shape must be ellipse()!");
-
-  const nsTArray<nsStyleCoord>& coords = aBasicShape.Coordinates();
-  MOZ_ASSERT(coords.Length() == 2, "wrong number of arguments");
+  MOZ_ASSERT(aBasicShape.IsEllipse(), "The basic shape must be ellipse()!");
+  const auto& ellipse = aBasicShape.AsEllipse();
   nsSize radii;
-
-  if (coords[0].GetUnit() == eStyleUnit_Enumerated) {
-    const StyleShapeRadius radiusX = coords[0].GetEnumValue<StyleShapeRadius>();
-    radii.width =
-        ComputeShapeRadius(radiusX, aCenter.x, aRefBox.x, aRefBox.XMost());
+  if (ellipse.semiaxis_x.IsLength()) {
+    radii.width = ellipse.semiaxis_x.AsLength().Resolve(aRefBox.width);
   } else {
-    radii.width = coords[0].ComputeCoordPercentCalc(aRefBox.width);
+    radii.width = ComputeShapeRadius(ellipse.semiaxis_x, aCenter.x, aRefBox.x,
+                                     aRefBox.XMost());
   }
 
-  if (coords[1].GetUnit() == eStyleUnit_Enumerated) {
-    const StyleShapeRadius radiusY = coords[1].GetEnumValue<StyleShapeRadius>();
-    radii.height =
-        ComputeShapeRadius(radiusY, aCenter.y, aRefBox.y, aRefBox.YMost());
+  if (ellipse.semiaxis_y.IsLength()) {
+    radii.height = ellipse.semiaxis_y.AsLength().Resolve(aRefBox.height);
   } else {
-    radii.height = coords[1].ComputeCoordPercentCalc(aRefBox.height);
+    radii.height = ComputeShapeRadius(ellipse.semiaxis_y, aCenter.y, aRefBox.y,
+                                      aRefBox.YMost());
   }
 
   return radii;
 }
 
 /* static */
 nsRect ShapeUtils::ComputeInsetRect(const StyleBasicShape& aBasicShape,
                                     const nsRect& aRefBox) {
-  MOZ_ASSERT(aBasicShape.GetShapeType() == StyleBasicShapeType::Inset,
-             "The basic shape must be inset()!");
-
-  const nsTArray<nsStyleCoord>& coords = aBasicShape.Coordinates();
-  MOZ_ASSERT(coords.Length() == 4, "wrong number of arguments");
-
-  nsMargin inset(coords[0].ComputeCoordPercentCalc(aRefBox.Height()),
-                 coords[1].ComputeCoordPercentCalc(aRefBox.Width()),
-                 coords[2].ComputeCoordPercentCalc(aRefBox.Height()),
-                 coords[3].ComputeCoordPercentCalc(aRefBox.Width()));
+  MOZ_ASSERT(aBasicShape.IsInset(), "The basic shape must be inset()!");
+  const auto& rect = aBasicShape.AsInset().rect;
+  nsMargin inset(
+      rect._0.Resolve(aRefBox.Height()), rect._1.Resolve(aRefBox.Width()),
+      rect._2.Resolve(aRefBox.Height()), rect._3.Resolve(aRefBox.Width()));
 
   nscoord x = aRefBox.X() + inset.left;
   nscoord width = aRefBox.Width() - inset.LeftRight();
   nscoord y = aRefBox.Y() + inset.top;
   nscoord height = aRefBox.Height() - inset.TopBottom();
 
   // Invert left and right, if necessary.
   if (width < 0) {
@@ -137,34 +122,29 @@ nsRect ShapeUtils::ComputeInsetRect(cons
 
   return nsRect(x, y, width, height);
 }
 
 /* static */
 bool ShapeUtils::ComputeInsetRadii(const StyleBasicShape& aBasicShape,
                                    const nsRect& aInsetRect,
                                    const nsRect& aRefBox, nscoord aRadii[8]) {
-  const auto& radius = aBasicShape.GetRadius();
+  const auto& radius = aBasicShape.AsInset().round;
   return nsIFrame::ComputeBorderRadii(radius, aInsetRect.Size(), aRefBox.Size(),
                                       Sides(), aRadii);
 }
 
 /* static */
 nsTArray<nsPoint> ShapeUtils::ComputePolygonVertices(
     const StyleBasicShape& aBasicShape, const nsRect& aRefBox) {
-  MOZ_ASSERT(aBasicShape.GetShapeType() == StyleBasicShapeType::Polygon,
-             "The basic shape must be polygon()!");
-
-  const nsTArray<nsStyleCoord>& coords = aBasicShape.Coordinates();
-  MOZ_ASSERT(coords.Length() % 2 == 0 && coords.Length() >= 2,
-             "Wrong number of arguments!");
+  MOZ_ASSERT(aBasicShape.IsPolygon(), "The basic shape must be polygon()!");
 
-  nsTArray<nsPoint> vertices(coords.Length() / 2);
-  for (size_t i = 0; i + 1 < coords.Length(); i += 2) {
-    vertices.AppendElement(
-        nsPoint(coords[i].ComputeCoordPercentCalc(aRefBox.width),
-                coords[i + 1].ComputeCoordPercentCalc(aRefBox.height)) +
-        aRefBox.TopLeft());
+  auto coords = aBasicShape.AsPolygon().coordinates.AsSpan();
+  nsTArray<nsPoint> vertices(coords.Length());
+  for (const StylePolygonCoord<LengthPercentage>& point : coords) {
+    vertices.AppendElement(nsPoint(point._0.Resolve(aRefBox.width),
+                                   point._1.Resolve(aRefBox.height)) +
+                           aRefBox.TopLeft());
   }
   return vertices;
 }
 
 }  // namespace mozilla
--- a/layout/base/ShapeUtils.h
+++ b/layout/base/ShapeUtils.h
@@ -5,35 +5,34 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ShapeUtils_h
 #define mozilla_ShapeUtils_h
 
 #include "nsCoord.h"
 #include "nsSize.h"
 #include "nsStyleConsts.h"
+#include "nsStyleCoord.h"
 #include "nsTArray.h"
 
 struct nsPoint;
 struct nsRect;
 
 namespace mozilla {
-class StyleBasicShape;
-
 // ShapeUtils is a namespace class containing utility functions related to
 // processing basic shapes in the CSS Shapes Module.
 // https://drafts.csswg.org/css-shapes/#basic-shape-functions
 //
 struct ShapeUtils final {
   // Compute the length of a keyword <shape-radius>, i.e. closest-side or
   // farthest-side, for a circle or an ellipse on a single dimension. The
   // caller needs to call for both dimensions and combine the result.
   // https://drafts.csswg.org/css-shapes/#typedef-shape-radius.
   // @return The length of the radius in app units.
-  static nscoord ComputeShapeRadius(const StyleShapeRadius aType,
+  static nscoord ComputeShapeRadius(const StyleShapeRadius& aType,
                                     const nscoord aCenter,
                                     const nscoord aPosMin,
                                     const nscoord aPosMax);
 
   // Compute the center of a circle or an ellipse.
   // @param aRefBox The reference box of the basic shape.
   // @return The point of the center.
   static nsPoint ComputeCircleOrEllipseCenter(const StyleBasicShape&,
--- a/layout/generic/BlockReflowInput.cpp
+++ b/layout/generic/BlockReflowInput.cpp
@@ -92,17 +92,25 @@ BlockReflowInput::BlockReflowInput(const
   }
   if ((aBEndMarginRoot && !logicalSkipSides.BEnd()) ||
       0 != mBorderPadding.BEnd(wm)) {
     mFlags.mIsBEndMarginRoot = true;
   }
   if (aBlockNeedsFloatManager) {
     mFlags.mBlockNeedsFloatManager = true;
   }
-  mFlags.mCanHaveTextOverflow = css::TextOverflow::CanHaveTextOverflow(mBlock);
+
+  // We need to check mInsideLineClamp here since we are here before the block
+  // has been reflowed, and CanHaveOverflowMarkers() relies on the block's
+  // NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS state bit to know if a -webkit-line-clamp
+  // ellipsis is set on one of the block's lines.  And that state bit is only
+  // set after we do the bsize measuring reflow of the flex item.
+  mFlags.mCanHaveOverflowMarkers =
+      aReflowInput.mFlags.mInsideLineClamp ||
+      css::TextOverflow::CanHaveOverflowMarkers(mBlock);
 
   MOZ_ASSERT(FloatManager(),
              "Float manager should be valid when creating BlockReflowInput!");
 
   // Save the coordinate system origin for later.
   FloatManager()->GetTranslation(mFloatManagerI, mFloatManagerB);
   FloatManager()->PushState(&mFloatManagerStateBefore);  // never popped
 
--- a/layout/generic/BlockReflowInput.h
+++ b/layout/generic/BlockReflowInput.h
@@ -35,17 +35,17 @@ class BlockReflowInput {
           mShouldApplyBStartMargin(false),
           mIsFirstInflow(false),
           mHasLineAdjacentToTop(false),
           mBlockNeedsFloatManager(false),
           mIsLineLayoutEmpty(false),
           mIsOverflowContainer(false),
           mIsFloatListInBlockPropertyTable(false),
           mFloatFragmentsInsideColumnEnabled(false),
-          mCanHaveTextOverflow(false) {}
+          mCanHaveOverflowMarkers(false) {}
 
     // Set in the BlockReflowInput constructor when the frame being reflowed has
     // been given NS_UNCONSTRAINEDSIZE as its available BSize in the
     // ReflowInput. If set, NS_UNCONSTRAINEDSIZE is passed to nsLineLayout as
     // the available BSize.
     bool mHasUnconstrainedBSize : 1;
 
     // Set in the BlockReflowInput constructor when reflowing a "block margin
@@ -99,18 +99,18 @@ class BlockReflowInput {
     bool mIsOverflowContainer : 1;
 
     // Set when our mPushedFloats list is stored on the block's property table.
     bool mIsFloatListInBlockPropertyTable : 1;
 
     // Set when the pref layout.float-fragments-inside-column.enabled is true.
     bool mFloatFragmentsInsideColumnEnabled : 1;
 
-    // Set when we need text-overflow processing.
-    bool mCanHaveTextOverflow : 1;
+    // Set when we need text-overflow or -webkit-line-clamp processing.
+    bool mCanHaveOverflowMarkers : 1;
   };
 
  public:
   BlockReflowInput(const ReflowInput& aReflowInput, nsPresContext* aPresContext,
                    nsBlockFrame* aFrame, bool aBStartMarginRoot,
                    bool aBEndMarginRoot, bool aBlockNeedsFloatManager,
                    nscoord aConsumedBSize = NS_INTRINSICSIZE);
 
--- a/layout/generic/ColumnSetWrapperFrame.cpp
+++ b/layout/generic/ColumnSetWrapperFrame.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 #include "ColumnSetWrapperFrame.h"
 
+#include "mozilla/ColumnUtils.h"
 #include "mozilla/PresShell.h"
 #include "nsContentUtils.h"
 #include "nsIFrame.h"
 #include "nsIFrameInlines.h"
 
 using namespace mozilla;
 
 nsBlockFrame* NS_NewColumnSetWrapperFrame(PresShell* aPresShell,
@@ -144,16 +145,84 @@ void ColumnSetWrapperFrame::MarkIntrinsi
 
   // The parent's method adds NS_BLOCK_NEEDS_BIDI_RESOLUTION to all our
   // continuations. Clear the bit because we don't want to call ResolveBidi().
   for (nsIFrame* f = FirstContinuation(); f; f = f->GetNextContinuation()) {
     f->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
   }
 }
 
+nscoord ColumnSetWrapperFrame::GetMinISize(gfxContext* aRenderingContext) {
+  nscoord iSize = 0;
+  DISPLAY_MIN_INLINE_SIZE(this, iSize);
+
+  if (StyleDisplay()->IsContainSize()) {
+    // If we're size-contained, we determine our minimum intrinsic size purely
+    // from our column styling, as if we had no descendants. This should match
+    // what happens in nsColumnSetFrame::GetMinISize in an actual no-descendants
+    // scenario.
+    const nsStyleColumn* colStyle = StyleColumn();
+    if (colStyle->mColumnWidth.IsLength()) {
+      // As available inline size reduces to zero, our number of columns reduces
+      // to one, so no column gaps contribute to our minimum intrinsic size.
+      // Also, column-width doesn't set a lower bound on our minimum intrinsic
+      // size, either. Just use 0 because we're size-contained.
+      iSize = 0;
+    } else {
+      MOZ_ASSERT(colStyle->mColumnCount != nsStyleColumn::kColumnCountAuto,
+                 "column-count and column-width can't both be auto!");
+      // As available inline size reduces to zero, we still have mColumnCount
+      // columns, so compute our minimum intrinsic size based on N zero-width
+      // columns, with specified gap size between them.
+      const nscoord colGap =
+          ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
+      iSize = ColumnUtils::IntrinsicISize(colStyle->mColumnCount, colGap, 0);
+    }
+  } else {
+    for (nsIFrame* f : PrincipalChildList()) {
+      iSize = std::max(iSize, f->GetMinISize(aRenderingContext));
+    }
+  }
+
+  return iSize;
+}
+
+nscoord ColumnSetWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) {
+  nscoord iSize = 0;
+  DISPLAY_PREF_INLINE_SIZE(this, iSize);
+
+  if (StyleDisplay()->IsContainSize()) {
+    const nsStyleColumn* colStyle = StyleColumn();
+    nscoord colISize;
+    if (colStyle->mColumnWidth.IsLength()) {
+      colISize =
+          ColumnUtils::ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength());
+    } else {
+      MOZ_ASSERT(colStyle->mColumnCount != nsStyleColumn::kColumnCountAuto,
+                 "column-count and column-width can't both be auto!");
+      colISize = 0;
+    }
+
+    // If column-count is auto, assume one column.
+    const uint32_t numColumns =
+        colStyle->mColumnCount == nsStyleColumn::kColumnCountAuto
+            ? 1
+            : colStyle->mColumnCount;
+    const nscoord colGap =
+        ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
+    iSize = ColumnUtils::IntrinsicISize(numColumns, colGap, colISize);
+  } else {
+    for (nsIFrame* f : PrincipalChildList()) {
+      iSize = std::max(iSize, f->GetPrefISize(aRenderingContext));
+    }
+  }
+
+  return iSize;
+}
+
 #ifdef DEBUG
 
 /* static */
 void ColumnSetWrapperFrame::AssertColumnSpanWrapperSubtreeIsSane(
     const nsIFrame* aFrame) {
   MOZ_ASSERT(aFrame->IsColumnSpan(), "aFrame is not column-span?");
 
   if (!nsLayoutUtils::GetStyleFrame(const_cast<nsIFrame*>(aFrame))
--- a/layout/generic/ColumnSetWrapperFrame.h
+++ b/layout/generic/ColumnSetWrapperFrame.h
@@ -49,16 +49,20 @@ class ColumnSetWrapperFrame final : publ
 
   void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
                     nsFrameList& aFrameList) override;
 
   void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override;
 
   void MarkIntrinsicISizesDirty() override;
 
+  nscoord GetMinISize(gfxContext* aRenderingContext) override;
+
+  nscoord GetPrefISize(gfxContext* aRenderingContext) override;
+
  private:
   explicit ColumnSetWrapperFrame(ComputedStyle* aStyle,
                                  nsPresContext* aPresContext);
   ~ColumnSetWrapperFrame() override = default;
 
 #ifdef DEBUG
   static void AssertColumnSpanWrapperSubtreeIsSane(const nsIFrame* aFrame);
 
new file mode 100644
--- /dev/null
+++ b/layout/generic/ColumnUtils.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* A namespace class for static muti-column utilities. */
+
+#include "mozilla/ColumnUtils.h"
+
+#include <algorithm>
+
+#include "nsContainerFrame.h"
+#include "nsLayoutUtils.h"
+
+namespace mozilla {
+
+/* static */
+nscoord ColumnUtils::GetColumnGap(const nsContainerFrame* aFrame,
+                                  nscoord aPercentageBasis) {
+  const auto& columnGap = aFrame->StylePosition()->mColumnGap;
+  if (columnGap.IsNormal()) {
+    return aFrame->StyleFont()->mFont.size;
+  }
+  return nsLayoutUtils::ResolveGapToLength(columnGap, aPercentageBasis);
+}
+
+/* static */
+nscoord ColumnUtils::ClampUsedColumnWidth(const Length& aColumnWidth) {
+  // Per spec, used values will be clamped to a minimum of 1px.
+  return std::max(CSSPixel::ToAppUnits(1), aColumnWidth.ToAppUnits());
+}
+
+/* static */
+nscoord ColumnUtils::IntrinsicISize(uint32_t aColCount, nscoord aColGap,
+                                    nscoord aColISize) {
+  MOZ_ASSERT(aColCount > 0, "Cannot compute with zero columns!");
+
+  // Column box's inline-size times number of columns (n), plus n-1 column gaps.
+  nscoord iSize = aColISize * aColCount + aColGap * (aColCount - 1);
+
+  // The multiplication above can make 'iSize' negative (integer overflow),
+  // so use std::max to protect against that.
+  return std::max(iSize, aColISize);
+}
+
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/generic/ColumnUtils.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* A namespace class for static muti-column utilities. */
+
+#ifndef mozilla_ColumnUtils_h
+#define mozilla_ColumnUtils_h
+
+#include "nsStyleCoord.h"
+
+class nsContainerFrame;
+
+namespace mozilla {
+
+// ColumnUtils is a namespace class containing utility functions used by
+// multi-column containers like ColumnSetWrapperFrame and nsColumnSetFrame.
+//
+class ColumnUtils final {
+ public:
+  // Compute used value of 'column-gap' for aFrame.
+  static nscoord GetColumnGap(const nsContainerFrame* aFrame,
+                              nscoord aPercentageBasis);
+
+  // Clamp used column width to a minimum of 1px.
+  static nscoord ClampUsedColumnWidth(const Length& aColumnWidth);
+
+  // Compute the intrinsic inline-size of a column container, given a non-zero
+  // column-count, column gap, and column box's inline-size.
+  static nscoord IntrinsicISize(uint32_t aColCount, nscoord aColGap,
+                                nscoord aColISize);
+};
+
+}  // namespace mozilla
+
+#endif  // mozilla_ColumnUtils_h
--- a/layout/generic/ReflowInput.cpp
+++ b/layout/generic/ReflowInput.cpp
@@ -210,16 +210,17 @@ ReflowInput::ReflowInput(nsPresContext* 
   mFlags.mDummyParentReflowInput = false;
   mFlags.mShrinkWrap = !!(aFlags & COMPUTE_SIZE_SHRINK_WRAP);
   mFlags.mUseAutoBSize = !!(aFlags & COMPUTE_SIZE_USE_AUTO_BSIZE);
   mFlags.mStaticPosIsCBOrigin = !!(aFlags & STATIC_POS_IS_CB_ORIGIN);
   mFlags.mIOffsetsNeedCSSAlign = mFlags.mBOffsetsNeedCSSAlign = false;
   mFlags.mIClampMarginBoxMinSize = !!(aFlags & I_CLAMP_MARGIN_BOX_MIN_SIZE);
   mFlags.mBClampMarginBoxMinSize = !!(aFlags & B_CLAMP_MARGIN_BOX_MIN_SIZE);
   mFlags.mApplyAutoMinSize = !!(aFlags & I_APPLY_AUTO_MIN_SIZE);
+  mFlags.mApplyLineClamp = false;
 
   if ((aFlags & DUMMY_PARENT_REFLOW_INPUT) ||
       (mParentReflowInput->mFlags.mDummyParentReflowInput &&
        mFrame->IsTableFrame())) {
     mFlags.mDummyParentReflowInput = true;
   }
 
   if (!(aFlags & CALLER_WILL_INIT)) {
--- a/layout/generic/ReflowInput.h
+++ b/layout/generic/ReflowInput.h
@@ -265,16 +265,30 @@ struct SizeComputationInput {
     // area, in that axis -- and these offsets need to be further-resolved
     // (with CSS Box Alignment) after we know the OOF frame's size.
     // NOTE: The "I" and "B" (for "Inline" and "Block") refer the axes of the
     // *containing block's writing-mode*, NOT mFrame's own writing-mode. This
     // is purely for convenience, since that's the writing-mode we're dealing
     // with when we set & react to these bits.
     bool mIOffsetsNeedCSSAlign : 1;
     bool mBOffsetsNeedCSSAlign : 1;
+
+    // Are we somewhere inside an element with -webkit-line-clamp set?
+    // This flag is inherited into descendant ReflowInputs, but we don't bother
+    // resetting it to false when crossing over into a block descendant that
+    // -webkit-line-clamp skips over (such as a BFC).
+    bool mInsideLineClamp : 1;
+
+    // Is this a flex item, and should we add or remove a -webkit-line-clamp
+    // ellipsis on a descendant line?  It's possible for this flag to be true
+    // when mInsideLineClamp is false if we previously had a numeric
+    // -webkit-line-clamp value, but now have 'none' and we need to find the
+    // line with the ellipsis flag and clear it.
+    // This flag is not inherited into descendant ReflowInputs.
+    bool mApplyLineClamp : 1;
   };
 
 #ifdef DEBUG
   // Reflow trace methods.  Defined in nsFrame.cpp so they have access
   // to the display-reflow infrastructure.
   static void* DisplayInitOffsetsEnter(nsIFrame* aFrame,
                                        SizeComputationInput* aState,
                                        nscoord aPercentBasis,
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -147,21 +147,21 @@ static bool IsFrameDescendantOfAny(
   }
   return false;
 }
 
 class nsDisplayTextOverflowMarker final : public nsPaintedDisplayItem {
  public:
   nsDisplayTextOverflowMarker(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                               const nsRect& aRect, nscoord aAscent,
-                              const nsStyleTextOverflowSide* aStyle,
+                              nsStyleTextOverflowSide aStyle,
                               uint32_t aLineNumber, uint16_t aIndex)
       : nsPaintedDisplayItem(aBuilder, aFrame),
         mRect(aRect),
-        mStyle(*aStyle),
+        mStyle(aStyle),
         mAscent(aAscent),
         mIndex((aLineNumber << 1) + aIndex) {
     MOZ_COUNT_CTOR(nsDisplayTextOverflowMarker);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayTextOverflowMarker() {
     MOZ_COUNT_DTOR(nsDisplayTextOverflowMarker);
   }
@@ -339,19 +339,20 @@ TextOverflow::TextOverflow(nsDisplayList
   }
   // The left/right marker string is setup in ExamineLineFrames when a line
   // has overflow on that side.
 }
 
 /* static */
 Maybe<TextOverflow> TextOverflow::WillProcessLines(
     nsDisplayListBuilder* aBuilder, nsIFrame* aBlockFrame) {
-  // Ignore 'text-overflow' for event and frame visibility processing.
+  // Ignore text-overflow and -webkit-line-clamp for event and frame visibility
+  // processing.
   if (aBuilder->IsForEventDelivery() || aBuilder->IsForFrameVisibility() ||
-      !CanHaveTextOverflow(aBlockFrame)) {
+      !CanHaveOverflowMarkers(aBlockFrame)) {
     return Nothing();
   }
   nsIScrollableFrame* scrollableFrame =
       nsLayoutUtils::GetScrollableFrameFor(aBlockFrame);
   if (scrollableFrame && scrollableFrame->IsTransformingByAPZ()) {
     // If the APZ is actively scrolling this, don't bother with markers.
     return Nothing();
   }
@@ -456,60 +457,66 @@ void TextOverflow::AnalyzeMarkerEdges(ns
           LogicalRect snappedRect = borderRect;
           if (istartOverlap > 0) {
             snappedRect.IStart(mBlockWM) += snappedIStart;
             snappedRect.ISize(mBlockWM) -= snappedIStart;
           }
           if (iendOverlap > 0) {
             snappedRect.ISize(mBlockWM) -= snappedIEnd;
           }
-          aAlignmentEdges->Accumulate(mBlockWM, snappedRect);
+          aAlignmentEdges->AccumulateInner(mBlockWM, snappedRect);
           *aFoundVisibleTextOrAtomic = true;
         }
       }
     } else {
       aFramesToHide->PutEntry(aFrame);
     }
   } else if (!insideIStartEdge || !insideIEndEdge) {
     // frame is outside
+    if (!insideIStartEdge) {
+      aAlignmentEdges->AccumulateOuter(mBlockWM, borderRect);
+    }
     if (IsAtomicElement(aFrame, aFrameType)) {
       aFramesToHide->PutEntry(aFrame);
     }
   } else {
     // frame is inside
-    aAlignmentEdges->Accumulate(mBlockWM, borderRect);
+    aAlignmentEdges->AccumulateInner(mBlockWM, borderRect);
     if (aFrameType == LayoutFrameType::Text) {
       auto textFrame = static_cast<nsTextFrame*>(aFrame);
       if (textFrame->HasNonSuppressedText()) {
         *aFoundVisibleTextOrAtomic = true;
       }
     } else {
       *aFoundVisibleTextOrAtomic = true;
     }
   }
 }
 
 LogicalRect TextOverflow::ExamineLineFrames(nsLineBox* aLine,
                                             FrameHashtable* aFramesToHide,
                                             AlignmentEdges* aAlignmentEdges) {
   // No ellipsing for 'clip' style.
-  bool suppressIStart = mIStart.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
-  bool suppressIEnd = mIEnd.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
+  bool suppressIStart = mIStart.IsSuppressed();
+  bool suppressIEnd = mIEnd.IsSuppressed();
   if (mCanHaveInlineAxisScrollbar) {
     LogicalPoint pos(mBlockWM, mScrollableFrame->GetScrollPosition(),
                      mBlockSize);
     LogicalRect scrollRange(mBlockWM, mScrollableFrame->GetScrollRange(),
                             mBlockSize);
     // No ellipsing when nothing to scroll to on that side (this includes
     // overflow:auto that doesn't trigger a horizontal scrollbar).
     if (pos.I(mBlockWM) <= scrollRange.IStart(mBlockWM)) {
       suppressIStart = true;
     }
     if (pos.I(mBlockWM) >= scrollRange.IEnd(mBlockWM)) {
-      suppressIEnd = true;
+      // Except that we always want to display a -webkit-line-clamp ellipsis.
+      if (!mIEnd.mHasBlockEllipsis) {
+        suppressIEnd = true;
+      }
     }
   }
 
   LogicalRect contentArea = mContentArea;
   bool snapStart = true, snapEnd = true;
   nscoord startEdge, endEdge;
   if (aLine->GetFloatEdges(&startEdge, &endEdge)) {
     // Narrow the |contentArea| to account for any floats on this line, and
@@ -538,32 +545,39 @@ LogicalRect TextOverflow::ExamineLineFra
     }
     if (snapEnd) {
       InflateIEnd(mBlockWM, &contentArea, scrollAdjust);
     }
   }
 
   LogicalRect lineRect(mBlockWM, aLine->GetScrollableOverflowArea(),
                        mBlockSize);
-  const bool istartOverflow =
+  const bool istartWantsMarker =
       !suppressIStart &&
       lineRect.IStart(mBlockWM) < contentArea.IStart(mBlockWM);
-  const bool iendOverflow =
+  const bool iendWantsTextOverflowMarker =
       !suppressIEnd && lineRect.IEnd(mBlockWM) > contentArea.IEnd(mBlockWM);
-  if (!istartOverflow && !iendOverflow) {
-    // The line does not overflow on a side we should ellipsize.
+  const bool iendWantsBlockEllipsisMarker =
+      !suppressIEnd && mIEnd.mHasBlockEllipsis;
+  const bool iendWantsMarker =
+      iendWantsTextOverflowMarker || iendWantsBlockEllipsisMarker;
+  if (!istartWantsMarker && !iendWantsMarker) {
+    // We don't need any markers on this line.
     return nonSnappedContentArea;
   }
 
   int pass = 0;
   bool retryEmptyLine = true;
-  bool guessIStart = istartOverflow;
-  bool guessIEnd = iendOverflow;
-  mIStart.mActive = istartOverflow;
-  mIEnd.mActive = iendOverflow;
+  bool guessIStart = istartWantsMarker;
+  bool guessIEnd = iendWantsMarker;
+  mIStart.mActive = istartWantsMarker;
+  mIEnd.mActive = iendWantsMarker;
+  mIStart.mEdgeAligned = mCanHaveInlineAxisScrollbar && istartWantsMarker;
+  mIEnd.mEdgeAligned =
+      mCanHaveInlineAxisScrollbar && iendWantsTextOverflowMarker;
   bool clippedIStartMarker = false;
   bool clippedIEndMarker = false;
   do {
     // Setup marker strings as needed.
     if (guessIStart) {
       mIStart.SetupString(mBlock);
     }
     if (guessIEnd) {
@@ -596,17 +610,18 @@ LogicalRect TextOverflow::ExamineLineFra
     nsIFrame* child = aLine->mFirstChild;
     InnerClipEdges clippedMarkerEdges;
     for (; n-- > 0; child = child->GetNextSibling()) {
       ExamineFrameSubtree(child, contentArea, insideMarkersArea, aFramesToHide,
                           aAlignmentEdges, &foundVisibleTextOrAtomic,
                           &clippedMarkerEdges);
     }
     if (!foundVisibleTextOrAtomic && retryEmptyLine) {
-      aAlignmentEdges->mAssigned = false;
+      aAlignmentEdges->mAssignedInner = false;
+      aAlignmentEdges->mIEndOuter = 0;
       aFramesToHide->Clear();
       pass = -1;
       if (mIStart.IsNeeded() && mIStart.mActive && !clippedIStartMarker) {
         if (clippedMarkerEdges.mAssignedIStart &&
             clippedMarkerEdges.mIStart >
                 nonSnappedContentArea.IStart(mBlockWM)) {
           mIStart.mISize = clippedMarkerEdges.mIStart -
                            nonSnappedContentArea.IStart(mBlockWM);
@@ -630,85 +645,122 @@ LogicalRect TextOverflow::ExamineLineFra
           mIEnd.mActive = guessIEnd = false;
         }
         continue;
       }
       // The line simply has no visible content even without markers,
       // so examine the line again without suppressing markers.
       retryEmptyLine = false;
       mIStart.mISize = mIStart.mIntrinsicISize;
-      mIStart.mActive = guessIStart = istartOverflow;
+      mIStart.mActive = guessIStart = istartWantsMarker;
       mIEnd.mISize = mIEnd.mIntrinsicISize;
-      mIEnd.mActive = guessIEnd = iendOverflow;
+      mIEnd.mActive = guessIEnd = iendWantsMarker;
+      // If we wanted to place a block ellipsis but didn't, due to not having
+      // any visible content to align to or the line's content being scrolled
+      // out of view, then clip the ellipsis so that it looks like it is aligned
+      // with the out of view content.
+      if (mIEnd.IsNeeded() && mIEnd.mActive && mIEnd.mHasBlockEllipsis) {
+        NS_ASSERTION(nonSnappedContentArea.IStart(mBlockWM) >
+                         aAlignmentEdges->mIEndOuter,
+                     "Expected the alignment edge for the out of view content "
+                     "to be before the start of the content area");
+        mIEnd.mISize = std::max(
+            mIEnd.mIntrinsicISize - (nonSnappedContentArea.IStart(mBlockWM) -
+                                     aAlignmentEdges->mIEndOuter),
+            0);
+      }
       continue;
     }
     if (guessIStart == (mIStart.mActive && mIStart.IsNeeded()) &&
         guessIEnd == (mIEnd.mActive && mIEnd.IsNeeded())) {
       break;
     } else {
       guessIStart = mIStart.mActive && mIStart.IsNeeded();
       guessIEnd = mIEnd.mActive && mIEnd.IsNeeded();
       mIStart.Reset();
       mIEnd.Reset();
       aFramesToHide->Clear();
     }
     NS_ASSERTION(pass == 0, "2nd pass should never guess wrong");
   } while (++pass != 2);
-  if (!istartOverflow || !mIStart.mActive) {
+  if (!istartWantsMarker || !mIStart.mActive) {
     mIStart.Reset();
   }
-  if (!iendOverflow || !mIEnd.mActive) {
+  if (!iendWantsMarker || !mIEnd.mActive) {
     mIEnd.Reset();
   }
   return nonSnappedContentArea;
 }
 
 void TextOverflow::ProcessLine(const nsDisplayListSet& aLists, nsLineBox* aLine,
                                uint32_t aLineNumber) {
-  NS_ASSERTION(mIStart.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP ||
-                   mIEnd.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP,
-               "TextOverflow with 'clip' for both sides");
+  if (mIStart.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP &&
+      mIEnd.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP &&
+      !aLine->HasLineClampEllipsis()) {
+    return;
+  }
+
   mIStart.Reset();
   mIStart.mActive = mIStart.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP;
   mIEnd.Reset();
-  mIEnd.mActive = mIEnd.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP;
+  mIEnd.mHasBlockEllipsis = aLine->HasLineClampEllipsis();
+  mIEnd.mActive = mIEnd.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP ||
+                  aLine->HasLineClampEllipsis();
 
   FrameHashtable framesToHide(64);
   AlignmentEdges alignmentEdges;
   const LogicalRect contentArea =
       ExamineLineFrames(aLine, &framesToHide, &alignmentEdges);
   bool needIStart = mIStart.IsNeeded();
   bool needIEnd = mIEnd.IsNeeded();
   if (!needIStart && !needIEnd) {
     return;
   }
-  NS_ASSERTION(
-      mIStart.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP || !needIStart,
-      "left marker for 'clip'");
-  NS_ASSERTION(mIEnd.mStyle->mType != NS_STYLE_TEXT_OVERFLOW_CLIP || !needIEnd,
-               "right marker for 'clip'");
+  NS_ASSERTION(!mIStart.IsSuppressed() || !needIStart,
+               "left marker when not needed");
+  NS_ASSERTION(!mIEnd.IsSuppressed() || !needIEnd,
+               "right marker when not needed");
 
   // If there is insufficient space for both markers then keep the one on the
   // end side per the block's 'direction'.
   if (needIStart && needIEnd &&
       mIStart.mISize + mIEnd.mISize > contentArea.ISize(mBlockWM)) {
     needIStart = false;
   }
   LogicalRect insideMarkersArea = contentArea;
   if (needIStart) {
     InflateIStart(mBlockWM, &insideMarkersArea, -mIStart.mISize);
   }
   if (needIEnd) {
     InflateIEnd(mBlockWM, &insideMarkersArea, -mIEnd.mISize);
   }
-  if (!mCanHaveInlineAxisScrollbar && alignmentEdges.mAssigned) {
+
+  if (alignmentEdges.mAssignedInner) {
+    if (mIStart.mEdgeAligned) {
+      alignmentEdges.mIStart = insideMarkersArea.IStart(mBlockWM);
+    }
+    if (mIEnd.mEdgeAligned) {
+      alignmentEdges.mIEnd = insideMarkersArea.IEnd(mBlockWM);
+    }
     LogicalRect alignmentRect(mBlockWM, alignmentEdges.mIStart,
                               insideMarkersArea.BStart(mBlockWM),
                               alignmentEdges.ISize(), 1);
     insideMarkersArea.IntersectRect(insideMarkersArea, alignmentRect);
+  } else {
+    // There was no content on the line that was visible at the current scolled
+    // position.  If we wanted to place a block ellipsis but failed due to
+    // having no visible content to align it to, we still need to ensure it
+    // is displayed.  It goes at the start of the line, even though it's an
+    // IEnd marker, since that is the side of the line that the content has
+    // been scrolled past.  We set the insideMarkersArea to a zero-sized
+    // rectangle placed next to the scrolled-out-of-view content.
+    if (mIEnd.mHasBlockEllipsis) {
+      insideMarkersArea = LogicalRect(mBlockWM, alignmentEdges.mIEndOuter,
+                                      insideMarkersArea.BStart(mBlockWM), 0, 1);
+    }
   }
 
   // Clip and remove display items as needed at the final marker edges.
   nsDisplayList* lists[] = {aLists.Content(), aLists.PositionedDescendants()};
   for (uint32_t i = 0; i < ArrayLength(lists); ++i) {
     PruneDisplayListContents(lists[i], framesToHide, insideMarkersArea);
   }
   CreateMarkers(aLine, needIStart, needIEnd, insideMarkersArea, contentArea,
@@ -757,26 +809,38 @@ void TextOverflow::PruneDisplayListConte
     }
 
     saved.AppendToTop(item);
   }
   aList->AppendToTop(&saved);
 }
 
 /* static */
-bool TextOverflow::HasClippedOverflow(nsIFrame* aBlockFrame) {
+bool TextOverflow::HasClippedTextOverflow(nsIFrame* aBlockFrame) {
   const nsStyleTextReset* style = aBlockFrame->StyleTextReset();
   return style->mTextOverflow.mLeft.mType == NS_STYLE_TEXT_OVERFLOW_CLIP &&
          style->mTextOverflow.mRight.mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
 }
 
 /* static */
-bool TextOverflow::CanHaveTextOverflow(nsIFrame* aBlockFrame) {
+bool TextOverflow::HasBlockEllipsis(nsIFrame* aBlockFrame) {
+  nsBlockFrame* f = do_QueryFrame(aBlockFrame);
+  return f && f->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS);
+}
+
+/* static */
+bool TextOverflow::CanHaveOverflowMarkers(nsIFrame* aBlockFrame) {
+  // Treat a line with a -webkit-line-clamp ellipsis as a kind of text
+  // overflow.
+  if (aBlockFrame->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS)) {
+    return true;
+  }
+
   // Nothing to do for text-overflow:clip or if 'overflow-x/y:visible'.
-  if (HasClippedOverflow(aBlockFrame) ||
+  if (HasClippedTextOverflow(aBlockFrame) ||
       IsInlineAxisOverflowVisible(aBlockFrame)) {
     return false;
   }
 
   // Skip ComboboxControlFrame because it would clip the drop-down arrow.
   // Its anon block inherits 'text-overflow' and does what is expected.
   if (aBlockFrame->IsComboboxControlFrame()) {
     return false;
@@ -810,43 +874,51 @@ void TextOverflow::CreateMarkers(const n
         mBlockWM, aInsideMarkersArea.IStart(mBlockWM) - mIStart.mIntrinsicISize,
         aLine->BStart(), mIStart.mIntrinsicISize, aLine->BSize());
     nsPoint offset = mBuilder->ToReferenceFrame(mBlock);
     nsRect markerRect =
         markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockSize) + offset;
     ClipMarker(aContentArea.GetPhysicalRect(mBlockWM, mBlockSize) + offset,
                markerRect, clipState);
     mMarkerList.AppendNewToTop<nsDisplayTextOverflowMarker>(
-        mBuilder, mBlock, markerRect, aLine->GetLogicalAscent(), mIStart.mStyle,
-        aLineNumber, 0);
+        mBuilder, mBlock, markerRect, aLine->GetLogicalAscent(),
+        *mIStart.mStyle, aLineNumber, 0);
   }
 
   if (aCreateIEnd) {
     DisplayListClipState::AutoSaveRestore clipState(mBuilder);
 
     LogicalRect markerLogicalRect(mBlockWM, aInsideMarkersArea.IEnd(mBlockWM),
                                   aLine->BStart(), mIEnd.mIntrinsicISize,
                                   aLine->BSize());
     nsPoint offset = mBuilder->ToReferenceFrame(mBlock);
     nsRect markerRect =
         markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockSize) + offset;
     ClipMarker(aContentArea.GetPhysicalRect(mBlockWM, mBlockSize) + offset,
                markerRect, clipState);
     mMarkerList.AppendNewToTop<nsDisplayTextOverflowMarker>(
-        mBuilder, mBlock, markerRect, aLine->GetLogicalAscent(), mIEnd.mStyle,
+        mBuilder, mBlock, markerRect, aLine->GetLogicalAscent(),
+        mIEnd.mHasBlockEllipsis ? nsStyleTextOverflowSide::Ellipsis()
+                                : *mIEnd.mStyle,
         aLineNumber, 1);
   }
 }
 
 void TextOverflow::Marker::SetupString(nsIFrame* aFrame) {
   if (mInitialized) {
     return;
   }
 
-  if (mStyle->mType == NS_STYLE_TEXT_OVERFLOW_ELLIPSIS) {
+  // A limitation here is that at the IEnd of a line, we only ever render one of
+  // a text-overflow marker and a -webkit-line-clamp block ellipsis.  Since we
+  // don't track the block ellipsis string and the text-overflow marker string
+  // separately, if both apply to the element, we will always use "…" as the
+  // string for text-overflow.
+  if (HasBlockEllipsis(aFrame) ||
+      mStyle->mType == NS_STYLE_TEXT_OVERFLOW_ELLIPSIS) {
     gfxTextRun* textRun = GetEllipsisTextRun(aFrame);
     if (textRun) {
       mISize = textRun->GetAdvanceWidth();
     } else {
       mISize = 0;
     }
   } else {
     RefPtr<gfxContext> rc =
--- a/layout/generic/TextOverflow.h
+++ b/layout/generic/TextOverflow.h
@@ -59,47 +59,61 @@ class TextOverflow final {
                    uint32_t aLineNumber);
 
   /**
    * Get the resulting text-overflow markers (the list may be empty).
    * @return a DisplayList containing any text-overflow markers.
    */
   nsDisplayList& GetMarkers() { return mMarkerList; }
 
-  /**
-   * @return true if aBlockFrmae has text-overflow:clip on both sides.
-   */
-  static bool HasClippedOverflow(nsIFrame* aBlockFrame);
-  /**
-   * @return true if aBlockFrame needs analysis for text overflow.
-   */
-  static bool CanHaveTextOverflow(nsIFrame* aBlockFrame);
+  // Returns whether aBlockFrame has text-overflow:clip on both sides.
+  static bool HasClippedTextOverflow(nsIFrame* aBlockFrame);
+
+  // Returns whether aBlockFrame has a block ellipsis on one of its lines.
+  static bool HasBlockEllipsis(nsIFrame* aBlockFrame);
+
+  // Returns whether aBlockFrame needs analysis for text overflow.
+  static bool CanHaveOverflowMarkers(nsIFrame* aBlockFrame);
 
   typedef nsTHashtable<nsPtrHashKey<nsIFrame>> FrameHashtable;
 
  private:
   typedef mozilla::WritingMode WritingMode;
   typedef mozilla::LogicalRect LogicalRect;
 
+  // Edges to align the IStart and IEnd markers to.
   struct AlignmentEdges {
-    AlignmentEdges() : mIStart(0), mIEnd(0), mAssigned(false) {}
-    void Accumulate(WritingMode aWM, const LogicalRect& aRect) {
-      if (MOZ_LIKELY(mAssigned)) {
+    AlignmentEdges()
+        : mIStart(0), mIEnd(0), mIEndOuter(0), mAssignedInner(false) {}
+    void AccumulateInner(WritingMode aWM, const LogicalRect& aRect) {
+      if (MOZ_LIKELY(mAssignedInner)) {
         mIStart = std::min(mIStart, aRect.IStart(aWM));
         mIEnd = std::max(mIEnd, aRect.IEnd(aWM));
       } else {
         mIStart = aRect.IStart(aWM);
         mIEnd = aRect.IEnd(aWM);
-        mAssigned = true;
+        mAssignedInner = true;
       }
     }
+    void AccumulateOuter(WritingMode aWM, const LogicalRect& aRect) {
+      mIEndOuter = std::max(mIEndOuter, aRect.IEnd(aWM));
+    }
     nscoord ISize() { return mIEnd - mIStart; }
+
+    // The outermost edges of all text and atomic inline-level frames that are
+    // inside the area between the markers.
     nscoord mIStart;
     nscoord mIEnd;
-    bool mAssigned;
+
+    // The closest IEnd edge of all text and atomic inline-level frames that
+    // fall completely before the IStart edge of the content area.  (Used to
+    // align a block ellipsis when there are no visible frames to align to.)
+    nscoord mIEndOuter;
+
+    bool mAssignedInner;
   };
 
   struct InnerClipEdges {
     InnerClipEdges()
         : mIStart(0), mIEnd(0), mAssignedIStart(false), mAssignedIEnd(false) {}
     void AccumulateIStart(WritingMode aWM, const LogicalRect& aRect) {
       if (MOZ_LIKELY(mAssignedIStart)) {
         mIStart = std::max(mIStart, aRect.IStart(aWM));
@@ -131,33 +145,37 @@ class TextOverflow final {
   }
 
   /**
    * Examines frames on the line to determine whether we should draw a left
    * and/or right marker, and if so, which frames should be completely hidden
    * and the bounds of what will be displayed between the markers.
    * @param aLine the line we're processing
    * @param aFramesToHide frames that should have their display items removed
-   * @param aAlignmentEdges the outermost edges of all text and atomic
-   *   inline-level frames that are inside the area between the markers
+   * @param aAlignmentEdges edges the markers will be aligned to, including
+   *   the outermost edges of all text and atomic inline-level frames that
+   *   are inside the content area, and the closest IEnd edge of such a frame
+   *   outside the content area
    * @return the area inside which we should add any markers;
    *   this is the block's content area narrowed by any floats on this line.
    */
   LogicalRect ExamineLineFrames(nsLineBox* aLine, FrameHashtable* aFramesToHide,
                                 AlignmentEdges* aAlignmentEdges);
 
   /**
    * LineHasOverflowingText calls this to analyze edges, both the block's
    * content edges and the hypothetical marker edges aligned at the block edges.
    * @param aFrame the descendant frame of mBlock that we're analyzing
    * @param aContentArea the block's content area
    * @param aInsideMarkersArea the rectangle between the markers
    * @param aFramesToHide frames that should have their display items removed
-   * @param aAlignmentEdges the outermost edges of all text and atomic
-   *   inline-level frames that are inside the area between the markers
+   * @param aAlignmentEdges edges the markers will be aligned to, including
+   *   the outermost edges of all text and atomic inline-level frames that
+   *   are inside the content area, and the closest IEnd edge of such a frame
+   *   outside the content area
    * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic
    *   inline-level frame is visible between the marker edges
    * @param aClippedMarkerEdges the innermost edges of all text and atomic
    *   inline-level frames that are clipped by the current marker width
    */
   void ExamineFrameSubtree(nsIFrame* aFrame, const LogicalRect& aContentArea,
                            const LogicalRect& aInsideMarkersArea,
                            FrameHashtable* aFramesToHide,
@@ -174,16 +192,20 @@ class TextOverflow final {
    * aFramesToHide.
    * @param aFrame the descendant frame of mBlock that we're analyzing
    * @param aFrameType aFrame's frame type
    * @param aInsideMarkersArea the rectangle between the markers
    * @param aFramesToHide frames that should have their display items removed
    * @param aAlignmentEdges the outermost edges of all text and atomic
    *   inline-level frames that are inside the area between the markers
    *                       inside aInsideMarkersArea
+   * @param aAlignmentEdges edges the markers will be aligned to, including
+   *   the outermost edges of all text and atomic inline-level frames that
+   *   are inside aInsideMarkersArea, and the closest IEnd edge of such a frame
+   *   outside the content area
    * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic
    *   inline-level frame is visible between the marker edges
    * @param aClippedMarkerEdges the innermost edges of all text and atomic
    *   inline-level frames that are clipped by the current marker width
    */
   void AnalyzeMarkerEdges(nsIFrame* aFrame, mozilla::LayoutFrameType aFrameType,
                           const LogicalRect& aInsideMarkersArea,
                           FrameHashtable* aFramesToHide,
@@ -229,40 +251,55 @@ class TextOverflow final {
   class Marker {
    public:
     void Init(const nsStyleTextOverflowSide& aStyle) {
       mInitialized = false;
       mISize = 0;
       mStyle = &aStyle;
       mIntrinsicISize = 0;
       mHasOverflow = false;
+      mHasBlockEllipsis = false;
       mActive = false;
+      mEdgeAligned = false;
     }
 
     /**
      * Setup the marker string and calculate its size, if not done already.
      */
     void SetupString(nsIFrame* aFrame);
 
-    bool IsNeeded() const { return mHasOverflow; }
-    void Reset() { mHasOverflow = false; }
+    bool IsSuppressed() const {
+      return !mHasBlockEllipsis && mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
+    }
+    bool IsNeeded() const { return mHasOverflow || mHasBlockEllipsis; }
+    void Reset() {
+      mHasOverflow = false;
+      mHasBlockEllipsis = false;
+      mEdgeAligned = false;
+    }
 
     // The current width of the marker, the range is [0 .. mIntrinsicISize].
     nscoord mISize;
     // The intrinsic width of the marker.
     nscoord mIntrinsicISize;
-    // The style for this side.
+    // The text-overflow style for this side.  Ignored if we're rendering a
+    // block ellipsis.
     const nsStyleTextOverflowSide* mStyle;
     // True if there is visible overflowing inline content on this side.
     bool mHasOverflow;
-    // True if mMarkerString and mWidth have been setup from style.
+    // True if this side has a block ellipsis (from -webkit-line-clamp).
+    bool mHasBlockEllipsis;
+    // True if mISize and mIntrinsicISize have been setup from style.
     bool mInitialized;
-    // True if the style is text-overflow:clip on this side and the marker
+    // True if the style is not text-overflow:clip on this side and the marker
     // won't cause the line to become empty.
     bool mActive;
+    // True if this marker is aligned to the edge of the content box, so that
+    // when scrolling the marker doesn't jump around.
+    bool mEdgeAligned;
   };
 
   Marker mIStart;  // the inline start marker
   Marker mIEnd;    // the inline end marker
 };
 
 }  // namespace css
 }  // namespace mozilla
--- a/layout/generic/moz.build
+++ b/layout/generic/moz.build
@@ -130,16 +130,17 @@ EXPORTS += [
     'ScrollSnap.h',
     'TextDrawTarget.h',
     'Visibility.h',
 ]
 
 EXPORTS.mozilla += [
     'AspectRatio.h',
     'AutoCopyListener.h',
+    'ColumnUtils.h',
     'CSSAlignUtils.h',
     'CSSOrderAwareFrameIterator.h',
     'FrameTypeList.h',
     'ReflowInput.h',
     'ReflowOutput.h',
     'ViewportFrame.h',
     'WritingModes.h',
 ]
@@ -148,16 +149,17 @@ EXPORTS.mozilla.layout += [
     'FrameChildList.h',
     'ScrollAnchorContainer.h',
 ]
 
 UNIFIED_SOURCES += [
     'BlockReflowInput.cpp',
     'BRFrame.cpp',
     'ColumnSetWrapperFrame.cpp',
+    'ColumnUtils.cpp',
     'CSSAlignUtils.cpp',
     'CSSOrderAwareFrameIterator.cpp',
     'DetailsFrame.cpp',
     'FrameChildList.cpp',
     'MathMLTextRunFactory.cpp',
     'nsAbsoluteContainingBlock.cpp',
     'nsBackdropFrame.cpp',
     'nsBlockFrame.cpp',
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -53,16 +53,17 @@
 #include "CounterStyleManager.h"
 #include "mozilla/dom/HTMLDetailsElement.h"
 #include "mozilla/dom/HTMLSummaryElement.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/Telemetry.h"
+#include "nsFlexContainerFrame.h"
 
 #include "nsBidiPresUtils.h"
 
 #include <inttypes.h>
 
 static const int MIN_LINES_NEEDING_CURSOR = 20;
 
 static const char16_t kDiscCharacter = 0x2022;
@@ -1052,16 +1053,149 @@ static LogicalSize CalculateContainingBl
                             logicalScrollbars.BStartEnd(aWM));
       }
     }
   }
 
   return cbSize;
 }
 
+/**
+ * Returns aFrame if it is a non-BFC block frame, and null otherwise.
+ *
+ * This is used to determine whether to recurse into aFrame when applying
+ * -webkit-line-clamp.
+ */
+static nsBlockFrame* GetAsLineClampDescendant(nsIFrame* aFrame) {
+  if (nsBlockFrame* block = do_QueryFrame(aFrame)) {
+    if (!block->HasAllStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS)) {
+      return block;
+    }
+  }
+  return nullptr;
+}
+
+/**
+ * Iterator over all descendant inline line boxes, except for those that are
+ * under an independent formatting context.
+ */
+class MOZ_RAII LineClampLineIterator {
+ public:
+  explicit LineClampLineIterator(nsBlockFrame* aFrame)
+      : mCur(aFrame->LinesBegin()),
+        mEnd(aFrame->LinesEnd()),
+        mCurrentFrame(mCur == mEnd ? nullptr : aFrame) {
+    if (mCur != mEnd && !mCur->IsInline()) {
+      Advance();
+    }
+  }
+
+  nsLineBox* GetCurrentLine() { return mCurrentFrame ? mCur.get() : nullptr; }
+  nsBlockFrame* GetCurrentFrame() { return mCurrentFrame; }
+
+  // Advances the iterator to the next line line.
+  //
+  // Next() shouldn't be called once the iterator is at the end, which can be
+  // checked for by GetCurrentLine() or GetCurrentFrame() returning null.
+  void Next() {
+    MOZ_ASSERT(mCur != mEnd && mCurrentFrame,
+               "Don't call Next() when the iterator is at the end");
+    ++mCur;
+    Advance();
+  }
+
+ private:
+  void Advance() {
+    for (;;) {
+      if (mCur == mEnd) {
+        // Reached the end of the current block.  Pop the parent off the
+        // stack; if there isn't one, then we've reached the end.
+        if (mStack.IsEmpty()) {
+          mCurrentFrame = nullptr;
+          break;
+        }
+        auto entry = mStack.PopLastElement();
+        mCurrentFrame = entry.first();
+        mCur = entry.second();
+        mEnd = mCurrentFrame->LinesEnd();
+      } else if (mCur->IsBlock()) {
+        if (nsBlockFrame* child = GetAsLineClampDescendant(mCur->mFirstChild)) {
+          nsBlockFrame::LineIterator next = mCur;
+          ++next;
+          mStack.AppendElement(MakePair(mCurrentFrame, next));
+          mCur = child->LinesBegin();
+          mEnd = child->LinesEnd();
+          mCurrentFrame = child;
+        } else {
+          // Some kind of frame we shouldn't descend into.
+          ++mCur;
+        }
+      } else {
+        MOZ_ASSERT(mCur->IsInline());
+        break;
+      }
+    }
+  }
+
+  // The current line within the current block.
+  //
+  // When this is equal to mEnd, the iterator is at its end, and mCurrentFrame
+  // is set to null.
+  nsBlockFrame::LineIterator mCur;
+
+  // The iterator end for the current block.
+  nsBlockFrame::LineIterator mEnd;
+
+  // The current block.
+  nsBlockFrame* mCurrentFrame;
+
+  // Stack of mCurrentFrame and mEnd values that we push and pop as we enter and
+  // exist blocks.
+  AutoTArray<Pair<nsBlockFrame*, nsBlockFrame::LineIterator>, 8> mStack;
+};
+
+static bool ClearLineClampEllipsis(nsBlockFrame* aFrame) {
+  if (!aFrame->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS)) {
+    for (nsIFrame* f : aFrame->PrincipalChildList()) {
+      if (nsBlockFrame* child = GetAsLineClampDescendant(f)) {
+        if (ClearLineClampEllipsis(child)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  aFrame->RemoveStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS);
+
+  nsBlockFrame::LineIterator line = aFrame->LinesBegin();
+  nsBlockFrame::LineIterator end = aFrame->LinesEnd();
+  while (line != end) {
+    if (line->HasLineClampEllipsis()) {
+      line->ClearHasLineClampEllipsis();
+      return true;
+    }
+    ++line;
+  }
+
+  MOZ_ASSERT_UNREACHABLE("expected to find a line with HasLineClampEllipsis");
+  return true;
+}
+
+void nsBlockFrame::ClearLineClampEllipsis() {
+  ::ClearLineClampEllipsis(this);
+}
+
+static bool IsLineClampItem(const ReflowInput& aReflowInput) {
+  return aReflowInput.mFlags.mApplyLineClamp ||
+         (aReflowInput.mParentReflowInput &&
+          aReflowInput.mParentReflowInput->mFrame->IsScrollFrame() &&
+          aReflowInput.mParentReflowInput->mFlags.mApplyLineClamp);
+}
+
 void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
                           const ReflowInput& aReflowInput,
                           nsReflowStatus& aStatus) {
   MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
 
@@ -1298,16 +1432,21 @@ void nsBlockFrame::Reflow(nsPresContext*
       }
       bbox.BStart(wm) = position.mBaseline - markerBaseline;
       marker->SetRect(wm, bbox, reflowOutput.PhysicalSize());
     }
     // Otherwise just leave the ::marker where it is, up against our
     // block-start padding.
   }
 
+  // Clear any existing -webkit-line-clamp ellipsis.
+  if (IsLineClampItem(aReflowInput)) {
+    ClearLineClampEllipsis();
+  }
+
   CheckFloats(state);
 
   // Compute our final size
   nscoord blockEndEdgeOfChildren;
   ComputeFinalSize(*reflowInput, state, aMetrics, &blockEndEdgeOfChildren);
 
   // If the block direction is right-to-left, we need to update the bounds of
   // lines that were placed relative to mContainerSize during reflow, as
@@ -1510,16 +1649,119 @@ bool nsBlockFrame::CheckForCollapsedBEnd
     }
     if (line->HasClearance()) {
       return true;
     }
   }
   // not reached
 }
 
+static nsLineBox* FindLineClampTarget(nsBlockFrame*& aFrame,
+                                      uint32_t aLineNumber) {
+  MOZ_ASSERT(aLineNumber > 0);
+  MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS),
+             "Should have been removed earlier in nsBlockReflow::Reflow");
+
+  nsLineBox* target = nullptr;
+  nsBlockFrame* targetFrame = nullptr;
+  bool foundFollowingLine = false;
+
+  LineClampLineIterator iter(aFrame);
+
+  while (nsLineBox* line = iter.GetCurrentLine()) {
+    MOZ_ASSERT(!line->HasLineClampEllipsis(),
+               "Should have been removed earlier in nsBlockFrame::Reflow");
+    MOZ_ASSERT(!iter.GetCurrentFrame()->HasAnyStateBits(
+                   NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS),
+               "Should have been removed earlier in nsBlockReflow::Reflow");
+
+    // Don't count a line that only has collapsible white space (as might exist
+    // after calling e.g. getBoxQuads).
+    if (line->IsEmpty()) {
+      continue;
+    }
+
+    if (aLineNumber == 0) {
+      // We already previously found our target line, and now we have
+      // confirmed that there is another line after it.
+      foundFollowingLine = true;
+      break;
+    }
+
+    if (--aLineNumber == 0) {
+      // This is our target line.  Continue looping to confirm that we
+      // have another line after us.
+      target = line;
+      targetFrame = iter.GetCurrentFrame();
+    }
+
+    iter.Next();
+  }
+
+  if (!foundFollowingLine) {
+    aFrame = nullptr;
+    return nullptr;
+  }
+
+  MOZ_ASSERT(target);
+  MOZ_ASSERT(targetFrame);
+
+  aFrame = targetFrame;
+  return target;
+}
+
+static nscoord ApplyLineClamp(const ReflowInput& aReflowInput,
+                              nsBlockFrame* aFrame, nscoord aContentBSize) {
+  // We only do the work of applying the -webkit-line-clamp value during the
+  // measuring bsize reflow.  Boxes affected by -webkit-line-clamp are always
+  // inflexible, so we will never need to select a different line to place the
+  // ellipsis on in the subsequent real reflow.
+  if (!IsLineClampItem(aReflowInput)) {
+    return aContentBSize;
+  }
+
+  auto container =
+      static_cast<nsFlexContainerFrame*>(nsLayoutUtils::GetClosestFrameOfType(
+          aFrame, LayoutFrameType::FlexContainer));
+  MOZ_ASSERT(container,
+             "A flex item affected by -webkit-line-clamp must have an ancestor "
+             "flex container");
+
+  uint32_t lineClamp = container->GetLineClampValue();
+  if (lineClamp == 0) {
+    // -webkit-line-clamp is none or doesn't apply.
+    return aContentBSize;
+  }
+
+  MOZ_ASSERT(container->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX),
+             "Should only have an effective -webkit-line-clamp value if we "
+             "are in a legacy flex container");
+
+  nsBlockFrame* frame = aFrame;
+  nsLineBox* line = FindLineClampTarget(frame, lineClamp);
+  if (!line) {
+    // The number of lines did not exceed the -webkit-line-clamp value.
+    return aContentBSize;
+  }
+
+  // Mark the line as having an ellipsis so that TextOverflow will render it.
+  line->SetHasLineClampEllipsis();
+  frame->AddStateBits(NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS);
+  container->AddStateBits(NS_STATE_FLEX_HAS_LINE_CLAMP_ELLIPSIS);
+
+  // Translate the b-end edge of the line up to aFrame's space.
+  nscoord edge = line->BEnd();
+  for (nsIFrame* f = frame; f != aFrame; f = f->GetParent()) {
+    edge +=
+        f->GetLogicalPosition(f->GetParent()->GetSize()).B(f->GetWritingMode());
+  }
+
+  return edge;
+}
+
 void nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput,
                                     BlockReflowInput& aState,
                                     ReflowOutput& aMetrics,
                                     nscoord* aBEndEdgeOfChildren) {
   WritingMode wm = aState.mReflowInput.GetWritingMode();
   const LogicalMargin& borderPadding = aState.BorderPadding();
 #ifdef NOISY_FINAL_SIZE
   ListTag(stdout);
@@ -1620,20 +1862,22 @@ void nsBlockFrame::ComputeFinalSize(cons
     // Hence this case is a simplified version of the case below.
     nscoord contentBSize = 0;
     nscoord autoBSize = aReflowInput.ApplyMinMaxBSize(contentBSize);
     aMetrics.mCarriedOutBEndMargin.Zero();
     autoBSize += borderPadding.BStartEnd(wm);
     finalSize.BSize(wm) = autoBSize;
   } else if (aState.mReflowStatus.IsComplete()) {
     nscoord contentBSize = blockEndEdgeOfChildren - borderPadding.BStart(wm);
-    nscoord autoBSize = aReflowInput.ApplyMinMaxBSize(contentBSize);
+    nscoord lineClampedContentBSize =
+        ApplyLineClamp(aReflowInput, this, contentBSize);
+    nscoord autoBSize = aReflowInput.ApplyMinMaxBSize(lineClampedContentBSize);
     if (autoBSize != contentBSize) {
-      // Our min- or max-bsize value made our bsize change.  Don't carry out
-      // our kids' block-end margins.
+      // Our min-block-size, max-block-size, or -webkit-line-clamp value made
+      // our bsize change.  Don't carry out our kids' block-end margins.
       aMetrics.mCarriedOutBEndMargin.Zero();
     }
     autoBSize += borderPadding.BStart(wm) + borderPadding.BEnd(wm);
     finalSize.BSize(wm) = autoBSize;
   } else {
     NS_ASSERTION(aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE,
                  "Shouldn't be incomplete if availableBSize is UNCONSTRAINED.");
     finalSize.BSize(wm) =
@@ -2803,19 +3047,19 @@ void nsBlockFrame::ReflowLine(BlockReflo
 
   // Now that we know what kind of line we have, reflow it
   if (aLine->IsBlock()) {
     ReflowBlockFrame(aState, aLine, aKeepReflowGoing);
   } else {
     aLine->SetLineWrapped(false);
     ReflowInlineFrames(aState, aLine, aKeepReflowGoing);
 
-    // Store the line's float edges for text-overflow analysis if needed.
+    // Store the line's float edges for overflow marker analysis if needed.
     aLine->ClearFloatEdges();
-    if (aState.mFlags.mCanHaveTextOverflow) {
+    if (aState.mFlags.mCanHaveOverflowMarkers) {
       WritingMode wm = aLine->mWritingMode;
       nsFlowAreaRect r = aState.GetFloatAvailableSpaceForBSize(
           aLine->BStart(), aLine->BSize(), nullptr);
       if (r.HasFloats()) {
         LogicalRect so = aLine->GetOverflowArea(eScrollableOverflow, wm,
                                                 aLine->mContainerSize);
         nscoord s = r.mRect.IStart(wm);
         nscoord e = r.mRect.IEnd(wm);
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -572,16 +572,22 @@ class nsBlockFrame : public nsContainerF
       return pushedFloats && !pushedFloats->IsEmpty();
     }
     return false;
   }
 
   // @see nsIFrame::AddSizeOfExcludingThisForTree
   void AddSizeOfExcludingThisForTree(nsWindowSizes&) const override;
 
+  /**
+   * Clears any -webkit-line-clamp ellipsis on a line in this block or one
+   * of its descendants.
+   */
+  void ClearLineClampEllipsis();
+
  protected:
   /** @see DoRemoveFrame */
   void DoRemoveFrameInternal(nsIFrame* aDeletedFrame, uint32_t aFlags,
                              PostDestroyData& data);
 
   /** grab overflow lines from this block's prevInFlow, and make them
    * part of this block's mLines list.
    * @return true if any lines were drained.
--- a/layout/generic/nsColumnSetFrame.cpp
+++ b/layout/generic/nsColumnSetFrame.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/. */
 
 /* rendering object for css3 multi-column layout */
 
 #include "nsColumnSetFrame.h"
 
+#include "mozilla/ColumnUtils.h"
 #include "mozilla/Logging.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/ToString.h"
 #include "nsCSSRendering.h"
 
 using namespace mozilla;
 using namespace mozilla::layout;
 
@@ -270,42 +271,28 @@ nscoord nsColumnSetFrame::GetAvailableCo
 
   WritingMode wm = aReflowInput.GetWritingMode();
   LogicalMargin bp = aReflowInput.ComputedLogicalBorderPadding();
   bp.ApplySkipSides(GetLogicalSkipSides(&aReflowInput));
   bp.BEnd(wm) = aReflowInput.ComputedLogicalBorderPadding().BEnd(wm);
   return std::max(0, aReflowInput.AvailableBSize() - bp.BStartEnd(wm));
 }
 
-static nscoord GetColumnGap(const nsColumnSetFrame* aFrame,
-                            nscoord aPercentageBasis) {
-  const auto& columnGap = aFrame->StylePosition()->mColumnGap;
-  if (columnGap.IsNormal()) {
-    return aFrame->StyleFont()->mFont.size;
-  }
-  return nsLayoutUtils::ResolveGapToLength(columnGap, aPercentageBasis);
-}
-
 static uint32_t ColumnBalancingDepth(const ReflowInput& aReflowInput,
                                      uint32_t aMaxDepth) {
   uint32_t depth = 0;
   for (const ReflowInput* ri = aReflowInput.mParentReflowInput;
        ri && depth < aMaxDepth; ri = ri->mParentReflowInput) {
     if (ri->mFlags.mIsColumnBalancing) {
       ++depth;
     }
   }
   return depth;
 }
 
-static nscoord ClampUsedColumnWidth(const Length& aColumnWidth) {
-  // Per spec, used values will be clamped to a minimum of 1px.
-  return std::max(CSSPixel::ToAppUnits(1), aColumnWidth.ToAppUnits());
-}
-
 nsColumnSetFrame::ReflowConfig nsColumnSetFrame::ChooseColumnStrategy(
     const ReflowInput& aReflowInput, bool aForceAuto = false) const {
   WritingMode wm = aReflowInput.GetWritingMode();
 
   const nsStyleColumn* colStyle = StyleColumn();
   nscoord availContentISize = GetAvailableContentISize(aReflowInput);
   if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) {
     availContentISize = aReflowInput.ComputedISize();
@@ -326,17 +313,18 @@ nsColumnSetFrame::ReflowConfig nsColumnS
     colBSize = std::min(colBSize, aReflowInput.ComputedMaxBSize());
   } else if (StaticPrefs::layout_css_column_span_enabled() &&
              aReflowInput.mCBReflowInput->ComputedMaxBSize() !=
                  NS_INTRINSICSIZE) {
     colBSize =
         std::min(colBSize, aReflowInput.mCBReflowInput->ComputedMaxBSize());
   }
 
-  nscoord colGap = GetColumnGap(this, aReflowInput.ComputedISize());
+  nscoord colGap =
+      ColumnUtils::GetColumnGap(this, aReflowInput.ComputedISize());
   int32_t numColumns = colStyle->mColumnCount;
 
   // If column-fill is set to 'balance', then we want to balance the columns.
   const bool isBalancing =
       colStyle->mColumnFill == StyleColumnFill::Balance && !aForceAuto;
   if (isBalancing) {
     const uint32_t kMaxNestedColumnBalancingDepth = 2;
     const uint32_t balancingDepth =
@@ -345,17 +333,18 @@ nsColumnSetFrame::ReflowConfig nsColumnS
       numColumns = 1;
     }
   }
 
   nscoord colISize;
   // In vertical writing-mode, "column-width" (inline size) will actually be
   // physical height, but its CSS name is still column-width.
   if (colStyle->mColumnWidth.IsLength()) {
-    colISize = ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength());
+    colISize =
+        ColumnUtils::ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength());
     NS_ASSERTION(colISize >= 0, "negative column width");
     // Reduce column count if necessary to make columns fit in the
     // available width. Compute max number of columns that fit in
     // availContentISize, satisfying colGap*(maxColumns - 1) +
     // colISize*maxColumns <= availContentISize
     if (availContentISize != NS_INTRINSICSIZE && colGap + colISize > 0 &&
         numColumns > 0) {
       // This expression uses truncated rounding, which is what we
@@ -485,81 +474,86 @@ static void MoveChildTo(nsIFrame* aChild
   aChild->SetPosition(aWM, aOrigin, aContainerSize);
   nsContainerFrame::PlaceFrameView(aChild);
 }
 
 nscoord nsColumnSetFrame::GetMinISize(gfxContext* aRenderingContext) {
   nscoord iSize = 0;
   DISPLAY_MIN_INLINE_SIZE(this, iSize);
 
-  if (mFrames.FirstChild() && !StyleDisplay()->IsContainSize()) {
+  if (mFrames.FirstChild() && (StaticPrefs::layout_css_column_span_enabled() ||
+                               !StyleDisplay()->IsContainSize())) {
     // We want to ignore this in the case that we're size contained
     // because our children should not contribute to our
     // intrinsic size.
+    //
+    // Bug 1499281: When we remove the column-span pref, we can also remove the
+    // contain:size check since nsColumnSetFrame is no longer the outermost
+    // frame in a multicol container, and we need to handle size-containment at
+    // the level of the outermost frame for the contained element.
     iSize = mFrames.FirstChild()->GetMinISize(aRenderingContext);
   }
   const nsStyleColumn* colStyle = StyleColumn();
-  nscoord colISize;
   if (colStyle->mColumnWidth.IsLength()) {
-    colISize = ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength());
+    nscoord colISize =
+        ColumnUtils::ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength());
     // As available width reduces to zero, we reduce our number of columns
     // to one, and don't enforce the column width, so just return the min
     // of the child's min-width with any specified column width.
     iSize = std::min(iSize, colISize);
   } else {
     NS_ASSERTION(colStyle->mColumnCount > 0,
                  "column-count and column-width can't both be auto");
     // As available width reduces to zero, we still have mColumnCount columns,
-    // so multiply the child's min-width by the number of columns (n) and
-    // include n-1 column gaps.
-    colISize = iSize;
-    iSize *= colStyle->mColumnCount;
-    nscoord colGap = GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
-    iSize += colGap * (colStyle->mColumnCount - 1);
-    // The multiplication above can make 'width' negative (integer overflow),
-    // so use std::max to protect against that.
-    iSize = std::max(iSize, colISize);
+    // so compute our minimum size based on the number of columns and their gaps
+    // and minimum per-column size.
+    nscoord colGap = ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
+    iSize = ColumnUtils::IntrinsicISize(colStyle->mColumnCount, colGap, iSize);
   }
   // XXX count forced column breaks here? Maybe we should return the child's
   // min-width times the minimum number of columns.
   return iSize;
 }
 
 nscoord nsColumnSetFrame::GetPrefISize(gfxContext* aRenderingContext) {
   // Our preferred width is our desired column width, if specified, otherwise
   // the child's preferred width, times the number of columns, plus the width
   // of any required column gaps
   // XXX what about forced column breaks here?
   nscoord result = 0;
   DISPLAY_PREF_INLINE_SIZE(this, result);
   const nsStyleColumn* colStyle = StyleColumn();
-  nscoord colGap = GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
 
   nscoord colISize;
   if (colStyle->mColumnWidth.IsLength()) {
-    colISize = ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength());
-  } else if (mFrames.FirstChild() && !StyleDisplay()->IsContainSize()) {
+    colISize =
+        ColumnUtils::ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength());
+  } else if (mFrames.FirstChild() &&
+             (StaticPrefs::layout_css_column_span_enabled() ||
+              !StyleDisplay()->IsContainSize())) {
     // We want to ignore this in the case that we're size contained
     // because our children should not contribute to our
     // intrinsic size.
+    //
+    // Bug 1499281: When we remove the column-span pref, we can also remove the
+    // contain:size check since nsColumnSetFrame is no longer the outermost
+    // frame in a multicol container, and we need to handle size-containment at
+    // the level of the outermost frame for the contained element.
     colISize = mFrames.FirstChild()->GetPrefISize(aRenderingContext);
   } else {
     colISize = 0;
   }
 
-  int32_t numColumns = colStyle->mColumnCount;
-  if (numColumns <= 0) {
-    // if column-count is auto, assume one column
-    numColumns = 1;
-  }
-
-  nscoord iSize = colISize * numColumns + colGap * (numColumns - 1);
-  // The multiplication above can make 'iSize' negative (integer overflow),
-  // so use std::max to protect against that.
-  result = std::max(iSize, colISize);
+  // If column-count is auto, assume one column.
+  uint32_t numColumns =
+      colStyle->mColumnCount == nsStyleColumn::kColumnCountAuto
+          ? 1
+          : colStyle->mColumnCount;
+  nscoord colGap = ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
+  result = ColumnUtils::IntrinsicISize(numColumns, colGap, colISize);
   return result;
 }
 
 nsColumnSetFrame::ColumnBalanceData nsColumnSetFrame::ReflowChildren(
     ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput,
     nsReflowStatus& aStatus, const ReflowConfig& aConfig,
     bool aUnboundedLastColumn) {
   ColumnBalanceData colData;
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -828,16 +828,20 @@ class nsFlexContainerFrame::FlexItem : p
 
   uint32_t GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const;
 
   // Once the main size has been resolved, should we bother doing layout to
   // establish the cross size?
   bool CanMainSizeInfluenceCrossSize(
       const FlexboxAxisTracker& aAxisTracker) const;
 
+  // Gets the block frame that contains the flex item's content.  This is
+  // Frame() itself or one of its descendants.
+  nsBlockFrame* BlockFrame() const;
+
  protected:
   // Helper called by the constructor, to set mNeedsMinSizeAutoResolution:
   void CheckForMinSizeAuto(const ReflowInput& aFlexItemReflowInput,
                            const FlexboxAxisTracker& aAxisTracker);
 
   // Values that we already know in constructor (and are hence mostly 'const'):
   nsIFrame* const mFrame;  // The flex item's frame.
   const float mFlexGrow;
@@ -1257,29 +1261,35 @@ uint16_t nsFlexContainerFrame::CSSAlignm
   }
 
   return (alignment | alignmentFlags);
 }
 
 UniquePtr<FlexItem> nsFlexContainerFrame::GenerateFlexItemForChild(
     nsPresContext* aPresContext, nsIFrame* aChildFrame,
     const ReflowInput& aParentReflowInput,
-    const FlexboxAxisTracker& aAxisTracker) {
+    const FlexboxAxisTracker& aAxisTracker, bool aHasLineClampEllipsis) {
   // Create temporary reflow input just for sizing -- to get hypothetical
   // main-size and the computed values of min / max main-size property.
   // (This reflow input will _not_ be used for reflow.)
   ReflowInput childRI(
       aPresContext, aParentReflowInput, aChildFrame,
       aParentReflowInput.ComputedSize(aChildFrame->GetWritingMode()));
+  childRI.mFlags.mInsideLineClamp = GetLineClampValue() != 0;
 
   // FLEX GROW & SHRINK WEIGHTS
   // --------------------------
   float flexGrow, flexShrink;
   if (IsLegacyBox(this)) {
-    flexGrow = flexShrink = aChildFrame->StyleXUL()->mBoxFlex;
+    if (GetLineClampValue() != 0) {
+      // Items affected by -webkit-line-clamp are always inflexible.
+      flexGrow = flexShrink = 0;
+    } else {
+      flexGrow = flexShrink = aChildFrame->StyleXUL()->mBoxFlex;
+    }
   } else {
     const nsStylePosition* stylePos = aChildFrame->StylePosition();
     flexGrow = stylePos->mFlexGrow;
     flexShrink = stylePos->mFlexShrink;
   }
 
   WritingMode childWM = childRI.GetWritingMode();
 
@@ -1374,17 +1384,18 @@ UniquePtr<FlexItem> nsFlexContainerFrame
       item->SetWasMinClamped();
     } else if (flexBaseSize > mainMaxSize) {
       item->SetWasMaxClamped();
     }
   }
 
   // Resolve "flex-basis:auto" and/or "min-[width|height]:auto" (which might
   // require us to reflow the item to measure content height)
-  ResolveAutoFlexBasisAndMinSize(aPresContext, *item, childRI, aAxisTracker);
+  ResolveAutoFlexBasisAndMinSize(aPresContext, *item, childRI, aAxisTracker,
+                                 aHasLineClampEllipsis);
   return item;
 }
 
 // Static helper-functions for ResolveAutoFlexBasisAndMinSize():
 // -------------------------------------------------------------
 // Indicates whether the cross-size property is set to something definite,
 // for the purpose of intrinsic ratio calculations.
 // The logic here should be similar to the logic for isAutoISize/isAutoBSize
@@ -1549,18 +1560,18 @@ static bool ResolveAutoFlexBasisFromRati
   }
   return false;
 }
 
 // Note: If & when we handle "min-height: min-content" for flex items,
 // we may want to resolve that in this function, too.
 void nsFlexContainerFrame::ResolveAutoFlexBasisAndMinSize(
     nsPresContext* aPresContext, FlexItem& aFlexItem,
-    const ReflowInput& aItemReflowInput,
-    const FlexboxAxisTracker& aAxisTracker) {
+    const ReflowInput& aItemReflowInput, const FlexboxAxisTracker& aAxisTracker,
+    bool aHasLineClampEllipsis) {
   // (Note: We can guarantee that the flex-basis will have already been
   // resolved if the main axis is the same is the same as the item's inline
   // axis. Inline-axis values should always be resolvable without reflow.)
   const bool isMainSizeAuto = (!aFlexItem.IsInlineAxisMainAxis() &&
                                NS_AUTOHEIGHT == aFlexItem.GetFlexBaseSize());
 
   const bool isMainMinSizeAuto = aFlexItem.NeedsMinSizeAutoResolution();
 
@@ -1653,17 +1664,17 @@ void nsFlexContainerFrame::ResolveAutoFl
       // need.)
       bool forceBResizeForMeasuringReflow =
           !aFlexItem.IsFrozen() ||          // Is the item flexible?
           !flexBasisNeedsToMeasureContent;  // Are we *only* measuring it for
                                             // 'min-block-size:auto'?
 
       nscoord contentBSize = MeasureFlexItemContentBSize(
           aPresContext, aFlexItem, forceBResizeForMeasuringReflow,
-          *flexContainerRI);
+          aHasLineClampEllipsis, *flexContainerRI);
       if (minSizeNeedsToMeasureContent) {
         resolvedMinSize = std::min(resolvedMinSize, contentBSize);
       }
       if (flexBasisNeedsToMeasureContent) {
         aFlexItem.SetFlexBaseSizeAndMainSize(contentBSize);
       }
     }
   }
@@ -1818,26 +1829,29 @@ void nsFlexContainerFrame::MarkIntrinsic
   mCachedMinISize = NS_INTRINSIC_ISIZE_UNKNOWN;
   mCachedPrefISize = NS_INTRINSIC_ISIZE_UNKNOWN;
 
   nsContainerFrame::MarkIntrinsicISizesDirty();
 }
 
 nscoord nsFlexContainerFrame::MeasureFlexItemContentBSize(
     nsPresContext* aPresContext, FlexItem& aFlexItem,
-    bool aForceBResizeForMeasuringReflow,
+    bool aForceBResizeForMeasuringReflow, bool aHasLineClampEllipsis,
     const ReflowInput& aParentReflowInput) {
   // Set up a reflow input for measuring the flex item's auto-height:
   WritingMode wm = aFlexItem.Frame()->GetWritingMode();
   LogicalSize availSize = aParentReflowInput.ComputedSize(wm);
   availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
   ReflowInput childRIForMeasuringBSize(aPresContext, aParentReflowInput,
                                        aFlexItem.Frame(), availSize, Nothing(),
                                        ReflowInput::CALLER_WILL_INIT);
   childRIForMeasuringBSize.mFlags.mIsFlexContainerMeasuringBSize = true;
+  childRIForMeasuringBSize.mFlags.mInsideLineClamp = GetLineClampValue() != 0;
+  childRIForMeasuringBSize.mFlags.mApplyLineClamp =
+      childRIForMeasuringBSize.mFlags.mInsideLineClamp || aHasLineClampEllipsis;
   childRIForMeasuringBSize.Init(aPresContext);
 
   if (aFlexItem.IsStretched()) {
     childRIForMeasuringBSize.SetComputedISize(aFlexItem.GetCrossSize());
     childRIForMeasuringBSize.SetIResize(true);
   }
 
   if (aForceBResizeForMeasuringReflow) {
@@ -3350,16 +3364,32 @@ void FlexItem::ResolveStretchedCrossSize
   stretchedSize = NS_CSS_MINMAX(stretchedSize, mCrossMinSize, mCrossMaxSize);
 
   // Update the cross-size & make a note that it's stretched, so we know to
   // override the reflow input's computed cross-size in our final reflow.
   SetCrossSize(stretchedSize);
   mIsStretched = true;
 }
 
+static nsBlockFrame* FindFlexItemBlockFrame(nsIFrame* aFrame) {
+  if (nsBlockFrame* block = do_QueryFrame(aFrame)) {
+    return block;
+  }
+  for (nsIFrame* f : aFrame->PrincipalChildList()) {
+    if (nsBlockFrame* block = FindFlexItemBlockFrame(f)) {
+      return block;
+    }
+  }
+  return nullptr;
+}
+
+nsBlockFrame* FlexItem::BlockFrame() const {
+  return FindFlexItemBlockFrame(Frame());
+}
+
 void SingleLineCrossAxisPositionTracker::ResolveAutoMarginsInCrossAxis(
     const FlexLine& aLine, FlexItem& aItem) {
   // Subtract the space that our item is already occupying, to see how much
   // space (if any) is available for its auto margins.
   nscoord spaceForAutoMargins =
       aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
 
   if (spaceForAutoMargins <= 0) {
@@ -3723,17 +3753,18 @@ bool nsFlexContainerFrame::ShouldUseMozB
 
   return false;
 }
 
 void nsFlexContainerFrame::GenerateFlexLines(
     nsPresContext* aPresContext, const ReflowInput& aReflowInput,
     nscoord aContentBoxMainSize, nscoord aAvailableBSizeForContent,
     const nsTArray<StrutInfo>& aStruts, const FlexboxAxisTracker& aAxisTracker,
-    nscoord aMainGapSize, nsTArray<nsIFrame*>& aPlaceholders, /* out */
+    nscoord aMainGapSize, bool aHasLineClampEllipsis,
+    nsTArray<nsIFrame*>& aPlaceholders, /* out */
     LinkedList<FlexLine>& aLines /* out */) {
   MOZ_ASSERT(aLines.isEmpty(), "Expecting outparam to start out empty");
 
   const bool isSingleLine =
       NS_STYLE_FLEX_WRAP_NOWRAP == aReflowInput.mStylePosition->mFlexWrap;
 
   // If we're transparently reversing axes, then we'll need to link up our
   // FlexItems and FlexLines in the reverse order, so that the rest of flex
@@ -3822,17 +3853,17 @@ void nsFlexContainerFrame::GenerateFlexL
                aStruts[nextStrutIdx].mItemIdx == itemIdxInContainer) {
       // Use the simplified "strut" FlexItem constructor:
       item = MakeUnique<FlexItem>(childFrame,
                                   aStruts[nextStrutIdx].mStrutCrossSize,
                                   aReflowInput.GetWritingMode());
       nextStrutIdx++;
     } else {
       item = GenerateFlexItemForChild(aPresContext, childFrame, aReflowInput,
-                                      aAxisTracker);
+                                      aAxisTracker, aHasLineClampEllipsis);
     }
 
     nscoord itemInnerHypotheticalMainSize = item->GetMainSize();
     nscoord itemOuterHypotheticalMainSize =
         item->GetOuterMainSize(aAxisTracker.GetMainAxis());
 
     // Check if we need to wrap |item| to a new line
     // (i.e. check if its outer hypothetical main size pushes our line over
@@ -4218,16 +4249,23 @@ void nsFlexContainerFrame::Reflow(nsPres
   const auto& bsize = stylePos->BSize(wm);
   if (bsize.HasPercent() || (StyleDisplay()->IsAbsolutelyPositionedStyle() &&
                              (bsize.IsAuto() || bsize.IsExtremumLength()) &&
                              !stylePos->mOffset.GetBStart(wm).IsAuto() &&
                              !stylePos->mOffset.GetBEnd(wm).IsAuto())) {
     AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
   }
 
+  // Check if there is a -webkit-line-clamp ellipsis somewhere inside at least
+  // one of the flex items, so we can clear the flag before the block frame
+  // re-sets it on the appropriate line during its bsize measuring reflow.
+  bool hasLineClampEllipsis =
+      HasAnyStateBits(NS_STATE_FLEX_HAS_LINE_CLAMP_ELLIPSIS);
+  RemoveStateBits(NS_STATE_FLEX_HAS_LINE_CLAMP_ELLIPSIS);
+
   const FlexboxAxisTracker axisTracker(this, aReflowInput.GetWritingMode());
 
   // Check to see if we need to create a computed info structure, to
   // be filled out for use by devtools.
   if (HasAnyStateBits(NS_STATE_FLEX_GENERATE_COMPUTED_VALUES)) {
     // This state bit will never be cleared. That's acceptable because
     // it's only set in a Chrome API invoked by devtools, and won't
     // impact normal browsing.
@@ -4275,24 +4313,24 @@ void nsFlexContainerFrame::Reflow(nsPres
                          "flows)");
     crossGapSize = nsLayoutUtils::ResolveGapToLength(
         stylePos->mColumnGap, aReflowInput.ComputedISize());
   }
 
   AutoTArray<StrutInfo, 1> struts;
   DoFlexLayout(aPresContext, aDesiredSize, aReflowInput, aStatus,
                contentBoxMainSize, availableBSizeForContent, struts,
-               axisTracker, mainGapSize, crossGapSize);
+               axisTracker, mainGapSize, crossGapSize, hasLineClampEllipsis);
 
   if (!struts.IsEmpty()) {
     // We're restarting flex layout, with new knowledge of collapsed items.
     aStatus.Reset();
     DoFlexLayout(aPresContext, aDesiredSize, aReflowInput, aStatus,
                  contentBoxMainSize, availableBSizeForContent, struts,
-                 axisTracker, mainGapSize, crossGapSize);
+                 axisTracker, mainGapSize, crossGapSize, hasLineClampEllipsis);
   }
 }
 
 // Class to let us temporarily provide an override value for the the main-size
 // CSS property ('width' or 'height') on a flex item, for use in
 // nsFrame::ComputeSizeWithIntrinsicDimensions.
 // (We could use this overridden size more broadly, too, but it's probably
 // better to avoid property-table accesses.  So, where possible, we communicate
@@ -4497,25 +4535,26 @@ static mozilla::dom::FlexPhysicalDirecti
   }
 }
 
 void nsFlexContainerFrame::DoFlexLayout(
     nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
     const ReflowInput& aReflowInput, nsReflowStatus& aStatus,
     nscoord aContentBoxMainSize, nscoord aAvailableBSizeForContent,
     nsTArray<StrutInfo>& aStruts, const FlexboxAxisTracker& aAxisTracker,
-    nscoord aMainGapSize, nscoord aCrossGapSize) {
+    nscoord aMainGapSize, nscoord aCrossGapSize, bool aHasLineClampEllipsis) {
   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
 
   AutoCleanLinkedList<FlexLine> lines;
   nsTArray<nsIFrame*> placeholderKids;
 
   GenerateFlexLines(aPresContext, aReflowInput, aContentBoxMainSize,
                     aAvailableBSizeForContent, aStruts, aAxisTracker,
-                    aMainGapSize, placeholderKids, lines);
+                    aMainGapSize, aHasLineClampEllipsis, placeholderKids,
+                    lines);
 
   if ((lines.getFirst()->IsEmpty() && !lines.getFirst()->getNext()) ||
       aReflowInput.mStyleDisplay->IsContainLayout()) {
     // If have no flex items, or if we  are layout contained and
     // want to behave as if we have none, our parent
     // should synthesize a baseline if needed.
     AddStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE);
   } else {
@@ -4651,16 +4690,17 @@ void nsFlexContainerFrame::DoFlexLayout(
           sizeOverride.emplace(*item, aAxisTracker);
         }
 
         WritingMode wm = item->Frame()->GetWritingMode();
         LogicalSize availSize = aReflowInput.ComputedSize(wm);
         availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
         ReflowInput childReflowInput(aPresContext, aReflowInput, item->Frame(),
                                      availSize);
+        childReflowInput.mFlags.mInsideLineClamp = GetLineClampValue() != 0;
         if (!sizeOverride) {
           // Directly override the computed main-size, by tweaking reflow input:
           if (item->IsInlineAxisMainAxis()) {
             childReflowInput.SetComputedISize(item->GetMainSize());
           } else {
             childReflowInput.SetComputedBSize(item->GetMainSize());
           }
         }
@@ -4832,17 +4872,18 @@ void nsFlexContainerFrame::DoFlexLayout(
       // (i.e. its frame rect), instead of the container's content-box:
       framePos += containerContentBoxOrigin;
 
       // (Intentionally snapshotting this before ApplyRelativePositioning, to
       // maybe use for setting the flex container's baseline.)
       const nscoord itemNormalBPos = framePos.B(flexWM);
 
       // Check if we actually need to reflow the item -- if we already reflowed
-      // it with the right size, we can just reposition it as-needed.
+      // it with the right size, and there is no need to do a reflow to clear
+      // out a -webkit-line-clamp ellipsis, we can just reposition it as-needed.
       bool itemNeedsReflow = true;  // (Start out assuming the worst.)
       if (item->HadMeasuringReflow()) {
         LogicalSize finalFlexItemCBSize =
             aAxisTracker.LogicalSizeFromFlexRelativeSizes(item->GetMainSize(),
                                                           item->GetCrossSize());
         // We've already reflowed the child once. Was the size we gave it in
         // that reflow the same as its final (post-flexing/stretching) size?
         if (finalFlexItemCBSize ==
@@ -4864,17 +4905,27 @@ void nsFlexContainerFrame::DoFlexLayout(
         if (itemNeedsReflow) {
           MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
                   ("[perf] Flex item needed both a measuring reflow and "
                    "a final reflow\n"));
         }
       }
       if (itemNeedsReflow) {
         ReflowFlexItem(aPresContext, aAxisTracker, aReflowInput, *item,
-                       framePos, containerSize);
+                       framePos, containerSize, aHasLineClampEllipsis);
+      }
+
+      // If we didn't perform a final reflow of the item, we still have a
+      // -webkit-line-clamp ellipsis hanging around, but we shouldn't have one
+      // any more, we need to clear that now.  We strictly only need to do this
+      // if we didn't do a bsize measuring reflow of the item earlier (since
+      // that is normally when we deal with -webkit-line-clamp ellipses) but
+      // not all flex items need such a reflow.
+      if (!itemNeedsReflow && aHasLineClampEllipsis && GetLineClampValue() == 0) {
+        item->BlockFrame()->ClearLineClampEllipsis();
       }
 
       // If the item has auto margins, and we were tracking the UsedMargin
       // property, set the property to the computed margin values.
       if (item->HasAnyAutoMargin()) {
         nsMargin* propValue =
             item->Frame()->GetProperty(nsIFrame::UsedMarginProperty());
         if (propValue) {
@@ -5024,23 +5075,31 @@ void nsFlexContainerFrame::MoveFlexItemT
   aItem.Frame()->SetPosition(outerWM, aFramePos, aContainerSize);
   PositionFrameView(aItem.Frame());
   PositionChildViews(aItem.Frame());
 }
 
 void nsFlexContainerFrame::ReflowFlexItem(
     nsPresContext* aPresContext, const FlexboxAxisTracker& aAxisTracker,
     const ReflowInput& aReflowInput, const FlexItem& aItem,
-    LogicalPoint& aFramePos, const nsSize& aContainerSize) {
+    LogicalPoint& aFramePos, const nsSize& aContainerSize,
+    bool aHasLineClampEllipsis) {
   WritingMode outerWM = aReflowInput.GetWritingMode();
   WritingMode wm = aItem.Frame()->GetWritingMode();
   LogicalSize availSize = aReflowInput.ComputedSize(wm);
   availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
   ReflowInput childReflowInput(aPresContext, aReflowInput, aItem.Frame(),
                                availSize);
+  childReflowInput.mFlags.mInsideLineClamp = GetLineClampValue() != 0;
+  // This is the final reflow of this flex item; if we previously had a
+  // -webkit-line-clamp, and we missed our chance to clear the ellipsis
+  // because we didn't need to call MeasureFlexItemContentBSize, we set
+  // mApplyLineClamp to cause it to get cleared here.
+  childReflowInput.mFlags.mApplyLineClamp =
+      !childReflowInput.mFlags.mInsideLineClamp && aHasLineClampEllipsis;
 
   // Keep track of whether we've overriden the child's computed ISize
   // and/or BSize, so we can set its resize flags accordingly.
   bool didOverrideComputedISize = false;
   bool didOverrideComputedBSize = false;
 
   // Override computed main-size
   if (aItem.IsInlineAxisMainAxis()) {
@@ -5135,16 +5194,18 @@ void nsFlexContainerFrame::ReflowPlaceho
   // |aPlaceholders| at the container's content-box origin.
   for (nsIFrame* placeholder : aPlaceholders) {
     MOZ_ASSERT(placeholder->IsPlaceholderFrame(),
                "placeholders array should only contain placeholder frames");
     WritingMode wm = placeholder->GetWritingMode();
     LogicalSize availSize = aReflowInput.ComputedSize(wm);
     ReflowInput childReflowInput(aPresContext, aReflowInput, placeholder,
                                  availSize);
+    // No need to set the -webkit-line-clamp related flags when reflowing
+    // a placeholder.
     ReflowOutput childDesiredSize(childReflowInput);
     nsReflowStatus childReflowStatus;
     ReflowChild(placeholder, aPresContext, childDesiredSize, childReflowInput,
                 outerWM, aContentBoxOrigin, aContainerSize, 0,
                 childReflowStatus);
 
     FinishReflowChild(placeholder, aPresContext, childDesiredSize,
                       &childReflowInput, outerWM, aContentBoxOrigin,
@@ -5232,8 +5293,22 @@ nscoord nsFlexContainerFrame::GetPrefISi
     mCachedPrefISize =
         StyleDisplay()->IsContainSize()
             ? 0
             : IntrinsicISize(aRenderingContext, nsLayoutUtils::PREF_ISIZE);
   }
 
   return mCachedPrefISize;
 }
+
+uint32_t nsFlexContainerFrame::GetLineClampValue() const {
+  // -webkit-line-clamp should only work on items in flex containers that are
+  // display:-webkit-(inline-)box and -webkit-box-orient:vertical.
+  //
+  // This check makes -webkit-line-clamp work on display:-moz-box too, but
+  // that shouldn't be a big deal.
+  if (!HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX) ||
+      StyleXUL()->mBoxOrient != StyleBoxOrient::Vertical) {
+    return 0;
+  }
+
+  return StyleDisplay()->mLineClamp;
+}
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -146,16 +146,24 @@ class nsFlexContainerFrame final : publi
       return false;
     }
     *aBaseline = aBaselineGroup == BaselineSharingGroup::First
                      ? mBaselineFromLastReflow
                      : mLastBaselineFromLastReflow;
     return true;
   }
 
+  /**
+   * Returns the effective value of -webkit-line-clamp for this flex container.
+   *
+   * This will be 0 if the property is 'none', or if the element is not
+   * display:-webkit-(inline-)box and -webkit-box-orient:vertical.
+   */
+  uint32_t GetLineClampValue() const;
+
   // nsContainerFrame overrides
   uint16_t CSSAlignmentForAbsPosChild(
       const ReflowInput& aChildRI,
       mozilla::LogicalAxis aLogicalAxis) const override;
 
   /**
    * Helper function to calculate packing space and initial offset of alignment
    * subjects in MainAxisPositionTracker() and CrossAxisPositionTracker() for
@@ -259,17 +267,18 @@ class nsFlexContainerFrame final : publi
    * everything before that point.)
    */
   void DoFlexLayout(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
                     const ReflowInput& aReflowInput, nsReflowStatus& aStatus,
                     nscoord aContentBoxMainSize,
                     nscoord aAvailableBSizeForContent,
                     nsTArray<StrutInfo>& aStruts,
                     const FlexboxAxisTracker& aAxisTracker,
-                    nscoord aMainGapSize, nscoord aCrossGapSize);
+                    nscoord aMainGapSize, nscoord aCrossGapSize,
+                    bool aHasLineClampEllipsis);
 
   /**
    * Checks whether our child-frame list "mFrames" is sorted, using the given
    * IsLessThanOrEqual function, and sorts it if it's not already sorted.
    *
    * XXXdholbert Once we support pagination, we need to make this function
    * check our continuations as well (or wrap it in a function that does).
    *
@@ -292,17 +301,17 @@ class nsFlexContainerFrame final : publi
    * flex basis (including e.g. auto-height) as well as to resolve
    * "min-height:auto", via ResolveAutoFlexBasisAndMinSize(). (Basically, the
    * returned FlexItem will be ready to participate in the "Resolve the
    * Flexible Lengths" step of the Flex Layout Algorithm.)
    */
   mozilla::UniquePtr<FlexItem> GenerateFlexItemForChild(
       nsPresContext* aPresContext, nsIFrame* aChildFrame,
       const ReflowInput& aParentReflowInput,
-      const FlexboxAxisTracker& aAxisTracker);
+      const FlexboxAxisTracker& aAxisTracker, bool aHasLineClampEllipsis);
 
   /**
    * This method gets a cached measuring reflow for a flex item, or does it and
    * caches it.
    *
    * This avoids exponential reflows, see the comment on
    * CachedMeasuringReflowResult.
    */
@@ -314,27 +323,29 @@ class nsFlexContainerFrame final : publi
    * This method performs a "measuring" reflow to get the content BSize of
    * aFlexItem.Frame() (treating it as if it had a computed BSize of "auto"),
    * and returns the resulting BSize measurement.
    * (Helper for ResolveAutoFlexBasisAndMinSize().)
    */
   nscoord MeasureFlexItemContentBSize(nsPresContext* aPresContext,
                                       FlexItem& aFlexItem,
                                       bool aForceBResizeForMeasuringReflow,
+                                      bool aHasLineClampEllipsis,
                                       const ReflowInput& aParentReflowInput);
 
   /**
    * This method resolves an "auto" flex-basis and/or min-main-size value
    * on aFlexItem, if needed.
    * (Helper for GenerateFlexItemForChild().)
    */
   void ResolveAutoFlexBasisAndMinSize(nsPresContext* aPresContext,
                                       FlexItem& aFlexItem,
                                       const ReflowInput& aItemReflowInput,
-                                      const FlexboxAxisTracker& aAxisTracker);
+                                      const FlexboxAxisTracker& aAxisTracker,
+                                      bool aHasLineClampEllipsis);
 
   /**
    * Returns true if "this" is the nsFlexContainerFrame for a -moz-box or
    * a -moz-inline-box -- these boxes have special behavior for flex items with
    * "visibility:collapse".
    *
    * @param aFlexStyleDisp This frame's StyleDisplay(). (Just an optimization to
    *                       avoid repeated lookup; some callers already have it.)
@@ -353,17 +364,17 @@ class nsFlexContainerFrame final : publi
    * (Absolutely positioned children of a flex container are *not* flex items.)
    */
   void GenerateFlexLines(nsPresContext* aPresContext,
                          const ReflowInput& aReflowInput,
                          nscoord aContentBoxMainSize,
                          nscoord aAvailableBSizeForContent,
                          const nsTArray<StrutInfo>& aStruts,
                          const FlexboxAxisTracker& aAxisTracker,
-                         nscoord aMainGapSize,
+                         nscoord aMainGapSize, bool aHasLineClampEllipsis,
                          nsTArray<nsIFrame*>& aPlaceholders,
                          mozilla::LinkedList<FlexLine>& aLines);
 
   nscoord GetMainSizeFromReflowInput(const ReflowInput& aReflowInput,
                                      const FlexboxAxisTracker& aAxisTracker);
 
   nscoord ComputeCrossSize(const ReflowInput& aReflowInput,
                            const FlexboxAxisTracker& aAxisTracker,
@@ -406,17 +417,17 @@ class nsFlexContainerFrame final : publi
    *                        be placed. (pre-relative positioning)
    * @param aContainerSize  The flex container's size (required by some methods
    *                        that we call, to interpret aFramePos correctly).
    */
   void ReflowFlexItem(nsPresContext* aPresContext,
                       const FlexboxAxisTracker& aAxisTracker,
                       const ReflowInput& aReflowInput, const FlexItem& aItem,
                       mozilla::LogicalPoint& aFramePos,
-                      const nsSize& aContainerSize);
+                      const nsSize& aContainerSize, bool aHasLineClampEllipsis);
 
   /**
    * Helper-function to perform a "dummy reflow" on all our nsPlaceholderFrame
    * children, at the container's content-box origin.
    *
    * This doesn't actually represent the static position of the placeholders'
    * out-of-flow (OOF) frames -- we can't compute that until we've reflowed the
    * OOF, because (depending on the CSS Align properties) the static position
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -2498,25 +2498,25 @@ nsFloatManager::ShapeInfo::CreateShapeBo
 /* static */ UniquePtr<nsFloatManager::ShapeInfo>
 nsFloatManager::ShapeInfo::CreateBasicShape(const StyleBasicShape& aBasicShape,
                                             nscoord aShapeMargin,
                                             nsIFrame* const aFrame,
                                             const LogicalRect& aShapeBoxRect,
                                             const LogicalRect& aMarginRect,
                                             WritingMode aWM,
                                             const nsSize& aContainerSize) {
-  switch (aBasicShape.GetShapeType()) {
-    case StyleBasicShapeType::Polygon:
+  switch (aBasicShape.tag) {
+    case StyleBasicShape::Tag::Polygon:
       return CreatePolygon(aBasicShape, aShapeMargin, aFrame, aShapeBoxRect,
                            aMarginRect, aWM, aContainerSize);
-    case StyleBasicShapeType::Circle:
-    case StyleBasicShapeType::Ellipse:
+    case StyleBasicShape::Tag::Circle:
+    case StyleBasicShape::Tag::Ellipse:
       return CreateCircleOrEllipse(aBasicShape, aShapeMargin, aFrame,
                                    aShapeBoxRect, aWM, aContainerSize);
-    case StyleBasicShapeType::Inset:
+    case StyleBasicShape::Tag::Inset:
       return CreateInset(aBasicShape, aShapeMargin, aFrame, aShapeBoxRect, aWM,
                          aContainerSize);
   }
   return nullptr;
 }
 
 /* static */ UniquePtr<nsFloatManager::ShapeInfo>
 nsFloatManager::ShapeInfo::CreateInset(const StyleBasicShape& aBasicShape,
@@ -2594,27 +2594,26 @@ nsFloatManager::ShapeInfo::CreateCircleO
       aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
   nsPoint physicalCenter = ShapeUtils::ComputeCircleOrEllipseCenter(
       aBasicShape, physicalShapeBoxRect);
   nsPoint logicalCenter =
       ConvertToFloatLogical(physicalCenter, aWM, aContainerSize);
 
   // Compute the circle or ellipse radii.
   nsSize radii;
-  StyleBasicShapeType type = aBasicShape.GetShapeType();
-  if (type == StyleBasicShapeType::Circle) {
+  if (aBasicShape.IsCircle()) {
     nscoord radius = ShapeUtils::ComputeCircleRadius(
         aBasicShape, physicalCenter, physicalShapeBoxRect);
     // Circles can use the three argument, math constructor for
     // EllipseShapeInfo.
     radii = nsSize(radius, radius);
     return MakeUnique<EllipseShapeInfo>(logicalCenter, radii, aShapeMargin);
   }
 
-  MOZ_ASSERT(type == StyleBasicShapeType::Ellipse);
+  MOZ_ASSERT(aBasicShape.IsEllipse());
   nsSize physicalRadii = ShapeUtils::ComputeEllipseRadii(
       aBasicShape, physicalCenter, physicalShapeBoxRect);
   LogicalSize logicalRadii(aWM, physicalRadii);
   radii = nsSize(logicalRadii.ISize(aWM), logicalRadii.BSize(aWM));
 
   // If radii are close to the same value, or if aShapeMargin is small
   // enough (as specified in css pixels), then we can use the three argument
   // constructor for EllipseShapeInfo, which uses math for a more efficient
--- a/layout/generic/nsFloatManager.h
+++ b/layout/generic/nsFloatManager.h
@@ -19,17 +19,16 @@
 #include "nsPoint.h"
 #include "nsTArray.h"
 
 class nsIFrame;
 class nsPresContext;
 namespace mozilla {
 struct ReflowInput;
 class PresShell;
-class StyleBasicShape;
 }  // namespace mozilla
 
 enum class nsFlowAreaRectFlags : uint32_t {
   NoFlags = 0,
   HasFloats = 1 << 0,
   MayWiden = 1 << 1
 };
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsFlowAreaRectFlags)
--- a/layout/generic/nsFrameStateBits.h
+++ b/layout/generic/nsFrameStateBits.h
@@ -345,16 +345,20 @@ FRAME_STATE_BIT(FlexContainer, 20,
 FRAME_STATE_BIT(FlexContainer, 21, NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX)
 
 // True iff computed flex values should be generated on the next reflow
 FRAME_STATE_BIT(FlexContainer, 22, NS_STATE_FLEX_GENERATE_COMPUTED_VALUES)
 
 // True if the container has no flex items; may lie if there is a pending reflow
 FRAME_STATE_BIT(FlexContainer, 23, NS_STATE_FLEX_SYNTHESIZE_BASELINE)
 
+// True if any flex item in the container has a line with a
+// -webkit-line-ellipsis marker.
+FRAME_STATE_BIT(FlexContainer, 24, NS_STATE_FLEX_HAS_LINE_CLAMP_ELLIPSIS)
+
 // == Frame state bits that apply to grid container frames ====================
 
 FRAME_STATE_GROUP(GridContainer, nsGridContainerFrame)
 
 // True iff the normal flow children are already in CSS 'order' in the
 // order they occur in the child frame list.
 FRAME_STATE_BIT(GridContainer, 20,
                 NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
@@ -563,16 +567,22 @@ FRAME_STATE_BIT(Block, 28, NS_BLOCK_CLIP
 FRAME_STATE_BIT(Block, 29, NS_BLOCK_HAS_FIRST_LETTER_STYLE)
 
 // NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER and NS_BLOCK_FRAME_HAS_INSIDE_MARKER
 // means the block has an associated ::marker frame, they are mutually
 // exclusive.
 FRAME_STATE_BIT(Block, 30, NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER)
 FRAME_STATE_BIT(Block, 31, NS_BLOCK_FRAME_HAS_INSIDE_MARKER)
 
+// NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS indicates that exactly one line in this
+// block has the LineClampEllipsis flag set, and that such a line must be found
+// and have that flag cleared when reflowing this element's nearest legacy box
+// container.
+FRAME_STATE_BIT(Block, 60, NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS)
+
 // This block has had a child marked dirty, so before we reflow we need
 // to look through the lines to find any such children and mark
 // appropriate lines dirty.
 FRAME_STATE_BIT(Block, 61, NS_BLOCK_LOOK_FOR_DIRTY_FRAMES)
 
 // Are our cached intrinsic widths intrinsic widths for font size
 // inflation?  i.e., what was the current state of
 // GetPresContext()->mInflationDisabledForShrinkWrap at the time they
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1180,16 +1180,17 @@ a11y::AccType nsHTMLScrollFrame::Accessi
 }
 #endif
 
 NS_QUERYFRAME_HEAD(nsHTMLScrollFrame)
   NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
   NS_QUERYFRAME_ENTRY(nsIScrollableFrame)
   NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
   NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
+  NS_QUERYFRAME_ENTRY(nsHTMLScrollFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 
 //----------nsXULScrollFrame-------------------------------------------
 
 nsXULScrollFrame* NS_NewXULScrollFrame(PresShell* aPresShell,
                                        ComputedStyle* aStyle, bool aIsRoot,
                                        bool aClipAllDescendants) {
   return new (aPresShell) nsXULScrollFrame(aStyle, aPresShell->GetPresContext(),
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -466,18 +466,19 @@ class ScrollFrameHelper : public nsIRefl
   void MarkNotRecentlyScrolled();
   nsExpirationState* GetExpirationState() { return &mActivityExpirationState; }
 
   void SetTransformingByAPZ(bool aTransforming) {
     if (mTransformingByAPZ && !aTransforming) {
       PostScrollEndEvent();
     }
     mTransformingByAPZ = aTransforming;
-    if (!mozilla::css::TextOverflow::HasClippedOverflow(mOuter)) {
-      // If the block has some text-overflow stuff we should kick off a paint
+    if (!mozilla::css::TextOverflow::HasClippedTextOverflow(mOuter) ||
+        mozilla::css::TextOverflow::HasBlockEllipsis(mScrolledFrame)) {
+      // If the block has some overflow marker stuff we should kick off a paint
       // because we have special behaviour for it when APZ scrolling is active.
       mOuter->SchedulePaint();
     }
   }
   bool IsTransformingByAPZ() const { return mTransformingByAPZ; }
   void SetScrollableByAPZ(bool aScrollable);
   void SetZoomableByAPZ(bool aZoomable);
   void SetHasOutOfFlowContentInsideFilter();
--- a/layout/generic/nsLineBox.h
+++ b/layout/generic/nsLineBox.h
@@ -262,16 +262,21 @@ class nsLineBox final : public nsLineLin
   }
   bool HasMarker() const { return mFlags.mHasMarker; }
 
   // mHadFloatPushed bit
   void SetHadFloatPushed() { mFlags.mHadFloatPushed = true; }
   void ClearHadFloatPushed() { mFlags.mHadFloatPushed = false; }
   bool HadFloatPushed() const { return mFlags.mHadFloatPushed; }
 
+  // mHasLineClampEllipsis bit
+  void SetHasLineClampEllipsis() { mFlags.mHasLineClampEllipsis = true; }
+  void ClearHasLineClampEllipsis() { mFlags.mHasLineClampEllipsis = false; }
+  bool HasLineClampEllipsis() const { return mFlags.mHasLineClampEllipsis; }
+
  private:
   // Add a hash table for fast lookup when the line has more frames than this.
   static const uint32_t kMinChildCountForHashtable = 200;
 
   /**
    * Take ownership of aFromLine's hash table and remove the frames that
    * stay on aFromLine from it, i.e. aFromLineNewCount frames starting with
    * mFirstChild.  This method is used to optimize moving a large number
@@ -603,16 +608,23 @@ class nsLineBox final : public nsLineLin
     bool mEmptyCacheState : 1;
     // mHasMarker indicates that this is an inline line whose block's
     // ::marker is adjacent to this line and non-empty.
     bool mHasMarker : 1;
     // Indicates that this line *may* have a placeholder for a float
     // that was pushed to a later column or page.
     bool mHadFloatPushed : 1;
     bool mHasHashedFrames : 1;
+    // Indicates that this line is the one identified by an ancestor block
+    // with -webkit-line-clamp on its legacy flex container, and that subsequent
+    // lines under that block are "clamped" away, and therefore we need to
+    // render a 'text-overflow: ellipsis'-like marker in this line.  At most one
+    // line in the set of lines found by LineClampLineIterator for a given
+    // block will have this flag set.
+    bool mHasLineClampEllipsis : 1;
     StyleClear mBreakType;
   };
 
   struct ExtraData {
     explicit ExtraData(const nsRect& aBounds)
         : mOverflowAreas(aBounds, aBounds) {}
     nsOverflowAreas mOverflowAreas;
   };
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -907,17 +907,18 @@ void nsLineLayout::ReflowFrame(nsIFrame*
             // there's something like a ::marker before or what not. We actually
             // need to place them now, because they're pretty nasty and they
             // create continuations that are in flow and not a kid of the
             // previous continuation's parent. We don't want the deferred reflow
             // of the letter frame to kill a continuation after we've stored it
             // in the line layout data structures. See bug 1490281 to fix the
             // underlying issue. When that's fixed this check should be removed.
             !outOfFlowFrame->IsLetterFrame() &&
-            !GetOutermostLineLayout()->mBlockRI->mFlags.mCanHaveTextOverflow) {
+            !GetOutermostLineLayout()
+                 ->mBlockRI->mFlags.mCanHaveOverflowMarkers) {
           // We'll do this at the next break opportunity.
           RecordNoWrapFloat(outOfFlowFrame);
         } else {
           TryToPlaceFloat(outOfFlowFrame);
         }
       }
     } else if (isText) {
       // Note non-empty text-frames for inline frame compatibility hackery
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -9589,38 +9589,38 @@ static Maybe<wr::WrClipId> CreateSimpleC
 
   auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
   const nsRect refBox =
       nsLayoutUtils::ComputeGeometryBox(frame, clipPath.GetReferenceBox());
 
   AutoTArray<wr::ComplexClipRegion, 1> clipRegions;
 
   wr::LayoutRect rect;
-  switch (shape.GetShapeType()) {
-    case StyleBasicShapeType::Inset: {
+  switch (shape.tag) {
+    case StyleBasicShape::Tag::Inset: {
       const nsRect insetRect = ShapeUtils::ComputeInsetRect(shape, refBox) +
                                aDisplayItem.ToReferenceFrame();
 
       nscoord radii[8] = {0};
 
       if (ShapeUtils::ComputeInsetRadii(shape, insetRect, refBox, radii)) {
         clipRegions.AppendElement(
             wr::ToComplexClipRegion(insetRect, radii, appUnitsPerDevPixel));
       }
 
       rect = wr::ToRoundedLayoutRect(
           LayoutDeviceRect::FromAppUnits(insetRect, appUnitsPerDevPixel));
       break;
     }
-    case StyleBasicShapeType::Ellipse:
-    case StyleBasicShapeType::Circle: {
+    case StyleBasicShape::Tag::Ellipse:
+    case StyleBasicShape::Tag::Circle: {
       nsPoint center = ShapeUtils::ComputeCircleOrEllipseCenter(shape, refBox);
 
       nsSize radii;
-      if (shape.GetShapeType() == StyleBasicShapeType::Ellipse) {
+      if (shape.IsEllipse()) {
         radii = ShapeUtils::ComputeEllipseRadii(shape, center, refBox);
       } else {
         nscoord radius = ShapeUtils::ComputeCircleRadius(shape, center, refBox);
         radii = {radius, radius};
       }
 
       nsRect ellipseRect(aDisplayItem.ToReferenceFrame() + center -
                              nsPoint(radii.width, radii.height),
--- a/layout/reftests/w3c-css/submitted/contain/reftest.list
+++ b/layout/reftests/w3c-css/submitted/contain/reftest.list
@@ -50,11 +50,11 @@ fuzzy-if(webrender&&winWidget,0-24,0-2) 
 == contain-layout-suppress-baseline-001.html contain-layout-suppress-baseline-001-ref.html
 fails == contain-layout-suppress-baseline-002.html contain-layout-suppress-baseline-002-ref.html # bug 1508441
 
 # The following lines are duplicates of other lines from further up in this
 # manifest. They're listed again here so we can re-run these tests with
 # column-span enabled. These lines can be removed once the pref becomes
 # default-enabled (Bug 1426010).
 default-preferences pref(layout.css.column-span.enabled,true)
-fails == contain-size-multicol-002.html contain-size-multicol-002-ref.html # Bug 1494100
-fails == contain-size-multicol-003.html contain-size-multicol-003-ref.html # Bug 1494100
+== contain-size-multicol-002.html contain-size-multicol-002-ref.html
+== contain-size-multicol-003.html contain-size-multicol-003-ref.html
 default-preferences
--- a/layout/style/FontFace.cpp
+++ b/layout/style/FontFace.cpp
@@ -173,17 +173,18 @@ already_AddRefed<FontFace> FontFace::Con
   obj->InitializeSource(aSource);
   return obj.forget();
 }
 
 void FontFace::InitializeSource(
     const StringOrArrayBufferOrArrayBufferView& aSource) {
   if (aSource.IsString()) {
     IgnoredErrorResult rv;
-    if (!SetDescriptor(eCSSFontDesc_Src, aSource.GetAsString(), rv)) {
+    SetDescriptor(eCSSFontDesc_Src, aSource.GetAsString(), rv);
+    if (rv.Failed()) {
       Reject(NS_ERROR_DOM_SYNTAX_ERR);
 
       SetStatus(FontFaceLoadStatus::Error);
       return;
     }
 
     mSourceType = eSourceType_URLs;
     return;
@@ -205,57 +206,67 @@ void FontFace::InitializeSource(
 
 void FontFace::GetFamily(nsString& aResult) {
   mFontFaceSet->FlushUserFontSet();
   GetDesc(eCSSFontDesc_Family, aResult);
 }
 
 void FontFace::SetFamily(const nsAString& aValue, ErrorResult& aRv) {
   mFontFaceSet->FlushUserFontSet();
-  SetDescriptor(eCSSFontDesc_Family, aValue, aRv);
+  if (SetDescriptor(eCSSFontDesc_Family, aValue, aRv)) {
+    DescriptorUpdated();
+  }
 }
 
 void FontFace::GetStyle(nsString& aResult) {
   mFontFaceSet->FlushUserFontSet();
   GetDesc(eCSSFontDesc_Style, aResult);
 }
 
 void FontFace::SetStyle(const nsAString& aValue, ErrorResult& aRv) {
   mFontFaceSet->FlushUserFontSet();
-  SetDescriptor(eCSSFontDesc_Style, aValue, aRv);
+  if (SetDescriptor(eCSSFontDesc_Style, aValue, aRv)) {
+    DescriptorUpdated();
+  }
 }
 
 void FontFace::GetWeight(nsString& aResult) {
   mFontFaceSet->FlushUserFontSet();
   GetDesc(eCSSFontDesc_Weight, aResult);
 }
 
 void FontFace::SetWeight(const nsAString& aValue, ErrorResult& aRv) {
   mFontFaceSet->FlushUserFontSet();
-  SetDescriptor(eCSSFontDesc_Weight, aValue, aRv);
+  if (SetDescriptor(eCSSFontDesc_Weight, aValue, aRv)) {
+    DescriptorUpdated();
+  }
 }
 
 void FontFace::GetStretch(nsString& aResult) {
   mFontFaceSet->FlushUserFontSet();
   GetDesc(eCSSFontDesc_Stretch, aResult);
 }
 
 void FontFace::SetStretch(const nsAString& aValue, ErrorResult& aRv) {
   mFontFaceSet->FlushUserFontSet();
-  SetDescriptor(eCSSFontDesc_Stretch, aValue, aRv);
+  if (SetDescriptor(eCSSFontDesc_Stretch, aValue, aRv)) {
+    DescriptorUpdated();
+  }
 }
 
 void FontFace::GetUnicodeRange(nsString& aResult) {
   mFontFaceSet->FlushUserFontSet();
   GetDesc(eCSSFontDesc_UnicodeRange, aResult);
 }
 
 void FontFace::SetUnicodeRange(const nsAString& aValue, ErrorResult& aRv) {
   mFontFaceSet->FlushUserFontSet();
-  SetDescriptor(eCSSFontDesc_UnicodeRange, aValue, aRv);
+  if (SetDescriptor(eCSSFontDesc_UnicodeRange, aValue, aRv)) {
+    DescriptorUpdated();
+  }
 }
 
 void FontFace::GetVariant(nsString& aResult) {
   mFontFaceSet->FlushUserFontSet();
 
   // XXX Just expose the font-variant descriptor as "normal" until we
   // support it properly (bug 1055385).
   aResult.AssignLiteral("normal");
@@ -270,37 +281,65 @@ void FontFace::SetVariant(const nsAStrin
 
 void FontFace::GetFeatureSettings(nsString& aResult) {
   mFontFaceSet->FlushUserFontSet();
   GetDesc(eCSSFontDesc_FontFeatureSettings, aResult);
 }
 
 void FontFace::SetFeatureSettings(const nsAString& aValue, ErrorResult& aRv) {
   mFontFaceSet->FlushUserFontSet();
-  SetDescriptor(eCSSFontDesc_FontFeatureSettings, aValue, aRv);
+  if (SetDescriptor(eCSSFontDesc_FontFeatureSettings, aValue, aRv)) {
+    DescriptorUpdated();
+  }
 }
 
 void FontFace::GetVariationSettings(nsString& aResult) {
   mFontFaceSet->FlushUserFontSet();
   GetDesc(eCSSFontDesc_FontVariationSettings, aResult);
 }
 
 void FontFace::SetVariationSettings(const nsAString& aValue, ErrorResult& aRv) {
   mFontFaceSet->FlushUserFontSet();
-  SetDescriptor(eCSSFontDesc_FontVariationSettings, aValue, aRv);
+  if (SetDescriptor(eCSSFontDesc_FontVariationSettings, aValue, aRv)) {
+    DescriptorUpdated();
+  }
 }
 
 void FontFace::GetDisplay(nsString& aResult) {
   mFontFaceSet->FlushUserFontSet();
   GetDesc(eCSSFontDesc_Display, aResult);
 }
 
 void FontFace::SetDisplay(const nsAString& aValue, ErrorResult& aRv) {
   mFontFaceSet->FlushUserFontSet();
-  SetDescriptor(eCSSFontDesc_Display, aValue, aRv);
+  if (SetDescriptor(eCSSFontDesc_Display, aValue, aRv)) {
+    DescriptorUpdated();
+  }
+}
+
+void FontFace::DescriptorUpdated()
+{
+  // If we haven't yet initialized mUserFontEntry, no need to do anything here;
+  // we'll respect the updated descriptor when the time comes to create it.
+  if (!mUserFontEntry) {
+    return;
+  }
+
+  // Behind the scenes, this will actually update the existing entry and return
+  // it, rather than create a new one.
+  RefPtr<gfxUserFontEntry> newEntry =
+    mFontFaceSet->FindOrCreateUserFontEntryFromFontFace(this);
+  SetUserFontEntry(newEntry);
+
+  if (mInFontFaceSet) {
+    mFontFaceSet->MarkUserFontSetDirty();
+  }
+  for (auto& set : mOtherFontFaceSets) {
+    set->MarkUserFontSetDirty();
+  }
 }
 
 FontFaceLoadStatus FontFace::Status() { return mStatus; }
 
 Promise* FontFace::Load(ErrorResult& aRv) {
   MOZ_ASSERT(NS_IsMainThread());
 
   mFontFaceSet->FlushUserFontSet();
@@ -442,66 +481,77 @@ already_AddRefed<URLExtraData> FontFace:
 
   // We pass RP_Unset when creating URLExtraData object here because it's not
   // going to result to change referer policy in a resource request.
   RefPtr<URLExtraData> url =
       new URLExtraData(base, docURI, principal, net::RP_Unset);
   return url.forget();
 }
 
+// Boolean result indicates whether the value of the descriptor was actually
+// changed.
 bool FontFace::SetDescriptor(nsCSSFontDesc aFontDesc, const nsAString& aValue,
                              ErrorResult& aRv) {
   // FIXME We probably don't need to distinguish between this anymore
   // since we have common backend now.
   NS_ASSERTION(!HasRule(), "we don't handle rule backed FontFace objects yet");
   if (HasRule()) {
     return false;
   }
 
   // FIXME(heycam): Should not allow modification of FontFaces that are
   // CSS-connected and whose rule is read only.
 
   NS_ConvertUTF16toUTF8 value(aValue);
   RefPtr<URLExtraData> url = GetURLExtraData();
-  if (!Servo_FontFaceRule_SetDescriptor(GetData(), aFontDesc, &value, url)) {
+  bool changed;
+  if (!Servo_FontFaceRule_SetDescriptor(GetData(), aFontDesc, &value, url,
+                                        &changed)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return false;
   }
 
+  if (!changed) {
+    return false;
+  }
+
   if (aFontDesc == eCSSFontDesc_UnicodeRange) {
     mUnicodeRangeDirty = true;
   }
 
-  // XXX Setting descriptors doesn't actually have any effect on FontFace
-  // objects that have started loading or have already been loaded.
   return true;
 }
 
 bool FontFace::SetDescriptors(const nsAString& aFamily,
                               const FontFaceDescriptors& aDescriptors) {
   MOZ_ASSERT(!HasRule());
   MOZ_ASSERT(!mDescriptors);
 
-  IgnoredErrorResult rv;
   mDescriptors = Servo_FontFaceRule_CreateEmpty().Consume();
 
+  // Helper to call SetDescriptor and return true on success, false on failure.
+  auto setDesc = [=](nsCSSFontDesc aDesc, const nsAString& aVal) -> bool {
+    IgnoredErrorResult rv;
+    SetDescriptor(aDesc, aVal, rv);
+    return !rv.Failed();
+  };
+
   // Parse all of the mDescriptors in aInitializer, which are the values
   // we got from the JS constructor.
-  if (!SetDescriptor(eCSSFontDesc_Family, aFamily, rv) ||
-      !SetDescriptor(eCSSFontDesc_Style, aDescriptors.mStyle, rv) ||
-      !SetDescriptor(eCSSFontDesc_Weight, aDescriptors.mWeight, rv) ||
-      !SetDescriptor(eCSSFontDesc_Stretch, aDescriptors.mStretch, rv) ||
-      !SetDescriptor(eCSSFontDesc_UnicodeRange, aDescriptors.mUnicodeRange,
-                     rv) ||
-      !SetDescriptor(eCSSFontDesc_FontFeatureSettings,
-                     aDescriptors.mFeatureSettings, rv) ||
+  if (!setDesc(eCSSFontDesc_Family, aFamily) ||
+      !setDesc(eCSSFontDesc_Style, aDescriptors.mStyle) ||
+      !setDesc(eCSSFontDesc_Weight, aDescriptors.mWeight) ||
+      !setDesc(eCSSFontDesc_Stretch, aDescriptors.mStretch) ||
+      !setDesc(eCSSFontDesc_UnicodeRange, aDescriptors.mUnicodeRange) ||
+      !setDesc(eCSSFontDesc_FontFeatureSettings,
+               aDescriptors.mFeatureSettings) ||
       (StaticPrefs::layout_css_font_variations_enabled() &&
-       !SetDescriptor(eCSSFontDesc_FontVariationSettings,
-                      aDescriptors.mVariationSettings, rv)) ||
-      !SetDescriptor(eCSSFontDesc_Display, aDescriptors.mDisplay, rv)) {
+       !setDesc(eCSSFontDesc_FontVariationSettings,
+                aDescriptors.mVariationSettings)) ||
+      !setDesc(eCSSFontDesc_Display, aDescriptors.mDisplay)) {
     // XXX Handle font-variant once we support it (bug 1055385).
 
     // If any of the descriptors failed to parse, none of them should be set
     // on the FontFace.
     mDescriptors = Servo_FontFaceRule_CreateEmpty().Consume();
 
     Reject(NS_ERROR_DOM_SYNTAX_ERR);
 
--- a/layout/style/FontFace.h
+++ b/layout/style/FontFace.h
@@ -183,28 +183,36 @@ class FontFace final : public nsISupport
   ~FontFace();
 
   void InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource);
 
   // Helper function for Load.
   void DoLoad();
 
   // Helper function for the descriptor setter methods.
-  // Returns whether it successfully sets the descriptor.
+  // Returns true if the descriptor was modified, false if descriptor is
+  // unchanged (which may not be an error: check aRv for actual failure).
   bool SetDescriptor(nsCSSFontDesc aFontDesc, const nsAString& aValue,
                      mozilla::ErrorResult& aRv);
 
   /**
    * Sets all of the descriptor values in mDescriptors using values passed
    * to the JS constructor.
+   * Returns true on success, false if parsing any descriptor failed.
    */
   bool SetDescriptors(const nsAString& aFamily,
                       const FontFaceDescriptors& aDescriptors);
 
   /**
+   * Called when a descriptor has been modified, so font-face sets can
+   * be told to refresh.
+   */
+  void DescriptorUpdated();
+
+  /**
    * Sets the current loading status.
    */
   void SetStatus(mozilla::dom::FontFaceLoadStatus aStatus);
 
   void GetDesc(nsCSSFontDesc aDescID, nsString& aResult) const;
 
   already_AddRefed<URLExtraData> GetURLExtraData() const;
 
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -1025,22 +1025,47 @@ FontFaceSet::FindOrCreateUserFontEntryFr
   if (Maybe<StyleFontLanguageOverride> descriptor =
           aFontFace->GetFontLanguageOverride()) {
     languageOverride = descriptor->_0;
   }
 
   // set up unicode-range
   gfxCharacterMap* unicodeRanges = aFontFace->GetUnicodeRangeAsCharacterMap();
 
+  RefPtr<gfxUserFontEntry> existingEntry = aFontFace->GetUserFontEntry();
+  if (existingEntry) {
+    // aFontFace already has a user font entry, so we update its attributes
+    // rather than creating a new one.
+    existingEntry->UpdateAttributes(weight, stretch, italicStyle,
+                                    featureSettings, variationSettings,
+                                    languageOverride, unicodeRanges,
+                                    fontDisplay, rangeFlags);
+    // If the family name has changed, remove the entry from its current family
+    // and clear the mFamilyName field so it can be reset when added to a new
+    // family.
+    if (!existingEntry->mFamilyName.IsEmpty() &&
+        existingEntry->mFamilyName != aFamilyName) {
+      gfxUserFontFamily* family =
+        set->GetUserFontSet()->LookupFamily(existingEntry->mFamilyName);
+      if (family) {
+        family->RemoveFontEntry(existingEntry);
+      }
+      existingEntry->mFamilyName.Truncate(0);
+    }
+    return existingEntry.forget();
+  }
+
   // set up src array
   nsTArray<gfxFontFaceSrc> srcArray;
 
   if (aFontFace->HasFontData()) {
     gfxFontFaceSrc* face = srcArray.AppendElement();
-    if (!face) return nullptr;
+    if (!face) {
+      return nullptr;
+    }
 
     face->mSourceType = gfxFontFaceSrc::eSourceType_Buffer;
     face->mBuffer = aFontFace->CreateBufferSource();
     face->mReferrerPolicy = mozilla::net::RP_Unset;
   } else {
     AutoTArray<StyleFontFaceSourceListComponent, 8> sourceListComponents;
     aFontFace->GetSources(sourceListComponents);
     size_t len = sourceListComponents.Length();
--- a/layout/style/FontFaceSet.h
+++ b/layout/style/FontFaceSet.h
@@ -194,16 +194,18 @@ class FontFaceSet final : public DOMEven
   already_AddRefed<mozilla::dom::FontFaceSetIterator> Values();
   MOZ_CAN_RUN_SCRIPT
   void ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback,
                JS::Handle<JS::Value> aThisArg, mozilla::ErrorResult& aRv);
 
   // For ServoStyleSet to know ahead of time whether a font is loadable.
   void CacheFontLoadability();
 
+  void MarkUserFontSetDirty();
+
  private:
   ~FontFaceSet();
 
   /**
    * Returns whether the given FontFace is currently "in" the FontFaceSet.
    */
   bool HasAvailableFontFace(FontFace* aFontFace);
 
@@ -273,17 +275,16 @@ class FontFaceSet final : public DOMEven
   bool IsFontLoadAllowed(const gfxFontFaceSrc& aSrc);
 
   void DispatchFontLoadViolations(nsTArray<nsCOMPtr<nsIRunnable>>& aViolations);
   nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
                             const gfxFontFaceSrc* aFontFaceSrc,
                             uint8_t*& aBuffer, uint32_t& aBufferLength);
   nsresult LogMessage(gfxUserFontEntry* aUserFontEntry, const char* aMessage,
                       uint32_t aFlags, nsresult aStatus);
-  void MarkUserFontSetDirty();
 
   void InsertRuleFontFace(FontFace* aFontFace, StyleOrigin aOrigin,
                           nsTArray<FontFaceRecord>& aOldRecords,
                           bool& aFontSetModified);
   void InsertNonRuleFontFace(FontFace* aFontFace, bool& aFontSetModified);
 
 #ifdef DEBUG
   bool HasRuleFontFace(FontFace* aFontFace);
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -1555,21 +1555,16 @@ void Gecko_DestroyShapeSource(StyleShape
   aShape->~StyleShapeSource();
 }
 
 void Gecko_StyleShapeSource_SetURLValue(StyleShapeSource* aShape,
                                         URLValue* aURL) {
   aShape->SetURL(*aURL);
 }
 
-void Gecko_NewBasicShape(StyleShapeSource* aShape, StyleBasicShapeType aType) {
-  aShape->SetBasicShape(MakeUnique<StyleBasicShape>(aType),
-                        StyleGeometryBox::NoBox);
-}
-
 void Gecko_NewShapeImage(StyleShapeSource* aShape) {
   aShape->SetShapeImage(MakeUnique<nsStyleImage>());
 }
 
 void Gecko_NewStyleSVGPath(StyleShapeSource* aShape) {
   MOZ_ASSERT(aShape);
   aShape->SetPath(MakeUnique<StyleSVGPath>());
 }
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -522,19 +522,16 @@ void Gecko_ResetStyleCoord(nsStyleUnit* 
 void Gecko_SetStyleCoordCalcValue(nsStyleUnit* unit, nsStyleUnion* value,
                                   nsStyleCoord::CalcValue calc);
 
 void Gecko_CopyShapeSourceFrom(mozilla::StyleShapeSource* dst,
                                const mozilla::StyleShapeSource* src);
 
 void Gecko_DestroyShapeSource(mozilla::StyleShapeSource* shape);
 
-void Gecko_NewBasicShape(mozilla::StyleShapeSource* shape,
-                         mozilla::StyleBasicShapeType type);
-
 void Gecko_NewShapeImage(mozilla::StyleShapeSource* shape);
 
 void Gecko_StyleShapeSource_SetURLValue(mozilla::StyleShapeSource* shape,
                                         mozilla::css::URLValue* uri);
 
 void Gecko_NewStyleSVGPath(mozilla::StyleShapeSource* shape);
 
 void Gecko_SetStyleMotion(mozilla::UniquePtr<mozilla::StyleMotion>* aMotion,
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -468,16 +468,17 @@ cbindgen-types = [
     { gecko = "StyleGenericColor", servo = "values::generics::color::Color" },
     { gecko = "StyleGenericColorOrAuto", servo = "values::generics::color::ColorOrAuto" },
     { gecko = "StyleGenericScrollbarColor", servo = "values::generics::ui::ScrollbarColor" },
     { gecko = "StyleRGBA", servo = "cssparser::RGBA" },
     { gecko = "StyleOrigin", servo = "stylesheets::Origin" },
     { gecko = "StyleGenericGradientItem", servo = "values::generics::image::GradientItem" },
     { gecko = "StyleGenericVerticalAlign", servo = "values::generics::box_::VerticalAlign" },
     { gecko = "StyleVerticalAlignKeyword", servo = "values::generics::box_::VerticalAlignKeyword" },
+    { gecko = "StyleGenericBasicShape", servo = "values::generics::basic_shape::BasicShape" },
 ]
 
 mapped-generic-types = [
     { generic = true, gecko = "mozilla::RustCell", servo = "::std::cell::Cell" },
     { generic = false, gecko = "ServoNodeData", servo = "AtomicRefCell<ElementData>" },
     { generic = false, gecko = "mozilla::ServoWritingMode", servo = "::logical_geometry::WritingMode" },
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<::servo_arc::Arc<::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<::rule_tree::StrongRuleNode>" },
--- a/layout/style/ServoStyleConstsForwards.h
+++ b/layout/style/ServoStyleConstsForwards.h
@@ -13,16 +13,18 @@
 #endif
 
 #ifndef mozilla_ServoStyleConstsForwards_h
 #  define mozilla_ServoStyleConstsForwards_h
 
 #  include "nsColor.h"
 #  include "nsCoord.h"
 #  include "mozilla/AtomArray.h"
+#  include "mozilla/IntegerRange.h"
+#  include "mozilla/Span.h"
 #  include "Units.h"
 #  include "mozilla/gfx/Types.h"
 #  include "mozilla/MemoryReporting.h"
 #  include "mozilla/ServoTypes.h"
 #  include "mozilla/ServoBindingTypes.h"
 #  include "nsCSSPropertyID.h"
 #  include "nsCompatibility.h"
 
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -505,36 +505,54 @@ void StyleSheet::SetSourceURL(const nsAS
   mInner->mSourceURL = aSourceURL;
 }
 
 css::Rule* StyleSheet::GetDOMOwnerRule() const { return mOwnerRule; }
 
 uint32_t StyleSheet::InsertRule(const nsAString& aRule, uint32_t aIndex,
                                 nsIPrincipal& aSubjectPrincipal,
                                 ErrorResult& aRv) {
-  if (IsReadOnly()) {
-    return 0;
-  }
-  if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
+  if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
     return 0;
   }
   return InsertRuleInternal(aRule, aIndex, aRv);
 }
 
 void StyleSheet::DeleteRule(uint32_t aIndex, nsIPrincipal& aSubjectPrincipal,
                             ErrorResult& aRv) {
-  if (IsReadOnly()) {
-    return;
-  }
-  if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
+  if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
     return;
   }
   return DeleteRuleInternal(aIndex, aRv);
 }
 
+int32_t StyleSheet::AddRule(const nsAString& aSelector, const nsAString& aBlock,
+                            const Optional<uint32_t>& aIndex,
+                            nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
+  if (IsReadOnly() || !AreRulesAvailable(aSubjectPrincipal, aRv)) {
+    return -1;
+  }
+
+  nsAutoString rule;
+  rule.Append(aSelector);
+  rule.AppendLiteral(" { ");
+  if (!aBlock.IsEmpty()) {
+    rule.Append(aBlock);
+    rule.Append(' ');
+  }
+  rule.Append('}');
+
+  auto index =
+      aIndex.WasPassed() ? aIndex.Value() : GetCssRulesInternal()->Length();
+
+  InsertRuleInternal(rule, index, aRv);
+  // Always return -1.
+  return -1;
+}
+
 nsresult StyleSheet::DeleteRuleFromGroup(css::GroupRule* aGroup,
                                          uint32_t aIndex) {
   NS_ENSURE_ARG_POINTER(aGroup);
   NS_ASSERTION(IsComplete(), "No deleting from an incomplete sheet!");
   RefPtr<css::Rule> rule = aGroup->GetStyleRuleAt(aIndex);
   NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE);
 
   // check that the rule actually belongs to this sheet!
--- a/layout/style/StyleSheet.h
+++ b/layout/style/StyleSheet.h
@@ -341,16 +341,19 @@ class StyleSheet final : public nsICSSLo
   // called GetOwnerRule because that would be ambiguous with the ImportRule
   // version.
   css::Rule* GetDOMOwnerRule() const;
   dom::CSSRuleList* GetCssRules(nsIPrincipal& aSubjectPrincipal, ErrorResult&);
   uint32_t InsertRule(const nsAString& aRule, uint32_t aIndex,
                       nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
   void DeleteRule(uint32_t aIndex, nsIPrincipal& aSubjectPrincipal,
                   ErrorResult& aRv);
+  int32_t AddRule(const nsAString& aSelector, const nsAString& aBlock,
+                  const dom::Optional<uint32_t>& aIndex,
+                  nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
 
   // WebIDL miscellaneous bits
   inline dom::ParentObject GetParentObject() const;
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
 
   // Changes to sheets should be after a WillDirty call.
   void WillDirty();
 
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1545177.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+    <script>
+      function start () {
+        const font_1 = new FontFace('m', 'url()', {})
+        const font_2 = new FontFace('', '')
+        document.fonts.add(font_1)
+        font_1.family = 'f'
+        document.fonts.add(font_2)
+      }
+
+      window.addEventListener('load', start)
+    </script>
+</head>
+</html>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -294,12 +294,13 @@ load 1469076.html
 load 1475003.html
 load 1479681.html
 load 1488817.html
 load 1490012.html
 load 1502893.html
 load 1509989.html
 load 1514086.html
 pref(layout.css.moz-binding.content.enabled,false) load 1517319.html
+load 1533783.html
 load 1533891.html
-load 1533783.html
+pref(gfx.omta.background-color,true) load 1533968.html
 load 1541126.html
-pref(gfx.omta.background-color,true) load 1533968.html
+load 1545177.html
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -378,21 +378,16 @@ const KTableEntry nsCSSProps::kTextEmpha
 
 const KTableEntry nsCSSProps::kTextOverflowKTable[] = {
     {eCSSKeyword_clip, NS_STYLE_TEXT_OVERFLOW_CLIP},
     {eCSSKeyword_ellipsis, NS_STYLE_TEXT_OVERFLOW_ELLIPSIS},
     {eCSSKeyword_UNKNOWN, -1}};
 
 // keyword tables for SVG properties
 
-const KTableEntry nsCSSProps::kShapeRadiusKTable[] = {
-    {eCSSKeyword_closest_side, StyleShapeRadius::ClosestSide},
-    {eCSSKeyword_farthest_side, StyleShapeRadius::FarthestSide},
-    {eCSSKeyword_UNKNOWN, -1}};
-
 const KTableEntry nsCSSProps::kFilterFunctionKTable[] = {
     {eCSSKeyword_blur, NS_STYLE_FILTER_BLUR},
     {eCSSKeyword_brightness, NS_STYLE_FILTER_BRIGHTNESS},
     {eCSSKeyword_contrast, NS_STYLE_FILTER_CONTRAST},
     {eCSSKeyword_grayscale, NS_STYLE_FILTER_GRAYSCALE},
     {eCSSKeyword_invert, NS_STYLE_FILTER_INVERT},
     {eCSSKeyword_opacity, NS_STYLE_FILTER_OPACITY},
     {eCSSKeyword_saturate, NS_STYLE_FILTER_SATURATE},
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2560,150 +2560,16 @@ already_AddRefed<CSSValue> nsComputedDOM
 
   gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
       aSpecifiedTransform->mHead, refBox,
       float(mozilla::AppUnitsPerCSSPixel()));
 
   return MatrixToCSSValue(matrix);
 }
 
-void nsComputedDOMStyle::BoxValuesToString(
-    nsAString& aString, const nsTArray<nsStyleCoord>& aBoxValues,
-    bool aClampNegativeCalc) {
-  MOZ_ASSERT(aBoxValues.Length() == 4, "wrong number of box values");
-  nsAutoString value1, value2, value3, value4;
-  SetCssTextToCoord(value1, aBoxValues[0], aClampNegativeCalc);
-  SetCssTextToCoord(value2, aBoxValues[1], aClampNegativeCalc);
-  SetCssTextToCoord(value3, aBoxValues[2], aClampNegativeCalc);
-  SetCssTextToCoord(value4, aBoxValues[3], aClampNegativeCalc);
-
-  // nsROCSSPrimitiveValue do not have binary comparison operators.
-  // Compare string results instead.
-  aString.Append(value1);
-  if (value1 != value2 || value1 != value3 || value1 != value4) {
-    aString.Append(' ');
-    aString.Append(value2);
-    if (value1 != value3 || value2 != value4) {
-      aString.Append(' ');
-      aString.Append(value3);
-      if (value2 != value4) {
-        aString.Append(' ');
-        aString.Append(value4);
-      }
-    }
-  }
-}
-
-void nsComputedDOMStyle::BasicShapeRadiiToString(nsAString& aCssText,
-                                                 const BorderRadius& aCorners) {
-  Servo_SerializeBorderRadius(&aCorners, &aCssText);
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::CreatePrimitiveValueForBasicShape(
-    const UniquePtr<StyleBasicShape>& aStyleBasicShape) {
-  MOZ_ASSERT(aStyleBasicShape, "Expect a valid basic shape pointer!");
-
-  StyleBasicShapeType type = aStyleBasicShape->GetShapeType();
-  // Shape function name and opening parenthesis.
-  nsAutoString shapeFunctionString;
-  AppendASCIItoUTF16(
-      nsCSSKeywords::GetStringValue(aStyleBasicShape->GetShapeTypeName()),
-      shapeFunctionString);
-  shapeFunctionString.Append('(');
-  switch (type) {
-    case StyleBasicShapeType::Polygon: {
-      bool hasEvenOdd =
-          aStyleBasicShape->GetFillRule() == StyleFillRule::Evenodd;
-      if (hasEvenOdd) {
-        shapeFunctionString.AppendLiteral("evenodd");
-      }
-      for (size_t i = 0; i < aStyleBasicShape->Coordinates().Length(); i += 2) {
-        nsAutoString coordString;
-        if (i > 0 || hasEvenOdd) {
-          shapeFunctionString.AppendLiteral(", ");
-        }
-        SetCssTextToCoord(coordString, aStyleBasicShape->Coordinates()[i],
-                          false);
-        shapeFunctionString.Append(coordString);
-        shapeFunctionString.Append(' ');
-        SetCssTextToCoord(coordString, aStyleBasicShape->Coordinates()[i + 1],
-                          false);
-        shapeFunctionString.Append(coordString);
-      }
-      break;
-    }
-    case StyleBasicShapeType::Circle:
-    case StyleBasicShapeType::Ellipse: {
-      const nsTArray<nsStyleCoord>& radii = aStyleBasicShape->Coordinates();
-      MOZ_ASSERT(
-          radii.Length() == (type == StyleBasicShapeType::Circle ? 1 : 2),
-          "wrong number of radii");
-      for (size_t i = 0; i < radii.Length(); ++i) {
-        nsAutoString radius;
-        RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
-        bool clampNegativeCalc = true;
-        SetValueToCoord(value, radii[i], clampNegativeCalc, nullptr,
-                        nsCSSProps::kShapeRadiusKTable);
-        value->GetCssText(radius);
-        shapeFunctionString.Append(radius);
-        shapeFunctionString.Append(' ');
-      }
-      shapeFunctionString.AppendLiteral("at ");
-
-      RefPtr<nsDOMCSSValueList> position = GetROCSSValueList(false);
-      nsAutoString positionString;
-      SetValueToPosition(aStyleBasicShape->GetPosition(), position);
-      position->GetCssText(positionString);
-      shapeFunctionString.Append(positionString);
-      break;
-    }
-    case StyleBasicShapeType::Inset: {
-      BoxValuesToString(shapeFunctionString, aStyleBasicShape->Coordinates(),
-                        false);
-      if (aStyleBasicShape->HasRadius()) {
-        shapeFunctionString.AppendLiteral(" round ");
-        nsAutoString radiiString;
-        BasicShapeRadiiToString(radiiString, aStyleBasicShape->GetRadius());
-        shapeFunctionString.Append(radiiString);
-      }
-      break;
-    }
-    default:
-      MOZ_ASSERT_UNREACHABLE("unexpected type");
-  }
-  shapeFunctionString.Append(')');
-  RefPtr<nsROCSSPrimitiveValue> functionValue = new nsROCSSPrimitiveValue;
-  functionValue->SetString(shapeFunctionString);
-  return functionValue.forget();
-}
-
-template <typename ReferenceBox>
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::CreatePrimitiveValueForShapeSource(
-    const UniquePtr<StyleBasicShape>& aStyleBasicShape,
-    ReferenceBox aReferenceBox, const KTableEntry aBoxKeywordTable[]) {
-  RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
-  if (aStyleBasicShape) {
-    valueList->AppendCSSValue(
-        CreatePrimitiveValueForBasicShape(aStyleBasicShape));
-  }
-
-  if (aReferenceBox == ReferenceBox::NoBox) {
-    return valueList.forget();
-  }
-
-  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  val->SetIdent(
-      nsCSSProps::ValueToKeywordEnum(aReferenceBox, aBoxKeywordTable));
-  valueList->AppendCSSValue(val.forget());
-
-  return valueList.forget();
-}
-
 void nsComputedDOMStyle::SetCssTextToCoord(nsAString& aCssText,
                                            const nsStyleCoord& aCoord,
                                            bool aClampNegativeCalc) {
   RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
   SetValueToCoord(value, aCoord, aClampNegativeCalc);
   value->GetCssText(aCssText);
 }
 
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -370,30 +370,16 @@ class nsComputedDOMStyle final : public 
   bool GetScrollFrameContentHeight(nscoord& aHeight);
   bool GetFrameBorderRectWidth(nscoord& aWidth);
   bool GetFrameBorderRectHeight(nscoord& aHeight);
 
   /* Helper functions for computing and serializing a nsStyleCoord. */
   void SetCssTextToCoord(nsAString& aCssText, const nsStyleCoord& aCoord,
                          bool aClampNegativeCalc);
 
-  template <typename ReferenceBox>
-  already_AddRefed<CSSValue> CreatePrimitiveValueForShapeSource(
-      const mozilla::UniquePtr<mozilla::StyleBasicShape>& aStyleBasicShape,
-      ReferenceBox aReferenceBox, const KTableEntry aBoxKeywordTable[]);
-
-  // Helper function for computing basic shape styles.
-  already_AddRefed<CSSValue> CreatePrimitiveValueForBasicShape(
-      const mozilla::UniquePtr<mozilla::StyleBasicShape>& aStyleBasicShape);
-  void BoxValuesToString(nsAString& aString,
-                         const nsTArray<nsStyleCoord>& aBoxValues,
-                         bool aClampNegativeCalc);
-  void BasicShapeRadiiToString(nsAString& aCssText,
-                               const mozilla::BorderRadius&);
-
   // Find out if we can safely skip flushing (i.e. pending restyles do not
   // affect mElement).
   bool NeedsToFlush() const;
 
   static ComputedStyleMap* GetComputedStyleMap();
 
   // We don't really have a good immutable representation of "presentation".
   // Given the way GetComputedStyle is currently used, we should just grab the
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -163,22 +163,16 @@ enum class StyleImageOrientation : uint8
 
 // scrollbar-width
 enum class StyleScrollbarWidth : uint8_t {
   Auto,
   Thin,
   None,
 };
 
-// <shape-radius> for <basic-shape>
-enum class StyleShapeRadius : uint8_t {
-  ClosestSide,
-  FarthestSide,
-};
-
 // Shape source type
 enum class StyleShapeSourceType : uint8_t {
   None,
   URL,    // clip-path only
   Image,  // shape-outside only
   Shape,
   Box,
   Path,  // SVG path function
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -806,40 +806,16 @@ nsChangeHint nsStyleSVG::CalcDifference(
       hint = nsChangeHint_NeutralChange;
     }
   }
 
   return hint;
 }
 
 // --------------------
-// StyleBasicShape
-
-StyleBasicShape::StyleBasicShape(StyleBasicShapeType aType)
-    : mType(aType),
-      mFillRule(StyleFillRule::Nonzero),
-      mPosition(Position::FromPercentage(0.5f)),
-      mRadius(ZeroBorderRadius()) {}
-
-nsCSSKeyword StyleBasicShape::GetShapeTypeName() const {
-  switch (mType) {
-    case StyleBasicShapeType::Polygon:
-      return eCSSKeyword_polygon;
-    case StyleBasicShapeType::Circle:
-      return eCSSKeyword_circle;
-    case StyleBasicShapeType::Ellipse:
-      return eCSSKeyword_ellipse;
-    case StyleBasicShapeType::Inset:
-      return eCSSKeyword_inset;
-  }
-  MOZ_ASSERT_UNREACHABLE("unexpected type");
-  return eCSSKeyword_UNKNOWN;
-}
-
-// --------------------
 // StyleShapeSource
 StyleShapeSource::StyleShapeSource() : mBasicShape() {}
 
 StyleShapeSource::StyleShapeSource(const StyleShapeSource& aSource) {
   DoCopy(aSource);
 }
 
 StyleShapeSource::~StyleShapeSource() { DoDestroy(); }
@@ -952,20 +928,23 @@ void StyleShapeSource::DoCopy(const Styl
     case StyleShapeSourceType::URL:
       SetURL(aOther.URL());
       break;
 
     case StyleShapeSourceType::Image:
       SetShapeImage(MakeUnique<nsStyleImage>(aOther.ShapeImage()));
       break;
 
-    case StyleShapeSourceType::Shape:
-      SetBasicShape(MakeUnique<StyleBasicShape>(aOther.BasicShape()),
-                    aOther.GetReferenceBox());
+    case StyleShapeSourceType::Shape: {
+      UniquePtr<StyleBasicShape> shape(Servo_CloneBasicShape(&aOther.BasicShape()));
+      // TODO(emilio): This could be a copy-ctor call like above if we teach
+      // cbindgen to generate copy-constructors for tagged unions.
+      SetBasicShape(std::move(shape), aOther.GetReferenceBox());
       break;
+    }
 
     case StyleShapeSourceType::Box:
       SetReferenceBox(aOther.GetReferenceBox());
       break;
 
     case StyleShapeSourceType::Path:
       SetPath(MakeUnique<StyleSVGPath>(aOther.Path()));
       break;
@@ -2952,16 +2931,17 @@ nsStyleDisplay::nsStyleDisplay(const Doc
       mOverscrollBehaviorY(StyleOverscrollBehavior::Auto),
       mOverflowAnchor(StyleOverflowAnchor::Auto),
       mScrollSnapType(
           {StyleScrollSnapAxis::Both, StyleScrollSnapStrictness::None}),
       mScrollSnapPointsX(eStyleUnit_None),
       mScrollSnapPointsY(eStyleUnit_None),
       mScrollSnapDestination(
           {LengthPercentage::Zero(), LengthPercentage::Zero()}),
+      mLineClamp(0),
       mBackfaceVisibility(NS_STYLE_BACKFACE_VISIBILITY_VISIBLE),
       mTransformStyle(NS_STYLE_TRANSFORM_STYLE_FLAT),
       mTransformBox(StyleGeometryBox::BorderBox),
       mTransformOrigin{LengthPercentage::FromPercentage(0.5),
                        LengthPercentage::FromPercentage(0.5),
                        {0.}},
       mChildPerspective(StylePerspective::None()),
       mPerspectiveOrigin(Position::FromPercentage(0.5f)),
@@ -3017,16 +2997,17 @@ nsStyleDisplay::nsStyleDisplay(const nsS
       mScrollBehavior(aSource.mScrollBehavior),
       mOverscrollBehaviorX(aSource.mOverscrollBehaviorX),
       mOverscrollBehaviorY(aSource.mOverscrollBehaviorY),
       mScrollSnapType(aSource.mScrollSnapType),
       mScrollSnapPointsX(aSource.mScrollSnapPointsX),
       mScrollSnapPointsY(aSource.mScrollSnapPointsY),
       mScrollSnapDestination(aSource.mScrollSnapDestination),
       mScrollSnapCoordinate(aSource.mScrollSnapCoordinate),
+      mLineClamp(aSource.mLineClamp),
       mBackfaceVisibility(aSource.mBackfaceVisibility),
       mTransformStyle(aSource.mTransformStyle),
       mTransformBox(aSource.mTransformBox),
       mSpecifiedTransform(aSource.mSpecifiedTransform),
       mSpecifiedRotate(aSource.mSpecifiedRotate),
       mSpecifiedTranslate(aSource.mSpecifiedTranslate),
       mSpecifiedScale(aSource.mSpecifiedScale),
       // We intentionally leave mIndividualTransform as null, is the caller's
@@ -3214,16 +3195,20 @@ nsChangeHint nsStyleDisplay::CalcDiffere
       hint |= nsChangeHint_ReflowHintsForFloatAreaChange;
     } else {
       // shape-outside or shape-margin or shape-image-threshold changed,
       // but we don't need to reflow because we're not floating.
       hint |= nsChangeHint_NeutralChange;
     }
   }
 
+  if (mLineClamp != aNewData.mLineClamp) {
+    hint |= NS_STYLE_HINT_REFLOW;
+  }
+
   if (mVerticalAlign != aNewData.mVerticalAlign) {
     // XXX Can this just be AllReflowHints + RepaintFrame, and be included in
     // the block below?
     hint |= NS_STYLE_HINT_REFLOW;
   }
 
   // XXX the following is conservative, for now: changing float breaking
   // shouldn't necessarily require a repaint, reflow should suffice.
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1353,16 +1353,22 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
     return aCoord.IsLengthPercentage() &&
            aCoord.AsLengthPercentage().HasPercent();
   }
 };
 
 struct nsStyleTextOverflowSide {
   nsStyleTextOverflowSide() : mType(NS_STYLE_TEXT_OVERFLOW_CLIP) {}
 
+  static nsStyleTextOverflowSide Ellipsis() {
+    nsStyleTextOverflowSide side;
+    side.mType = NS_STYLE_TEXT_OVERFLOW_ELLIPSIS;
+    return side;
+  }
+
   bool operator==(const nsStyleTextOverflowSide& aOther) const {
     return mType == aOther.mType && (mType != NS_STYLE_TEXT_OVERFLOW_STRING ||
                                      mString == aOther.mString);
   }
   bool operator!=(const nsStyleTextOverflowSide& aOther) const {
     return !(*this == aOther);
   }
 
@@ -1652,75 +1658,16 @@ struct StyleAnimation {
   float mDelay;
   RefPtr<nsAtom> mName;  // nsGkAtoms::_empty for 'none'
   dom::PlaybackDirection mDirection;
   dom::FillMode mFillMode;
   StyleAnimationPlayState mPlayState;
   float mIterationCount;  // mozilla::PositiveInfinity<float>() means infinite
 };
 
-class StyleBasicShape final {
- public:
-  explicit StyleBasicShape(StyleBasicShapeType);
-
-  StyleBasicShapeType GetShapeType() const { return mType; }
-  nsCSSKeyword GetShapeTypeName() const;
-
-  StyleFillRule GetFillRule() const { return mFillRule; }
-
-  const mozilla::Position& GetPosition() const {
-    MOZ_ASSERT(mType == StyleBasicShapeType::Circle ||
-                   mType == StyleBasicShapeType::Ellipse,
-               "expected circle or ellipse");
-    return mPosition;
-  }
-
-  bool HasRadius() const {
-    MOZ_ASSERT(mType == StyleBasicShapeType::Inset, "expected inset");
-    NS_FOR_CSS_HALF_CORNERS(corner) {
-      auto& radius = mRadius.Get(corner);
-      if (radius.HasPercent() || radius.LengthInCSSPixels() != 0.0f) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  const mozilla::StyleBorderRadius& GetRadius() const {
-    MOZ_ASSERT(mType == StyleBasicShapeType::Inset, "expected inset");
-    return mRadius;
-  }
-
-  // mCoordinates has coordinates for polygon or radii for
-  // ellipse and circle.
-  const nsTArray<nsStyleCoord>& Coordinates() const { return mCoordinates; }
-
-  bool operator==(const StyleBasicShape& aOther) const {
-    return mType == aOther.mType && mFillRule == aOther.mFillRule &&
-           mCoordinates == aOther.mCoordinates &&
-           mPosition == aOther.mPosition && mRadius == aOther.mRadius;
-  }
-  bool operator!=(const StyleBasicShape& aOther) const {
-    return !(*this == aOther);
-  }
-
- private:
-  StyleBasicShapeType mType;
-  StyleFillRule mFillRule;
-
-  // mCoordinates has coordinates for polygon or radii for
-  // ellipse and circle.
-  // (top, right, bottom, left) for inset
-  nsTArray<nsStyleCoord> mCoordinates;
-  // position of center for ellipse or circle
-  mozilla::Position mPosition;
-  // corner radii for inset (0 if not set)
-  mozilla::StyleBorderRadius mRadius;
-};
-
 struct StyleSVGPath final {
   const nsTArray<StylePathCommand>& Path() const { return mPath; }
 
   StyleFillRule FillRule() const { return mFillRule; }
 
   bool operator==(const StyleSVGPath& aOther) const {
     return mPath == aOther.mPath && mFillRule == aOther.mFillRule;
   }
@@ -1768,24 +1715,24 @@ struct StyleShapeSource final {
 
   // Iff we have "shape-outside:<image>" with an image URI (not a gradient),
   // this method returns the corresponding imgIRequest*. Else, returns
   // null.
   imgIRequest* GetShapeImageData() const;
 
   void SetShapeImage(UniquePtr<nsStyleImage> aShapeImage);
 
-  const StyleBasicShape& BasicShape() const {
+  const mozilla::StyleBasicShape& BasicShape() const {
     MOZ_ASSERT(mType == StyleShapeSourceType::Shape,
                "Wrong shape source type!");
     MOZ_ASSERT(mBasicShape);
     return *mBasicShape;
   }
 
-  void SetBasicShape(UniquePtr<StyleBasicShape> aBasicShape,
+  void SetBasicShape(UniquePtr<mozilla::StyleBasicShape> aBasicShape,
                      StyleGeometryBox aReferenceBox);
 
   StyleGeometryBox GetReferenceBox() const {
     MOZ_ASSERT(mType == StyleShapeSourceType::Box ||
                    mType == StyleShapeSourceType::Shape,
                "Wrong shape source type!");
     return mReferenceBox;
   }
@@ -1804,17 +1751,17 @@ struct StyleShapeSource final {
 
  private:
   void* operator new(size_t) = delete;
 
   void DoCopy(const StyleShapeSource& aOther);
   void DoDestroy();
 
   union {
-    mozilla::UniquePtr<StyleBasicShape> mBasicShape;
+    mozilla::UniquePtr<mozilla::StyleBasicShape> mBasicShape;
     mozilla::UniquePtr<nsStyleImage> mShapeImage;
     mozilla::UniquePtr<StyleSVGPath> mSVGPath;
     // TODO: Bug 1480665, implement ray() function.
   };
   StyleShapeSourceType mType = StyleShapeSourceType::None;
   StyleGeometryBox mReferenceBox = StyleGeometryBox::NoBox;
 };
 
@@ -1896,16 +1843,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   mozilla::StyleOverscrollBehavior mOverscrollBehaviorY;
   mozilla::StyleOverflowAnchor mOverflowAnchor;
   mozilla::StyleScrollSnapAlign mScrollSnapAlign;
   mozilla::StyleScrollSnapType mScrollSnapType;
   nsStyleCoord mScrollSnapPointsX;
   nsStyleCoord mScrollSnapPointsY;
   mozilla::Position mScrollSnapDestination;
   nsTArray<mozilla::Position> mScrollSnapCoordinate;
+  uint32_t mLineClamp;
 
   // mSpecifiedTransform is the list of transform functions as
   // specified, or null to indicate there is no transform.  (inherit or
   // initial are replaced by an actual list of transform functions, or
   // null, as appropriate.)
   uint8_t mBackfaceVisibility;
   uint8_t mTransformStyle;
   StyleGeometryBox mTransformBox;
@@ -2715,16 +2663,17 @@ class nsStyleSVGPaint {
  private:
   void Reset();
   void Assign(const nsStyleSVGPaint& aOther);
 
   union ColorOrPaintServer {
     mozilla::StyleColor mColor;
     mozilla::css::URLValue* mPaintServer;
     explicit ColorOrPaintServer(mozilla::StyleColor c) : mColor(c) {}
+    ~ColorOrPaintServer() {}  // Caller must call Reset().
   };
   ColorOrPaintServer mPaint;
   nsStyleSVGPaintType mType;
   nsStyleSVGFallbackType mFallbackType;
   mozilla::StyleColor mFallbackColor;
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleSVG {
--- a/layout/style/res/ua.css
+++ b/layout/style/res/ua.css
@@ -212,16 +212,17 @@
   /* Flex container */
   flex-direction: inherit;
   flex-wrap: inherit;
   /* -webkit-box container (aliased from -webkit versions to -moz versions) */
   -moz-box-orient: inherit;
   -moz-box-direction: inherit;
   -moz-box-pack: inherit;
   -moz-box-align: inherit;
+  -webkit-line-clamp: inherit;
   /* Grid container */
   grid-auto-columns: inherit;
   grid-auto-rows: inherit;
   grid-auto-flow: inherit;
   grid-column-gap: inherit;
   grid-row-gap: inherit;
   grid-template-areas: inherit;
   grid-template-columns: inherit;
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -8536,16 +8536,27 @@ if (IsCSSPropertyPrefEnabled("layout.css
     domProp: "MozColumnSpan",
     inherited: false,
     type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
     alias_for: "column-span",
     subproperties: [ "column-span" ]
   };
 }
 
+if (IsCSSPropertyPrefEnabled("layout.css.webkit-line-clamp.enabled")) {
+  gCSSProperties["-webkit-line-clamp"] = {
+    domProp: "webkitLineClamp",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "1", "2" ],
+    invalid_values: [ "auto", "0", "-1" ],
+  };
+}
+
 if (false) {
   // TODO These properties are chrome-only, and are not exposed via CSSOM.
   // We may still want to find a way to test them. See bug 1206999.
   gCSSProperties["-moz-window-shadow"] = {
     //domProp: "MozWindowShadow",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "default" ],
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -293,16 +293,17 @@ var supported_properties = {
     "vertical-align": [ test_length_transition, test_percent_transition,
                         test_length_unclamped, test_percent_unclamped ],
     "visibility": [ test_visibility_transition ],
     "width": [ test_length_transition, test_percent_transition,
                test_length_percent_calc_transition,
                test_length_clamped, test_percent_clamped ],
     "word-spacing": [ test_length_transition, test_length_unclamped ],
     "z-index": [ test_integer_transition, test_pos_integer_or_auto_transition ],
+    "-webkit-line-clamp": [ test_pos_integer_or_none_transition ],
     "-webkit-text-fill-color": [ test_color_transition,
                                  test_currentcolor_transition ],
     "-webkit-text-stroke-color": [ test_color_transition,
                                    test_currentcolor_transition ]
 };
 
 if (IsCSSPropertyPrefEnabled("layout.css.motion-path.enabled")) {
   supported_properties["offset-path"] = [ test_path_function ];
@@ -2249,38 +2250,46 @@ function test_font_weight(prop) {
 
 function test_grid_gap(prop) {
   test_length_transition(prop);
   test_length_clamped(prop);
   test_percent_transition(prop);
   test_percent_clamped(prop);
 }
 
-function test_pos_integer_or_auto_transition(prop) {
+function test_pos_integer_or_keyword_transition(prop, keyword) {
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "4", "");
   is(cs.getPropertyValue(prop), "4",
      "integer-valued property " + prop + ": computed value before transition");
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "11", "");
   is(cs.getPropertyValue(prop), "6",
      "integer-valued property " + prop + ": interpolation of integers");
   check_distance(prop, "4", "6", "12");
-  div.style.setProperty(prop, "auto", "");
-  is(cs.getPropertyValue(prop), "auto",
-     "integer-valued property " + prop + ": auto not interpolable");
+  div.style.setProperty(prop, keyword, "");
+  is(cs.getPropertyValue(prop), keyword,
+     "integer-valued property " + prop + ": " + keyword + " not interpolable");
   div.style.setProperty(prop, "8", "");
   is(cs.getPropertyValue(prop), "8",
      "integer-valued property " + prop + ": computed value before transition");
   div.style.setProperty(prop, "4", "");
   is(cs.getPropertyValue(prop), "7",
      "integer-valued property " + prop + ": interpolation of integers");
   check_distance(prop, "8", "7", "4");
 }
 
+function test_pos_integer_or_auto_transition(prop) {
+  return test_pos_integer_or_keyword_transition(prop, "auto");
+}
+
+function test_pos_integer_or_none_transition(prop) {
+  return test_pos_integer_or_keyword_transition(prop, "none");
+}
+
 function test_integer_at_least_one_clamping(prop) {
   div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "1", "");
   is(cs.getPropertyValue(prop), "1",
      "integer-valued property " + prop + ": flush before clamping test");
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "5", "");
--- a/layout/svg/nsCSSClipPathInstance.cpp
+++ b/layout/svg/nsCSSClipPathInstance.cpp
@@ -111,24 +111,24 @@ already_AddRefed<Path> nsCSSClipPathInst
     return builder->Finish();
   }
 
   MOZ_ASSERT(mClipPathStyle.GetType() == StyleShapeSourceType::Shape);
 
   r = ToAppUnits(r.ToNearestPixels(appUnitsPerDevPixel), appUnitsPerDevPixel);
 
   const auto& basicShape = mClipPathStyle.BasicShape();
-  switch (basicShape.GetShapeType()) {
-    case StyleBasicShapeType::Circle:
+  switch (basicShape.tag) {
+    case StyleBasicShape::Tag::Circle:
       return CreateClipPathCircle(aDrawTarget, r);
-    case StyleBasicShapeType::Ellipse:
+    case StyleBasicShape::Tag::Ellipse:
       return CreateClipPathEllipse(aDrawTarget, r);
-    case StyleBasicShapeType::Polygon:
+    case StyleBasicShape::Tag::Polygon:
       return CreateClipPathPolygon(aDrawTarget, r);
-    case StyleBasicShapeType::Inset:
+    case StyleBasicShape::Tag::Inset:
       return CreateClipPathInset(aDrawTarget, r);
       break;
     default:
       MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected shape type");
   }
   // Return an empty Path:
   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
   return builder->Finish();
@@ -167,17 +167,17 @@ already_AddRefed<Path> nsCSSClipPathInst
                   Size(radii.width, radii.height) / appUnitsPerDevPixel);
   builder->Close();
   return builder->Finish();
 }
 
 already_AddRefed<Path> nsCSSClipPathInstance::CreateClipPathPolygon(
     DrawTarget* aDrawTarget, const nsRect& aRefBox) {
   const auto& basicShape = mClipPathStyle.BasicShape();
-  auto fillRule = basicShape.GetFillRule() == StyleFillRule::Nonzero
+  auto fillRule = basicShape.AsPolygon().fill == StyleFillRule::Nonzero
                       ? FillRule::FILL_WINDING
                       : FillRule::FILL_EVEN_ODD;
   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(fillRule);
 
   nsTArray<nsPoint> vertices =
       ShapeUtils::ComputePolygonVertices(basicShape, aRefBox);
   if (vertices.IsEmpty()) {
     MOZ_ASSERT_UNREACHABLE(
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -177,17 +177,17 @@ bool nsSVGIntegrationUtils::UsingSimpleC
   }
 
   const auto& clipPath = style->mClipPath;
   if (clipPath.GetType() != StyleShapeSourceType::Shape) {
     return false;
   }
 
   const auto& shape = clipPath.BasicShape();
-  return (shape.GetShapeType() != StyleBasicShapeType::Polygon);
+  return !shape.IsPolygon();
 }
 
 nsPoint nsSVGIntegrationUtils::GetOffsetToBoundingBox(nsIFrame* aFrame) {
   if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
     // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the
     // covered region relative to the nsSVGOuterSVGFrame, which is absolutely
     // not what we want. SVG frames are always in user space, so they have
     // no offset adjustment to make.
--- a/mobile/android/app/src/main/res/xml-v11/preference_headers.xml
+++ b/mobile/android/app/src/main/res/xml-v11/preference_headers.xml
@@ -54,22 +54,15 @@
     <header android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment"
         android:title="@string/pref_clear_private_data_now"
         android:id="@+id/pref_header_clear_private_data">
         <extra android:name="resource"
             android:value="preferences_privacy_clear_tablet"/>
     </header>
 
     <header android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment"
-        android:title="@string/pref_default_browser"
-        android:id="@+id/pref_header_default_browser">
-        <extra android:name="resource"
-            android:value="preferences_default_browser_tablet"/>
-    </header>
-
-    <header android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment"
             android:title="@string/pref_header_vendor"
             android:id="@+id/pref_header_vendor">
         <extra android:name="resource"
                android:value="preferences_vendor"/>
     </header>
 
 </preference-headers>
deleted file mode 100644
--- a/mobile/android/app/src/main/res/xml-v11/preferences_default_browser_tablet.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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/. -->
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <org.mozilla.gecko.preferences.DefaultBrowserPreference
-        android:key="android.not_a_preference.default_browser.link"
-        android:title="@string/pref_default_browser_mozilla_support_tablet"
-        android:persistent="false"
-        url="https://support.mozilla.org/kb/make-firefox-default-browser-android?utm_source=inproduct&amp;utm_medium=settings&amp;utm_campaign=mobileandroid"/>
-
-</PreferenceScreen>
--- a/mobile/android/app/src/main/res/xml/preferences_general_tablet.xml
+++ b/mobile/android/app/src/main/res/xml/preferences_general_tablet.xml
@@ -10,16 +10,22 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                   android:title="@string/pref_category_general"
                   android:enabled="false">
 
     <org.mozilla.gecko.preferences.SyncPreference android:key="android.not_a_preference.sync"
                                                   android:title="@string/pref_sync"
                                                   android:persistent="false" />
 
+    <org.mozilla.gecko.preferences.DefaultBrowserPreference
+            android:key="android.not_a_preference.default_browser.link"
+            android:title="@string/pref_default_browser"
+            android:persistent="false"
+            url="https://support.mozilla.org/kb/make-firefox-default-browser-android?utm_source=inproduct&amp;utm_medium=settings&amp;utm_campaign=mobileandroid"/>
+
     <PreferenceScreen android:key="android.not_a_preference.general_home"
                       android:title="@string/pref_category_home"
                       android:summary="@string/pref_category_home_summary"
                       android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
             <extra android:name="resource"
                    android:value="preferences_home" />
     </PreferenceScreen>
 
--- a/mobile/android/base/java/org/mozilla/gecko/util/UnusedResourcesUtil.java
+++ b/mobile/android/base/java/org/mozilla/gecko/util/UnusedResourcesUtil.java
@@ -82,18 +82,17 @@ final class UnusedResourcesUtil {
             R.string.bookmarkdefaults_url_restricted_webmaker,
     };
 
     public static final int[] USED_IN_PREFS = {
             R.xml.preferences_advanced,
             R.xml.preferences_accessibility,
             R.xml.preferences_home,
             R.xml.preferences_privacy,
-            R.xml.preferences_privacy_clear_tablet,
-            R.xml.preferences_default_browser_tablet
+            R.xml.preferences_privacy_clear_tablet
     };
 
     // String resources that are used in the full-pane Activity Stream that are temporarily
     // not needed while Activity Stream is part of the HomePager
     public static final int[] TEMPORARY_UNUSED_ACTIVITY_STREAM = {
             R.string.activity_stream_topsites
     };
 
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -413,17 +413,16 @@ shown from Android O while a tab is bein
 <!ENTITY pref_private_data_downloadFiles2 "Downloads">
 <!ENTITY pref_private_data_syncedTabs "Synced tabs">
 
 <!ENTITY pref_default_browser2 "Make &brandShortName; your default browser">
 <!-- LOCALIZATION NOTE (default_browser_system_settings_toast):
     Message of an Android Toast that appears after our app opens "Android System Settings -> Apps -> Default apps"
     and instruct users to tap on the "Browser app" option on that settings screen to choose a default browser app. -->
 <!ENTITY default_browser_system_settings_toast "Tap Browser app &amp; select &brandShortName;">
-<!ENTITY pref_default_browser_mozilla_support_tablet "Visit Mozilla Support">
 <!ENTITY pref_about_firefox "About &brandShortName;">
 <!ENTITY pref_vendor_faqs "FAQs">
 <!ENTITY pref_vendor_feedback "Give feedback">
 
 <!ENTITY pref_dialog_set_default "Set as default">
 <!ENTITY pref_dialog_default "Default">
 <!ENTITY pref_dialog_remove "Remove">
 <!ENTITY pref_dialog_activitystream_header_content "Additional content">
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -308,17 +308,16 @@
   <string name="tab_queue_toast_action">&tab_queue_toast_action;</string>
   <string name="tab_queue_notification_text_singular">&tab_queue_notification_text_singular2;</string>
   <string name="tab_queue_notification_text_plural">&tab_queue_notification_text_plural2;</string>
   <string name="tab_queue_notification_prompt">&tab_queue_notification_prompt;</string>
   <string name="tab_queue_notification_title">&tab_queue_notification_title;</string>
   <string name="tab_queue_notification_settings">&tab_queue_notification_settings;</string>
 
   <string name="pref_default_browser">&pref_default_browser2;</string>
-  <string name="pref_default_browser_mozilla_support_tablet">&pref_default_browser_mozilla_support_tablet;</string>
   <string name="default_browser_system_settings_toast">&default_browser_system_settings_toast;</string>
 
   <string name="pref_about_firefox">&pref_about_firefox;</string>
 
   <string name="pref_vendor_faqs">&pref_vendor_faqs;</string>
   <!-- https://support.mozilla.org/1/mobile/%VERSION%/%OS%/%LOCALE%/faq -->
   <string name="faq_link">https://support.mozilla.org/1/mobile/&formatS1;/&formatS2;/&formatS3;/faq</string>
 
--- a/mobile/android/geckoview/src/androidTest/assets/www/forms2.html
+++ b/mobile/android/geckoview/src/androidTest/assets/www/forms2.html
@@ -1,16 +1,18 @@
 <html>
     <head><title>Forms2</title></head>
     <body>
         <form>
-            <input type="text" id="firstname">
-            <input type="text" id="lastname">
-            <input type="text" id="user1" value="foo">
-            <input type="password" id="pass1" value="foo">
+            <fieldset>
+                <input type="text" id="firstname">
+                <input type="text" id="lastname">
+                <input type="text" id="user1" value="foo">
+                <input type="password" id="pass1" value="foo" autofocus>
+            </fieldset>
         </form>
         <iframe id="iframe"></iframe>
     <script>
         addEventListener("load", function(e) {
             if (window.parent === window) {
                 document.getElementById("iframe").contentWindow.location.href = window.location.href;
             }
         });
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateTest.kt
@@ -417,23 +417,16 @@ class ContentDelegateTest : BaseSessionT
         mainSession.loadTestPath(FORMS2_HTML_PATH)
         // Wait for the auto-fill nodes to populate.
         sessionRule.waitUntilCalled(object : Callbacks.TextInputDelegate {
             @AssertCalled(count = 2)
             override fun notifyAutoFill(session: GeckoSession, notification: Int, virtualId: Int) {
             }
         })
 
-        mainSession.evaluateJS("$('#pass1').focus()")
-        sessionRule.waitUntilCalled(object : Callbacks.TextInputDelegate {
-            @AssertCalled(count = 1)
-            override fun notifyAutoFill(session: GeckoSession, notification: Int, virtualId: Int) {
-            }
-        })
-
         val rootNode = ViewNode.newInstance()
         val rootStructure = ViewNodeBuilder.newInstance(AssistStructure(), rootNode,
                 /* async */ false) as ViewStructure
 
         // Perform auto-fill and return number of auto-fills performed.
         fun checkAutoFillChild(child: AssistStructure.ViewNode): Int {
             var sum = 0
             // Seal the node info instance so we can perform actions on it.
--- a/mobile/android/modules/geckoview/GeckoViewAutoFill.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewAutoFill.jsm
@@ -116,18 +116,29 @@ class GeckoViewAutoFill {
       }
 
       this._autoFillInfos.set(element, info);
       this._autoFillElements.set(info.id, Cu.getWeakReference(element));
       sendFocusEvent |= (element === element.ownerDocument.activeElement);
       return info;
     };
 
+    // Get password field to get better form data via LoginManagerContent.
+    let passwordField;
+    for (const field of aFormLike.elements) {
+      if (ChromeUtils.getClassName(field) === "HTMLInputElement" &&
+          field.type == "password") {
+        passwordField = field;
+        break;
+      }
+    }
+
     const [usernameField] =
-      LoginManagerContent.getUserNameAndPasswordFields(aFormLike.elements[0]);
+      LoginManagerContent.getUserNameAndPasswordFields(
+        passwordField || aFormLike.elements[0]);
 
     const rootInfo = getInfo(aFormLike.rootElement, null, undefined, null);
     rootInfo.root = rootInfo.id;
     rootInfo.children = aFormLike.elements
         .filter(element => (!usernameField || element.type != "text" ||
                             element == usernameField))
         .map(element => getInfo(element, rootInfo.id, rootInfo.id, usernameField));
 
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1335,16 +1335,23 @@ VARCACHE_PREF(
 #endif
 VARCACHE_PREF(
   "xul.panel-animations.enabled",
    xul_panel_animations_enabled,
   bool, PREF_VALUE
 )
 #undef PREF_VALUE
 
+// Is support for -webkit-line-clamp enabled?
+VARCACHE_PREF(
+  "layout.css.webkit-line-clamp.enabled",
+  layout_css_webkit_line_clamp_enabled,
+  bool, true
+)
+
 //---------------------------------------------------------------------------
 // JavaScript prefs
 //---------------------------------------------------------------------------
 
 // nsJSEnvironmentObserver observes the memory-pressure notifications and
 // forces a garbage collection and cycle collection when it happens, if the
 // appropriate pref is set.
 #ifdef ANDROID
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2656,17 +2656,17 @@ pref("security.notification_enable_delay
 
 #if defined(DEBUG) && !defined(ANDROID)
 pref("csp.about_uris_without_csp", "blank,printpreview,srcdoc,about,addons,cache-entry,config,crashes,debugging,devtools,downloads,home,memory,networking,newtab,performance,plugins,policies,profiles,restartrequired,serviceworkers,sessionrestore,support,sync-log,telemetry,url-classifier,webrtc,welcomeback");
 // the following prefs are for testing purposes only.
 pref("csp.overrule_about_uris_without_csp_whitelist", false);
 pref("csp.skip_about_page_has_csp_assert", false);
 // assertion flag will be set to false after fixing Bug 1473549
 pref("security.allow_eval_with_system_principal", false);
-pref("security.uris_using_eval_with_system_principal", "autocomplete.xml,redux.js,react-redux.js,content-task.js,preferencesbindings.js,lodash.js,jszip.js,sinon-7.2.7.js,ajv-4.1.1.js,setup,jsol.js,simpletest/simpletest.js");
+pref("security.uris_using_eval_with_system_principal", "autocomplete.xml,redux.js,react-redux.js,content-task.js,preferencesbindings.js,lodash.js,jszip.js,sinon-7.2.7.js,ajv-4.1.1.js,jsol.js,simpletest/simpletest.js");
 #endif
 
 #if defined(DEBUG) || defined(FUZZING)
 // Disallow web documents loaded with the SystemPrincipal
 pref("security.disallow_non_local_systemprincipal_in_tests", false);
 #endif
 
 // Mixed content blocking
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -4851,17 +4851,17 @@ nsresult nsCookieService::RemoveCookiesF
     }
 
     uint32_t cookiesCount = entry->GetCookies().Length();
     for (nsCookieEntry::IndexType i = cookiesCount; i != 0; --i) {
       nsListIter iter(entry, i - 1);
       RefPtr<nsCookie> cookie = iter.Cookie();
 
       bool hasRootDomain = false;
-      rv = mTLDService->HasRootDomain(cookie->Host(), aHost, &hasRootDomain);
+      rv = mTLDService->HasRootDomain(cookie->RawHost(), aHost, &hasRootDomain);
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (!hasRootDomain) {
         continue;
       }
 
       // Remove the cookie.
       RemoveCookieFromList(iter);
--- a/netwerk/test/gtest/TestMozURL.cpp
+++ b/netwerk/test/gtest/TestMozURL.cpp
@@ -1,17 +1,21 @@
 #include "gtest/gtest.h"
 #include "gtest/MozGTestBench.h"  // For MOZ_GTEST_BENCH
 
 #include <regex>
 #include "json/json.h"
+#include "json/reader.h"
 #include "mozilla/net/MozURL.h"
 #include "nsCOMPtr.h"
 #include "nsDirectoryServiceDefs.h"
+#include "nsNetUtil.h"
 #include "nsIFile.h"
+#include "nsIURI.h"
+#include "nsStreamUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 
 TEST(TestMozURL, Getters)
 {
   nsAutoCString href("http://user:pass@example.com/path?query#ref");
   RefPtr<MozURL> url;
--- a/netwerk/test/gtest/TestServerTimingHeader.cpp
+++ b/netwerk/test/gtest/TestServerTimingHeader.cpp
@@ -1,15 +1,18 @@
 #include "gtest/gtest.h"
 
 #include "mozilla/Unused.h"
 #include "mozilla/net/nsServerTiming.h"
 #include <string>
 #include <vector>
 
+using namespace mozilla;
+using namespace mozilla::net;
+
 void testServerTimingHeader(
     const char* headerValue,
     std::vector<std::vector<std::string>> expectedResults) {
   nsAutoCString header(headerValue);
   ServerTimingParser parser(header);
   parser.Parse();
 
   nsTArray<nsCOMPtr<nsIServerTiming>> results =
--- a/netwerk/test/gtest/moz.build
+++ b/netwerk/test/gtest/moz.build
@@ -5,39 +5,39 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
     'TestBufferedInputStream.cpp',
     'TestHeaders.cpp',
     'TestHttpAuthUtils.cpp',
     'TestIsValidIp.cpp',
     'TestMIMEInputStream.cpp',
+    'TestMozURL.cpp',
     'TestProtocolProxyService.cpp',
     'TestReadStreamToString.cpp',
+    'TestServerTimingHeader.cpp',
     'TestStandardURL.cpp',
 ]
 
 # skip the test on windows10-aarch64
 if not(CONFIG['OS_TARGET'] == 'WINNT' and CONFIG['CPU_ARCH'] == 'aarch64'):
     UNIFIED_SOURCES += [
         'TestPACMan.cpp',
         'TestPartiallySeekableInputStream.cpp',
         'TestURIMutator.cpp',
     ]
 
-if CONFIG['OS_TARGET'] != 'Android':
-    UNIFIED_SOURCES += [
-        'TestMozURL.cpp',
-        'TestServerTimingHeader.cpp',
-    ]
-
 TEST_HARNESS_FILES.gtest += [
     'urltestdata.json',
 ]
 
+USE_LIBS += [
+    'jsoncpp',
+]
+
 TEST_DIRS += [
     'parse-ftp',
 ]
 
 LOCAL_INCLUDES += [
     '/netwerk/base',
     '/toolkit/components/jsoncpp/include',
     '/xpcom/tests/gtest',
--- a/security/manager/ssl/StaticHPKPins.h
+++ b/security/manager/ssl/StaticHPKPins.h
@@ -1148,9 +1148,9 @@ static const TransportSecurityPreload kP
   { "za.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "zh.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
 };
 
 // Pinning Preload List Length = 486;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1565612900901000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1565872296829000);
--- a/security/manager/ssl/nsSTSPreloadList.inc
+++ b/security/manager/ssl/nsSTSPreloadList.inc
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*****************************************************************************/
 /* This is an automatically generated file. If you're not                    */
 /* nsSiteSecurityService.cpp, you shouldn't be #including it.                */
 /*****************************************************************************/
 
 #include <stdint.h>
-const PRTime gPreloadListExpirationTime = INT64_C(1568032095032000);
+const PRTime gPreloadListExpirationTime = INT64_C(1568291484477000);
 %%
 0-1.party, 1
 00100010.net, 1
 0010100.net, 1
 00120012.net, 1
 00130013.net, 1
 00140014.net, 1
 00150015.net, 1
@@ -31,17 +31,16 @@ 00550055.net, 1
 005555.xyz, 1
 00660066.net, 1
 00770077.net, 1
 007kf.com, 1
 008207.com, 1
 008251.com, 1
 008253.com, 1
 008271.com, 1
-0086286.com, 1
 00880088.net, 1
 00990099.net, 1
 009p.com, 1
 00d88.com, 1
 00dani.me, 1
 00f.net, 1
 00wbf.com, 1
 01-edu.org, 1
@@ -131,16 +130,17 @@ 065790.com, 1
 065970.com, 1
 065976.com, 1
 066570.com, 1
 066579.com, 1
 066590.com, 1
 066705.com, 1
 066709.com, 1
 066790.com, 1
+066816.com, 1
 06804.com, 1
 068697.com, 1
 068756.com, 1
 068957.com, 1
 069657.com, 1
 069676.com, 1
 06se.com, 1
 070709.net, 1
@@ -199,16 +199,17 @@ 087540.com, 1
 087569.com, 1
 087580.com, 1
 08845.cc, 1
 089818.com, 1
 08detaxe.fr, 1
 095598.cc, 1
 09892.net, 1
 098955.com, 1
+09elektrik.com, 1
 0akarma.me, 1
 0au.de, 1
 0c3.de, 1
 0cdn.ga, 1
 0chan.pl, 1
 0day.agency, 1
 0day.su, 1
 0des.com, 1
@@ -302,16 +303,17 @@ 10og.de, 1
 10ppm.com, 1
 10x.ooo, 1
 1100.so, 1
 110110110.net, 1
 110320.com, 1
 110692.com, 1
 111.one, 1
 1111k8.com, 1
+111zlong.com, 1
 112112112.net, 1
 1126p.com, 1
 112app.nl, 1
 112hz.com, 1
 112it.ro, 1
 1130p.com, 1
 113113113.net, 1
 114514ss.com, 1
@@ -328,17 +330,16 @@ 123110.com, 1
 123123qq.com, 1
 1236.be, 1
 123apps.net, 1
 123bearing.co.uk, 1
 123bearing.com, 1
 123bearing.eu, 1
 123birthdaygreetings.com, 1
 123comparer.fr, 1
-123derivatives.com, 1
 123djdrop.com, 1
 123midterm.com, 1
 123nutricion.es, 1
 123opstalverzekeringen.nl, 1
 123plons.nl, 1
 123roulement.be, 1
 123roulement.com, 1
 123termpapers.com, 1
@@ -532,16 +533,17 @@ 1password.com, 1
 1password.eu, 1
 1pw.ca, 1
 1px.tv, 1
 1q2w.nl, 1
 1q365a.com, 1
 1r.is, 1
 1rs.nl, 1
 1salland.nl, 1
+1sand0s.nl, 1
 1scope.com, 1
 1se.co, 1
 1se2or3.com, 1
 1st-bounce.co.uk, 1
 1st-community.de, 1
 1stchoicefun.co.uk, 1
 1stchoicelandscapingwa.com, 1
 1stclassbouncycastles.co.uk, 1
@@ -611,16 +613,17 @@ 2495dentalimplants.com, 1
 249722.com, 1
 249cq.com, 1
 24dian30.com, 1
 24hour-locksmithsanantonio.com, 1
 24hourcyclist.co.uk, 1
 24hourlocksmithbaltimore.com, 1
 24hourlocksmithdallastx.com, 1
 24hourlocksmithdetroit.com, 1
+24hourlocksmithshouston.com, 1
 24hoursanantoniolocksmiths.com, 1
 24hourscienceprojects.com, 1
 24ip.com, 1
 24ip.de, 1
 24ip.fr, 1
 24kbet.com, 1
 24zpravy.cz, 1
 2502.net, 1
@@ -816,16 +819,17 @@ 360008888.com, 1
 360live.fr, 1
 360rail.nl, 1
 360vrs.com, 1
 360woodworking.com, 1
 361116.com, 1
 361171.com, 1
 361173.com, 1
 361183.com, 1
+3615jacky.fr, 1
 364553.com, 1
 365365.com, 1
 365beautyworld.com, 1
 365d88.com, 1
 365daysreview.com, 1
 365electricalvn.com, 1
 365healthworld.com, 1
 365propertybuyer.co.uk, 0
@@ -944,17 +948,16 @@ 4-it.de, 1
 4000milestare.com, 0
 403.ch, 1
 404.guide, 1
 404notfound.com.br, 1
 4096b.com, 1
 4096bit.de, 0
 40acts.org.uk, 1
 41-where.com, 1
-411416.com, 1
 41199.com, 1
 411film.com, 1
 411movie.com, 1
 411quest.com, 1
 414553.com, 1
 41844.de, 1
 41studio.com, 1
 41where.com, 1
@@ -1041,37 +1044,91 @@ 500fcw.com, 1
 500k.nl, 1
 500p.xyz, 1
 502312.com, 1
 504122.com, 1
 504322.com, 1
 504622.com, 1
 504922.com, 1
 506422.com, 1
-508088.com, 1
 50lakeshore.com, 1
 50ma.xyz, 1
 50north.de, 1
 50plusnet.nl, 1
 514122.com, 1
 514522.com, 1
 514622.com, 1
 514922.com, 1
 515422.com, 1
 516422.com, 1
 517vpn.cn, 1
 518.com.tw, 1
 51877.net, 1
 518d88.com, 1
 519422.com, 1
+5197.co, 1
 5197.com, 1
+5197a.co, 1
+5197aa.co, 1
+5197b.co, 1
+5197bb.co, 1
+5197c.co, 1
+5197cc.co, 1
+5197d.co, 1
+5197dd.co, 1
+5197dh.co, 1
 5197dh.com, 1
 5197dns.com, 1
 5197dz.com, 1
+5197e.co, 1
+5197ee.co, 1
+5197f.co, 1
+5197ff.co, 1
+5197g.co, 1
+5197gg.co, 1
+5197h.co, 1
+5197hd.co, 1
+5197hh.co, 1
+5197i.co, 1
+5197ii.co, 1
+5197j.co, 1
+5197jj.co, 1
+5197k.co, 1
+5197kk.co, 1
+5197l.co, 1
+5197ll.co, 1
+5197m.co, 1
+5197mm.co, 1
+5197n.co, 1
+5197nn.co, 1
+5197o.co, 1
+5197oo.co, 1
+5197p.co, 1
+5197pp.co, 1
+5197q.co, 1
+5197qq.co, 1
+5197r.co, 1
+5197rr.co, 1
+5197s.co, 1
+5197ss.co, 1
 5197sx.com, 1
+5197t.co, 1
+5197tt.co, 1
+5197u.co, 1
+5197uu.co, 1
+5197v.co, 1
+5197vv.co, 1
+5197w.co, 1
+5197ww.co, 1
+5197x.co, 1
+5197xx.co, 1
+5197y.co, 1
+5197yy.co, 1
+5197z.co, 1
+5197zz.co, 1
 51acg.eu.org, 1
 51aifuli.com, 1
 51chiyu.com, 1
 51guaq.com, 1
 51tiaojiu.com, 1
 52051.com, 1
 52051a.com, 1
 52051b.com, 1
@@ -1364,17 +1421,16 @@ 692990.com, 1
 692b8c32.de, 1
 693422.com, 1
 694322.com, 1
 694622.com, 1
 694922.com, 1
 695660.com, 1
 6969.us, 1
 698da.com, 1
-69928.com, 1
 6997896.com, 1
 69butterfly.com, 1
 69fps.gg, 1
 69games.xxx, 1
 69mentor.com, 1
 69wasted.net, 1
 6bwcp.com, 1
 6dec.gc.ca, 1
@@ -1414,16 +1470,17 @@ 718433.com, 1
 718552.com, 1
 718772.com, 1
 719433.com, 1
 721167.com, 1
 721172.com, 1
 722201.com, 1
 724233.com, 1
 724go.com, 1
+726127.com, 1
 726162.com, 1
 7261696e626f77.net, 1
 726176.com, 1
 726221.com, 1
 726433.com, 1
 728433.com, 1
 729433.com, 1
 72ty.com, 1
@@ -1441,17 +1498,16 @@ 738433.com, 1
 739433.com, 1
 740833.com, 1
 741833.com, 1
 742833.com, 1
 743833.com, 1
 74th.jp, 1
 755k3.com, 1
 756337.com, 1
-758m.com, 1
 762.ch, 1
 762116.com, 1
 763137.com, 1
 7733445.com, 1
 7770b.com, 1
 7770t.com, 1
 7771p.com, 1
 7777k8.com, 1
@@ -1466,16 +1522,17 @@ 781713.com, 1
 783631.com, 1
 783lab.com, 1
 787637.com, 1
 787k3.com, 1
 7885765.com, 1
 7891553.com, 1
 7891997.com, 1
 7898666.com, 1
+790security.co.za, 1
 797715.com, 1
 79ch.com, 1
 7careconnect.com, 1
 7delights.com, 1
 7delights.in, 1
 7f.is, 1
 7geese.com, 1
 7graus.pt, 1
@@ -1536,32 +1593,35 @@ 8190d88.com, 1
 81uc.com, 1
 8207d88.com, 1
 8211p.com, 1
 8212p.com, 1
 8213p.com, 1
 8214p.com, 1
 8215p.com, 1
 8216p.com, 1
+8228d88.com, 1
+8230d88.com, 1
 82kb88.com, 1
 833792.com, 1
 8349822.com, 1
 8363p.com, 1
 8367p.com, 1
 8368p.com, 1
 8369p.com, 1
 8371p.com, 1
 8373p.com, 1
 8376p.com, 1
 8378p.com, 1
 8379p.com, 1
 8387p.com, 1
 8391p.com, 1
 8396p.com, 1
 83kb88.com, 1
+842844.com, 1
 848jz.com, 1
 850226.com, 1
 8522.am, 1
 8522club.com, 1
 8522hk.com, 1
 8522ph.com, 1
 8522top.com, 1
 8522tw.com, 1
@@ -1707,84 +1767,303 @@ 91-freedom.com, 1
 910kj.com, 1
 911.gov, 1
 911216.xyz, 1
 9118.com, 1
 911commission.gov, 1
 912422.com, 1
 913422.com, 1
 914122.com, 1
-914cq.com, 1
 915ers.com, 0
-918116.com, 0
+918116.com, 1
 9181181.com, 1
 9182289.com, 1
 918gd.com, 1
 918yy.com, 1
 919422.com, 1
 91966.com, 1
 91d00.com, 1
 91d01.com, 1
 91d02.com, 1
 91d27.com, 1
 91d30.com, 1
 91d31.com, 1
 91d33.com, 1
+91d52.com, 1
+91d58.com, 1
+91d89.com, 1
 91dh.cc, 1
 91tianmi.com, 0
 91travel.info, 1
 924122.com, 1
 924322.com, 1
 924622.com, 1
 926422.com, 1
 929349.com, 1
+9297.co, 1
 9297.com, 1
+9297a.co, 1
+9297aa.co, 1
+9297b.co, 1
+9297bb.co, 1
+9297c.co, 1
+9297cc.co, 1
+9297d.co, 1
+9297dd.co, 1
+9297dh.co, 1
 9297dh.com, 1
 9297dns.com, 1
+9297e.co, 1
+9297ee.co, 1
+9297f.co, 1
+9297ff.co, 1
+9297g.co, 1
+9297gg.co, 1
+9297h.co, 1
 9297hb.com, 1
+9297hd.co, 1
 9297hd.com, 1
+9297hh.co, 1
+9297i.co, 1
+9297ii.co, 1
+9297j.co, 1
+9297jj.co, 1
+9297k.co, 1
+9297kk.co, 1
+9297l.co, 1
+9297ll.co, 1
+9297m.co, 1
+9297mm.co, 1
+9297n.co, 1
+9297nn.co, 1
+9297o.co, 1
+9297oo.co, 1
+9297p.co, 1
+9297pp.co, 1
+9297q.co, 1
+9297qq.co, 1
+9297r.co, 1
+9297rr.co, 1
+9297s.co, 1
+9297ss.co, 1
+9297t.co, 1
+9297tt.co, 1
+9297u.co, 1
+9297uu.co, 1
+9297v.co, 1
+9297vv.co, 1
+9297w.co, 1
+9297ww.co, 1
+9297x.co, 1
+9297xx.co, 1
+9297y.co, 1
+9297yy.co, 1
+9297z.co, 1
+9297zz.co, 1
 92kb88.com, 1
 92url.com, 1
 931422.com, 1
 932422.com, 1
 933325.com, 1
 934122.com, 1
 939394.org, 1
+9397.com, 1
+9397a.com, 1
+9397aa.com, 1
+9397b.com, 1
+9397bb.com, 1
+9397c.com, 1
+9397cc.com, 1
+9397dd.com, 1
+9397dh.com, 1
+9397e.com, 1
+9397ee.com, 1
+9397f.com, 1
+9397ff.com, 1
+9397g.com, 1
+9397gg.com, 1
+9397h.com, 1
+9397hb.com, 1
+9397hd.com, 1
+9397hh.com, 1
+9397i.com, 1
+9397ii.com, 1
+9397j.com, 1
+9397jj.com, 1
+9397kk.com, 1
+9397l.com, 1
+9397ll.com, 1
+9397m.com, 1
+9397mm.com, 1
+9397n.com, 1
+9397nn.com, 1
+9397o.com, 1
+9397oo.com, 1
+9397p.com, 1
+9397pp.com, 1
+9397q.com, 1
+9397qq.com, 1
+9397r.com, 1
+9397rr.com, 1
+9397s.com, 1
+9397ss.com, 1
+9397t.com, 1
+9397tt.com, 1
+9397u.com, 1
+9397uu.com, 1
+9397v.com, 1
+9397vv.com, 1
+9397w.com, 1
+9397ww.com, 1
+9397x.com, 1
+9397xx.com, 1
+9397y.com, 1
+9397yy.com, 1
+9397z.com, 1
+9397zz.com, 1
 93kb88.com, 1
 943022.com, 1
 9449-27a1-22a1-e0d9-4237-dd99-e75e-ac85-2f47-9d34.de, 1
 946022.com, 1
 946422.com, 1
 947cq.com, 1
 949022.com, 1
 949122.com, 1
 949622.com, 1
 949722.com, 1
 95778.com, 1
 95kb88.com, 1
 961621.com, 1
 9617818.com, 1
 9617818.net, 1
-961cq.com, 1
 962312.com, 1
 963cq.com, 1
 967606.com, 1
 9679693.com, 1
 9681909.com, 1
 9696178.com, 1
 9696178.net, 1
 96kb88.com, 1
+9721.com, 1
+9721a.com, 1
+9721aa.com, 1
+9721b.com, 1
+9721bb.com, 1
+9721c.com, 1
+9721cc.com, 1
+9721d.com, 1
+9721dd.com, 1
+9721dh.com, 1
+9721e.com, 1
+9721ee.com, 1
+9721f.com, 1
+9721ff.com, 1
+9721g.com, 1
+9721gg.com, 1
+9721h.com, 1
+9721hd.com, 1
+9721hh.com, 1
+9721i.com, 1
+9721j.com, 1
+9721jj.com, 1
+9721k.com, 1
+9721kk.com, 1
+9721l.com, 1
+9721ll.com, 1
+9721m.com, 1
+9721mm.com, 1
+9721n.com, 1
+9721nn.com, 1
+9721o.com, 1
+9721oo.com, 1
+9721p.com, 1
+9721pp.com, 1
+9721q.com, 1
+9721qq.com, 1
+9721r.com, 1
+9721rr.com, 1
+9721s.com, 1
+9721ss.com, 1
+9721t.com, 1
+9721tt.com, 1
+9721u.com, 1
+9721uu.com, 1
+9721v.com, 1
+9721vv.com, 1
+9721w.com, 1
+9721ww.com, 1
+9721x.com, 1
+9721xx.com, 1
+9721y.com, 1
+9721yy.com, 1
+9721z.com, 1
+9721zz.com, 1
 972422.com, 1
+9728.co, 1
 9728.com, 1
+9728a.co, 1
+9728aa.co, 1
+9728b.co, 1
+9728bb.co, 1
+9728c.co, 1
+9728cc.co, 1
+9728d.co, 1
+9728dd.co, 1
+9728dh.co, 1
 9728dh.com, 1
 9728dns.com, 1
 9728dz.com, 1
+9728e.co, 1
+9728ee.co, 1
+9728f.co, 1
+9728ff.co, 1
+9728g.co, 1
+9728gg.co, 1
+9728h.co, 1
 9728hb.com, 1
+9728hd.co, 1
 9728hd.com, 1
+9728hh.co, 1
+9728i.co, 1
+9728ii.co, 1
+9728j.co, 1
+9728jj.co, 1
+9728k.co, 1
+9728kk.co, 1
+9728l.co, 1
+9728ll.co, 1
+9728m.co, 1
+9728mm.co, 1
+9728n.co, 1
+9728nn.co, 1
+9728o.co, 1
+9728oo.co, 1
+9728p.co, 1
+9728pp.co, 1
+9728q.co, 1
+9728qq.co, 1
+9728r.co, 1
+9728rr.co, 1
+9728s.co, 1
+9728ss.co, 1
 9728sx.com, 1
+9728t.co, 1
+9728tt.co, 1
+9728u.co, 1
+9728uu.co, 1
+9728v.co, 1
+9728vv.co, 1
+9728w.co, 1
+9728ww.co, 1
+9728x.co, 1
+9728xx.co, 1
+9728y.co, 1
+9728yy.co, 1
+9728z.co, 1
+9728zz.co, 1
 977hghg.com, 1
 9788876.com, 1
 9822.am, 1
 9822am.com, 1
 9822cn.com, 1
 9822hk.com, 1
 9822ph.com, 1
 9822tw.com, 1
@@ -1828,16 +2107,17 @@ 9k286.com, 1
 9k635.com, 1
 9k637.com, 1
 9k639.com, 1
 9k675.com, 1
 9k677.com, 1
 9k679.com, 1
 9k695.com, 1
 9k697.com, 1
+9k886.com, 1
 9pkfz.com, 1
 9riddles.com, 1
 9ss6.com, 1
 9uelle.jp, 1
 9vx.org, 1
 9won.kr, 1
 9y.at, 1
 9yw.me, 1
@@ -1866,34 +2146,44 @@ a1jumpandbounce.co.uk, 1
 a1moldsolutions.com, 1
 a1scuba.com, 1
 a22z.xyz, 1
 a2a.me, 1
 a2a.net, 1
 a2c-co.net, 1
 a2nutrition.com.au, 1
 a2os.club, 1
+a30.tokyo, 1
 a4sound.com, 1
+a5197.co, 1
 a632079.me, 1
 a7la-chat.com, 1
 a7m2.me, 1
 a8q.org, 1
+a9297.co, 1
+a9397.com, 1
+a9721.com, 1
+a9728.co, 1
 aa-tour.ru, 1
 aa43d.cn, 1
+aa5197.co, 1
 aa6688.net, 1
+aa9297.co, 1
+aa9397.com, 1
+aa9721.com, 1
+aa9728.co, 1
 aaa-racing.com, 1
 aaa-racing.net, 1
 aaa-racing.uk, 1
 aaapl.com, 1
 aaben-bank.dk, 1
 aabenbank.dk, 1
 aacfree.com, 1
 aacs-design.com, 1
 aadw.de, 1
-aaex.cloud, 1
 aagetransport.no, 1
 aalalbayt.com, 1
 aalalbayt.net, 1
 aalstmotors-usedcars.be, 1
 aaltocapital.com, 1
 aamwa.com, 1
 aandeautobody.com, 1
 aandkevents.co.uk, 1
@@ -1941,17 +2231,16 @@ abcbouncycastlessurrey.co.uk, 1
 abcbouncyfactory.co.uk, 1
 abcdef.be, 1
 abcheck.se, 1
 abckam.com, 1
 abcpartyhire.com, 1
 abcstudio.com.au, 1
 abdel.me, 1
 abdl.link, 1
-abdullah.pw, 1
 abdulrahman.eu, 1
 abdulwahaab.ca, 1
 abe-elektro.de, 1
 abe-medical.jp, 1
 abecodes.net, 1
 abeilles-idapi.fr, 1
 abelsflooringandtile.com, 1
 abenteuer-ahnenforschung.de, 1
@@ -1973,16 +2262,17 @@ abigisp.com, 1
 abilitycaresoftware.com, 1
 abilitymatters.co.uk, 1
 abilityone.gov, 1
 abilma.com, 1
 abilymp06.net, 1
 abimelec.com, 1
 abinferis.com, 1
 abinyah.com, 1
+abitaspringsla.gov, 1
 abitidalavoro.roma.it, 1
 abitidasposa.roma.it, 1
 abitur97ag.de, 1
 abiturma.de, 1
 abjay.com, 1
 ableprop.net, 1
 abloop.com, 1
 abmc.gov, 1
@@ -2076,17 +2366,16 @@ ac0g.dyndns.org, 1
 aca-creative.co.uk, 1
 academiadebomberosonline.com, 1
 academicexperts.us, 1
 academichealthscience.net, 1
 academie-de-police.ch, 1
 academkin.com, 1
 academus.io, 1
 academytv.com.au, 1
-acadianapatios.com, 1
 acaeum.com, 1
 acandroid.top, 1
 acaonegocios.com.br, 1
 acaptureservices.com, 1
 acara-yoga.de, 1
 acareer.in, 1
 acat.io, 1
 acbrussels-used.be, 1
@@ -2097,16 +2386,17 @@ acceleratenetworks.com, 1
 accelerateyourworld.org, 1
 accelsnow.com, 1
 accentthailand.com, 1
 accesloges.com, 1
 accessacab.co.uk, 1
 accessauto-occasions.be, 1
 accessgaragedoors.com, 1
 accessibility.gov, 1
+accessibletravelclub.com, 1
 accesskeycloning.com, 1
 accessmy.net, 1
 accessoirescheveuxchic.com, 1
 accessoripersmartphone.it, 1
 acchicocchi.com, 1
 acclivity.pro, 1
 accme.co, 1
 accolade.com.br, 1
@@ -2196,17 +2486,16 @@ acousticsoundrecords.com, 1
 acoustique-tardy.com, 1
 acp-integrative.fr, 1
 acpcoils.com, 1
 acperu.ch, 1
 acpinformatique.fr, 1
 acquisition.gov, 1
 acquistareviagragenericoitalia.net, 0
 acraft.org, 1
-acrealux.lu, 1
 acrepairgeorgetown.com, 1
 acrepairhutto.com, 1
 acrepairroundrocktx.com, 1
 acrevalue.com, 1
 acriticismlab.org, 1
 acrolife.cz, 1
 across.ml, 1
 acrossgw.com, 1
@@ -2233,17 +2522,17 @@ actionfinancialservices.net, 1
 actionlabs.net, 1
 actionmadagascar.ch, 1
 actionsack.com, 1
 actionselling.com, 1
 activatemyiphone.com, 1
 activateudid.com, 1
 active-tluszcz.pl, 1
 active.hu, 0
-activecare-monitor.com, 1
+activecare-monitor.com, 0
 activeclearweb.com, 1
 activeexcavator.com, 1
 activehire.co.uk, 1
 activeleisure.ie, 1
 activiteithardenberg.nl, 1
 activitesaintnicaise.org, 1
 activityeventhire.co.uk, 1
 actom.cc, 1
@@ -2398,19 +2687,19 @@ admins.tech, 1
 adminton.eu, 1
 adminwerk.net, 1
 admirable.one, 1
 admirable.pro, 1
 admody.com, 1
 admongo.gov, 1
 adnanoktar.com, 1
 adnanotoyedekparca.com, 1
+adnexa.it, 1
 adnmb1.com, 1
 adnolesh.com, 1
-adnot.am, 1
 adnseguros.es, 1
 adohanyzasjovoje.hu, 1
 adoll.ml, 1
 adomani-italia.com, 1
 adoniscabaret.co.uk, 1
 adonizer.science, 1
 adonnante.com, 1
 adoptionlink.co.uk, 1
@@ -2500,17 +2789,16 @@ adventureswithlillie.ca, 1
 adventurousway.com, 1
 advertis.biz, 1
 advertisemant.com, 1
 adviserplus.com, 1
 advocate-europe.eu, 1
 advocator.ca, 1
 advocoeurdehaan.nl, 1
 advogatech.com.br, 1
-advokat-romanov.com, 1
 advtran.com, 1
 adware.pl, 0
 adwokatkosterka.pl, 1
 adwokatzdunek.pl, 1
 adws.io, 1
 adxperience.com, 1
 adzie.xyz, 1
 adzuna.at, 1
@@ -2750,17 +3038,16 @@ ahcpr.gov, 0
 ahd.com, 0
 ahegao.ca, 1
 aheng.me, 1
 ahero4all.org, 1
 ahkubiak.ovh, 0
 ahlaejaba.com, 1
 ahlz.sk, 1
 ahmad.works, 1
-ahmadly.com, 1
 ahmd.io, 1
 ahmedabadflowermall.com, 1
 ahmedcharles.com, 1
 ahmedknowmadic.com, 1
 ahmerjamilkhan.org, 1
 ahmetozer.org, 1
 ahosi.com, 1
 ahoy.travel, 1
@@ -2827,16 +3114,17 @@ aimax.com, 1
 aimd.tech, 1
 aimeeandalec.com, 1
 aimgroup.co.tz, 1
 aimi-salon.com, 1
 aimonline.nl, 1
 aimotive.com, 1
 aimrom.org, 1
 aimstoreglobal.com, 1
+ainfographie.com, 1
 aintevenmad.ch, 1
 ainvest.de, 1
 aioboot.com, 1
 aiois.com, 1
 aipbarcelona.com, 1
 aiphyron.com, 1
 air-craftglass.com, 1
 air-shots.ch, 1
@@ -2935,16 +3223,17 @@ airlibre-parachutisme.com, 1
 airlinesettlement.com, 1
 airmail.cc, 1
 airmaxinflatables.com, 1
 airnow.gov, 1
 airpbx.com, 1
 airplay-inflatable-hire.co.uk, 1
 airplayradio.nl, 1
 airport-charlotte.com, 1
+airportal.cn, 1
 airpurifierproductsonline.com, 1
 airrestoration.ch, 1
 airslate.com, 1
 airsnore.com, 1
 airsoft.ch, 1
 airswap.io, 1
 airtec-france.fr, 1
 airtimerewards.co.uk, 1
@@ -2977,16 +3266,17 @@ aizxxs.net, 1
 ajarope.com, 1
 ajaxed.net, 1
 ajbouncycastles.co.uk, 1
 ajces.com, 1
 ajdiaz.me, 1
 ajetaci.cz, 1
 ajeventhire.co.uk, 1
 ajgroup-me.com, 1
+ajhstamps.co.uk, 1
 ajiaojr.info, 1
 ajiaojr.io, 1
 ajiaojr.me, 1
 ajiaojr.net, 1
 ajiboye.com, 1
 ajnasz.hu, 1
 ajsb85.com, 1
 ajwebsolutions.com, 1
@@ -3132,24 +3422,24 @@ aleksib.fi, 1
 alela.fr, 1
 aleph.land, 1
 alerbon.net, 1
 alertboxx.com, 1
 alertonline.nl, 1
 alerts.sg, 1
 alertwire.com, 1
 alesia-formation.fr, 1
-alessandro.pw, 1
 alessandrotravel.com, 1
 aletm.it, 1
 alex-ross.co.uk, 1
 alex4386.us, 1
 alex97000.de, 1
 alexander-beck.eu, 1
 alexanderb.info, 1
+alexandercanton.com, 1
 alexanderneng.de, 1
 alexandernorth.ch, 1
 alexanderrans.com, 0
 alexanderschimpf.de, 1
 alexandra-schulze.de, 1
 alexandrastorm.com, 1
 alexandrastylist.com, 0
 alexandre-blond.fr, 1
@@ -3289,16 +3579,17 @@ allbounceandplay.co.uk, 1
 allbouncesurrey.co.uk, 1
 allbrandbrand.com, 1
 allbursaries.co.za, 1
 allbusiness.com, 1
 allcapa.org, 1
 allcarecorrectionalpharmacy.com, 1
 allcarepharmacy.com, 1
 allcinema.jp, 1
+allcleaningservice.org, 1
 allcleanservices.ca, 1
 allcloud.com, 1
 allcovered.nl, 1
 alldewall.de, 1
 alldigitalsolutions.com, 1
 alldm.ru, 1
 alldolledupstore.com, 1
 alle-zonvakanties.nl, 1
@@ -3464,17 +3755,16 @@ altco.group, 1
 altea-pep18.com, 1
 altedirect.com, 1
 alteiria.fr, 1
 alter-news.fr, 1
 alterbaum.net, 1
 altered.network, 1
 altered.si, 1
 alteria.xyz, 1
-alternador.com.br, 1
 alternative.bike, 1
 alternative.hosting, 1
 alternativebit.fr, 1
 alternativedev.ca, 1
 alternativehosting.ca, 1
 alternativehosting.com, 1
 alternativeinternet.ca, 1
 alternativet.party, 1
@@ -3524,16 +3814,17 @@ alza.cz, 1
 alza.de, 1
 alza.hu, 1
 alza.sk, 1
 alzashop.com, 1
 alzonaprinting.com, 1
 am-39.com, 1
 am-dd.com, 1
 am-executive-consulting.com, 1
+am-liaotian.com, 1
 am156.com, 1
 am22i6xaf1m2a5m9k.xyz, 1
 am2s.fr, 1
 am3.se, 1
 am5039.com, 1
 am5199.com, 1
 am5566m.com, 1
 am6118.com, 1
@@ -3659,17 +3950,16 @@ aminafrance.com, 1
 amineptine.com, 1
 aminorth.com, 1
 aminullrouted.com, 1
 amionvpn.com, 1
 amir-heinisch.de, 1
 amirautos.com, 0
 amirmahdy.com, 1
 amisderodin.fr, 1
-amiserver.de, 1
 amisharingstuff.com, 1
 amitabhsirkiclasses.org.in, 1
 amitpatra.com, 1
 amiu.org, 1
 amj74-informatique.fr, 1
 amleather.pl, 1
 amm6e.com, 1
 ammanagingdirectors.com, 1
@@ -3715,17 +4005,16 @@ amyharrisonline.com, 1
 amyria.jp, 1
 amyrussellhair.com, 1
 amyyeung.com, 1
 amzn.rocks, 1
 an-alles-gedacht.de, 1
 an7hrax.se, 1
 anabolic.co, 0
 anacreon.de, 1
-anadiyogacentre.com, 1
 anaethelion.fr, 1
 anaiscoachpersonal.es, 1
 anaisfae.art, 1
 anakin.ca, 1
 anakros.me, 0
 analangelsteen.com, 1
 analbleachingguide.com, 1
 analgesia.net, 1
@@ -3935,16 +4224,17 @@ anime-rg.com, 1
 anime-tip.com, 1
 anime.my, 0
 anime1.me, 1
 anime1.moe, 1
 anime1.pw, 1
 anime1.top, 1
 animeai.com, 1
 animeclub.in.ua, 1
+animefire.net, 1
 animefluxxx.com, 1
 animeinsights.net, 1
 animeone.me, 1
 animesharp.com, 1
 animetriad.com, 1
 animojis.es, 1
 animorphsfanforum.com, 1
 anipassion.com, 0
@@ -3968,16 +4258,17 @@ ankiweb.net, 1
 ankwanoma.com, 1
 ankya9.com, 1
 anleitung-deutsch-lernen.de, 1
 anleitung-zum-flechten.de, 1
 anleitung-zum-haekeln.de, 1
 anleitung-zum-schreiben.de, 1
 anleitung-zum-schweissen.de, 1
 anleitung-zum-toepfern.de, 1
+anlovegeek.net, 1
 anlp.top, 1
 anna.info, 1
 annaenemma.nl, 1
 annafiore.com.br, 1
 annalitvinova.pro, 1
 annangela.moe, 1
 annarokina.com, 1
 annawagner.pl, 1
@@ -4115,16 +4406,17 @@ anvartay.com, 0
 anvorte.com, 0
 anwalt.us, 1
 anwaltsindex.com, 1
 anxietyspace.com, 1
 anxiolytics.com, 1
 any.pm, 0
 anyad.at, 1
 anyfood.fi, 1
+anyi.in, 1
 anymetrix.io, 1
 anyon.com, 1
 anypeer.net, 1
 anyprime.net, 1
 anyquestions.govt.nz, 1
 anystack.xyz, 1
 anyways.at, 1
 anzeiger.ag, 1
@@ -4134,16 +4426,17 @@ aoa.gov, 1
 aoadatacommunity.us, 1
 aoaprograms.net, 1
 aobeauty.com.au, 1
 aobogo.com, 1
 aod-tech.com, 1
 aoe9.com, 1
 aoeuaoeu.com, 1
 aofusa.net, 1
+aoicprobationil.gov, 1
 aoil.gr, 1
 aojiao.org, 1
 aokae.com, 1
 aoku3d.com, 1
 aooobo.com, 1
 aopedeure.nl, 1
 aopsy.de, 1
 aori.com, 1
@@ -4179,17 +4472,16 @@ aperturesciencelabs.de, 1
 apervita.net, 1
 apethink.net, 1
 apexitsolutions.ca, 1
 apfelcholest.de, 1
 apgw.jp, 1
 aphelionentertainment.com, 1
 aphotrax.eu, 1
 api-connect.com, 1
-api-geek.com, 1
 api.biz.tr, 1
 api.intercom.io, 1
 api.lookout.com, 0
 api.recurly.com, 1
 api.simple.com, 0
 api.xero.com, 0
 apiary.blog, 1
 apiary.clothing, 1
@@ -4208,16 +4500,17 @@ apio.systems, 1
 apiplus.fr, 1
 apis.blue, 1
 apis.google.com, 1
 apis.moe, 1
 apisyouwonthate.com, 1
 apiu.me, 1
 apk.li, 1
 apk4fun.com, 1
+apkmod.id, 1
 apkoyunlar.club, 1
 aplikaceproandroid.cz, 1
 aplis-online.de, 0
 aplpackaging.co.uk, 1
 aplu.fr, 1
 aplus-usa.net, 1
 aplusdownload.com, 1
 apluswaterservices.com, 1
@@ -4339,16 +4632,17 @@ aquabio.ch, 1
 aquadonis.ch, 1
 aquafc.com, 1
 aquagarden.com.pl, 1
 aquahomo.com, 1
 aquainfo.net, 1
 aqualife.com.gr, 1
 aqualifeprojects.com, 1
 aqualysis.nl, 1
+aquamarin.icu, 1
 aquapoint.kiev.ua, 1
 aquarium-supplement.net, 1
 aquaselect.eu, 1
 aquatechnologygroup.com, 1
 aquaterm72.ru, 1
 aquaundine.net, 1
 aquavitaedayspa.com.au, 1
 aquelarreweb.com, 1
@@ -4388,16 +4682,17 @@ arbitrary.ch, 1
 arboleda-hurtado.com, 1
 arboworks.com, 1
 arbu.eu, 0
 arcaea.net, 1
 arcaik.net, 1
 arcbouncycastles.co.uk, 1
 arcenergy.co.uk, 1
 archaeoadventures.com, 1
+archematerial.com, 1
 archeologicatoscana.it, 1
 archerygearonline.com, 1
 archimedicx.com, 1
 archined.nl, 1
 archit.in, 1
 architectryan.com, 1
 architecture-colleges.com, 1
 architectureandgovernance.com, 1
@@ -4499,16 +4794,17 @@ arlatools.com, 1
 arlen.tv, 1
 arlenarmageddon.com, 1
 arlet.click, 1
 arletalibrary.com, 1
 arlingtonelectric.com, 1
 arlingtonwine.net, 1
 arm-host.com, 1
 arm.gov, 1
+armadale.wa.gov.au, 1
 armadaquadrat.com, 1
 armandsdiscount.com, 1
 armanozak.com, 1
 armansfinejewellery.com, 1
 armansfinejewellery.com.au, 1
 armazemdaminiatura.com.br, 1
 armbrust.me, 1
 armedpoet.com, 1
@@ -4541,16 +4837,17 @@ arnonerba.com, 1
 arnor.org, 1
 arnoudraeven.nl, 1
 arnoudvandalen.nl, 1
 arnove.net, 1
 arnsmedia.nl, 1
 arocloud.de, 1
 arogov.com, 1
 arokha.com, 1
+aromachat.eu, 1
 aromacos.ch, 1
 aromatlas.com, 1
 aron.host, 1
 aroonchande.com, 0
 aros.pl, 1
 arose.io, 1
 around-cms.de, 1
 arox.eu, 1
@@ -4654,22 +4951,24 @@ artmaxi.eu, 1
 artmoney.com, 1
 artofcode.co.uk, 1
 artofeyes.nl, 1
 artofhomeorganizing.com, 1
 artofmonitoring.com, 0
 artofwhere.com, 1
 artozoul.fr, 1
 artplasticsurgeons.com, 1
+artransparency.gov, 1
 artratio.net, 1
 artroot.jp, 1
 artroscopiaperlosport.it, 1
 arts.gov, 1
 artschmidtoptical.com, 1
 artspac.es, 1
+arttel-media.ru, 1
 arturkohut.com, 1
 arturopinto.com.mx, 1
 arturrossa.de, 1
 arturszalak.com, 1
 artweby.cz, 1
 artworxbathrooms.com.au, 1
 arty.name, 1
 arubasunsetbeach.com, 1
@@ -4687,16 +4986,17 @@ aryabusines.com, 1
 aryalaroca.de, 1
 aryan-nation.com, 1
 aryasenna.net, 1
 arzid.com, 1
 arzinfo.pw, 1
 as200753.com, 1
 as200753.net, 1
 as44222.net, 1
+as8423.net, 1
 asadatec.de, 1
 asafaweb.com, 1
 asafilm.co, 1
 asafomba.com, 1
 asandu.eu, 1
 asanger.biz, 1
 asato-jewelry.com, 1
 asbestosthedarkarts.com, 1
@@ -4714,16 +5014,17 @@ asd.gov.au, 1
 asdyx.de, 1
 asec01.net, 1
 asegem.es, 1
 aseith.com, 1
 asemanhotel.com, 1
 asenno.com, 1
 aserver.co, 1
 asexualitat.cat, 1
+asfaleianet.gr, 1
 asgapps.co.za, 1
 ashastalent.com, 1
 ashd1.goip.de, 1
 ashd2.goip.de, 1
 ashd3.goip.de, 1
 ashessin.com, 1
 ashleakunowski.com, 1
 ashleyadum.com, 1
@@ -4868,17 +5169,16 @@ at7s.me, 1
 ataber.pw, 1
 atac.no, 1
 atacadooptico.com.br, 1
 atahualpa.com, 1
 atallo.com, 1
 atallo.es, 1
 ataton.ch, 1
 atc.cuneo.it, 1
-atc.io, 1
 atchleyjazz.com, 1
 atchleyjazz.org, 1
 atchleylab.org, 1
 atcom.cl, 1
 atcreform.gov, 1
 atds.ch, 1
 ateamsport.dk, 1
 atease-salon.jp, 1
@@ -4975,16 +5275,17 @@ atracaosexshop.com.br, 1
 atraining.ru, 1
 atraverscugy.ch, 1
 atrevillot.com, 1
 atrinik.org, 1
 atsoftware.de, 1
 atspeeds.com, 1
 attac.us, 1
 atte.fi, 1
+attendanceondemand.com, 1
 attendantdesign.com, 1
 attendu.cz, 1
 attentigroup.com, 1
 attention.horse, 1
 attilagyorffy.com, 1
 attilavandervelde.nl, 1
 attinderdhillon.com, 1
 attiremr.tk, 1
@@ -5015,17 +5316,16 @@ aubg.org, 1
 aubio.org, 1
 aubonmanger.fr, 1
 aubreysnider.com, 1
 auburn-housekeeper.com, 1
 aucarresainteloi.com, 1
 aucielrose.com, 1
 aucklandcastles.co.uk, 1
 aucubin.de, 1
-audialbuquerqueparts.com, 1
 audiense.com, 0
 audio-detector.com, 1
 audiobookboo.com, 1
 audiobookstudio.com, 1
 audioboom.com, 1
 audiolibri.org, 1
 audiolot.com, 1
 audion.cc, 1
@@ -5196,16 +5496,17 @@ autorijschooljohanbos.nl, 1
 autorijschoolrichardschut.nl, 1
 autos-mertens.com, 1
 autosaan.ro, 1
 autoschadeschreuder.nl, 1
 autoscuola.roma.it, 1
 autosecurityfinance.com, 1
 autoshinka72.ru, 1
 autoshun.org, 1
+autosites.com.au, 0
 autoskola.hr, 1
 autoskolaplzen.cz, 1
 autoskole.hr, 1
 autospurgo.it, 1
 autospurgo.milano.it, 1
 autostock.me, 1
 autostodulky.cz, 1
 autostop-occasions.be, 1
@@ -5227,16 +5528,17 @@ autouncle.pt, 1
 autouncle.ro, 1
 autouncle.se, 1
 autoverzekeringafsluiten.com, 1
 autowerkstatt-puchheim.de, 1
 autozane.com, 1
 autres-talents.fr, 1
 autshir.com, 1
 auvernet.org, 1
+auvidos.ru, 1
 aux-arts-de-la-table.com, 1
 auxiliame.com, 1
 auxille.com, 1
 auxquatrevents.ch, 1
 av-yummy.com, 1
 av01.tv, 1
 av0ndale.de, 1
 av163.cc, 1
@@ -5324,29 +5626,31 @@ avtogara-isperih.com, 1
 avtomarket.ru, 1
 avtoveles.by, 1
 avtovokzaly.ru, 1
 avvaterra.ch, 1
 avvcorda.com, 1
 avvocato.bologna.it, 1
 avxo.pw, 1
 aw.gov.pl, 1
+awakenedmind.com, 1
 awan.tech, 1
 awardplatform.com, 1
 awardsplatform.com, 1
 awaremi-tai.com, 1
 awaresec.com, 1
 awaresec.no, 1
 awarify.io, 1
 awarify.me, 1
 awaro.net, 1
 awbouncycastlehire.com, 1
 awei.pub, 1
 awesome-coconut-software.fr, 1
 awesomebouncycastles.co.uk, 1
+awesomenamegenerator.com, 1
 awesomesit.es, 1
 awf0.xyz, 1
 awic.ca, 1
 awk.tw, 1
 awksolutions.com, 1
 awningcanopyus.com, 1
 awningsaboveus.com, 1
 awningsatlantaga.com, 1
@@ -5417,16 +5721,17 @@ aziende.com.ar, 1
 azimut.fr, 1
 azino777.ru, 1
 azizvicdan.com, 0
 azlk-team.ru, 1
 azmusica.biz, 1
 aznaetelivy.ru, 1
 aznews.site, 1
 azort.com, 1
+azrangers.gov, 1
 azrazalea.net, 1
 azsgeniedev.azurewebsites.net, 1
 azso.pro, 1
 azsupport.com, 1
 aztraslochi.it, 1
 aztrix.me, 1
 aztummytuck.com, 1
 azu-l.com, 1
@@ -5474,16 +5779,17 @@ b2bmuzikbank.com, 1
 b2families.com.au, 1
 b303.me, 1
 b422edu.com, 1
 b4bouncycastles.co.uk, 1
 b4ckbone.de, 1
 b4z.eu, 1
 b5189.com, 1
 b5189.net, 1
+b5197.co, 1
 b5289.com, 1
 b5289.net, 1
 b5989.com, 1
 b5989.net, 1
 b61688.com, 1
 b64.club, 1
 b72.com, 1
 b72.net, 1
@@ -5507,16 +5813,17 @@ b9168.com, 1
 b91688.com, 1
 b91688.info, 1
 b91688.net, 1
 b91688.org, 1
 b9175.com, 1
 b9175.net, 1
 b9258.com, 1
 b9258.net, 1
+b9297.co, 1
 b9318.com, 1
 b9318.net, 1
 b9418.com, 1
 b9418.net, 1
 b9428.com, 1
 b9428.net, 1
 b9453.com, 1
 b9453.net, 1
@@ -5539,16 +5846,17 @@ b9586.net, 1
 b9588.net, 1
 b95888.net, 1
 b9589.net, 1
 b9598.com, 1
 b9598.net, 1
 b9658.com, 1
 b9658.net, 1
 b96899.com, 1
+b9728.co, 1
 b9758.com, 1
 b9758.net, 1
 b9818.com, 1
 b9818.net, 1
 b9858.com, 1
 b9858.net, 1
 b9880.com, 1
 b9883.net, 1
@@ -5716,27 +6024,28 @@ bakim.li, 1
 bakingstone.com, 1
 bakkerinjebuurt.be, 1
 bakongcondo.com, 1
 balade-commune.ch, 1
 baladecommune.ch, 1
 balance7.jp, 1
 balancedbrawl.net, 1
 balancenaturalhealthclinic.ca, 1
+balaskas.gr, 1
 balboa.io, 1
 balcaonet.com.br, 1
 balcarek.pl, 1
 balconnr.com, 1
 balconsverdun.com, 1
-baldwin.com.au, 1
 baldy.blog, 1
 baleen.us, 1
 balia.de, 1
 balicekzdravi.cz, 1
 balikonos.cz, 1
+balilingo.ooo, 0
 balinese.dating, 1
 balist.es, 1
 balivillassanur.com, 1
 baliyano.com, 1
 balkonien.org, 1
 ball-bizarr.de, 1
 ball3d.es, 1
 ballarin.cc, 1
@@ -6015,22 +6324,27 @@ baystreet.com.mt, 1
 baytalebaa.com, 1
 baywatch.io, 1
 bayz.de, 1
 baza-gai.com.ua, 1
 bazaarbhaav.com, 1
 bazaarcompass.com, 1
 bazari.com.pl, 1
 bazdell.com, 1
+bazinga-events.nl, 1
 bazos.at, 1
 bazos.cz, 1
 bazos.pl, 1
 bazos.sk, 1
 bazziergraphik.com, 1
 bb37roma.it, 1
+bb5197.co, 1
+bb9297.co, 1
+bb9721.com, 1
+bb9728.co, 1
 bbalposticino.it, 1
 bbb1991.me, 0
 bbcastles.com, 1
 bbgeschenke.ch, 1
 bbimarketing.com, 1
 bbinsure.com, 1
 bbka.org.uk, 1
 bbkaforum.co.uk, 1
@@ -6121,16 +6435,17 @@ beamitapp.com, 1
 beamstat.com, 1
 beanbagaa.com, 1
 beanbot.party, 1
 beanilla.com, 1
 beanjuice.me, 1
 beans-one.com, 0
 beansgalore.com.au, 1
 bearcms.com, 1
+beardboys.co.za, 1
 bearded.sexy, 1
 beardic.cn, 1
 bearingworks.com, 1
 bearlakelife.com, 1
 beasel.biz, 1
 beastiejob.com, 1
 beastowner.li, 1
 beatfeld.de, 1
@@ -6224,21 +6539,21 @@ beeswax-orgone.com, 1
 beetgroup.id, 1
 beethoveninlove.com, 1
 beetman.net, 1
 beeutifulparties.co.uk, 1
 beexfit.com, 1
 beezkneezcastles.co.uk, 1
 beeznest.com, 1
 befoodsafe.gov, 1
-beforesunrise.de, 1
 beforeyoueatoc.com, 1
 beframed.ch, 1
 befreewifi.info, 1
 befundonline.de, 1
+bega-dc.gov, 1
 begabungsfoerderung.info, 1
 begbie.com, 1
 beginner.nl, 1
 beginwp.top, 1
 behamepresrdce.sk, 1
 behamzdarma.cz, 1
 behead.de, 1
 beherit.pl, 1
@@ -6248,16 +6563,17 @@ behoreal.cz, 1
 bei18.com, 1
 beichtgenerator.de, 1
 beijing.dating, 1
 beijinglug.club, 1
 beimchristoph.de, 1
 beinad.com, 1
 beinad.ru, 1
 beisance.com, 1
+bejarano.io, 1
 belacapa.com.br, 1
 belanglos.de, 1
 belani.eu, 1
 belanja.express, 1
 belarto.be, 1
 belarto.de, 1
 belarto.es, 1
 belarto.fr, 1
@@ -6422,24 +6738,27 @@ berikod.ru, 1
 beringsoegaard.dk, 1
 berkat-luqs.ddns.net, 1
 berlin.dating, 1
 bermeitinger.eu, 1
 bermos.net, 1
 bermytraq.bm, 1
 berna.fr, 1
 bernadetteanderes.ch, 1
+bernama.com.my, 1
 bernar.do, 1
 bernardcontainers.be, 1
 bernarddickens.com, 1
 bernardez-photo.com, 1
 bernardgo.com, 1
 bernardo.fm, 1
 bernat.ch, 1
 bernat.im, 1
+bernbrucher.com, 1
+bernbrucher.de, 1
 bernd-leitner-fotodesign.com, 1
 bernd-leitner-fotodesign.de, 1
 bernd-leitner.de, 1
 berndklaus.at, 1
 bernhard-seidenspinner.de, 1
 bernhardkau.de, 1
 bernhardluginbuehl.ch, 1
 bernhardluginbuehl.com, 1
@@ -6678,32 +6997,30 @@ bfh.science, 1
 bfkcloud.ddns.net, 1
 bflix.tv, 1
 bfob.gg, 1
 bforb.sk, 1
 bfp-mail.de, 1
 bfpg.org, 1
 bfrailwayclub.cf, 1
 bft-media.com, 1
-bftbradio.com, 1
 bfw-online.de, 1
 bg-sexologia.com, 1
 bgbaby.net, 1
 bgbhsf.top, 1
 bgenlisted.com, 1
 bgeo.io, 1
 bgfashion.net, 1
 bgfoto.info, 1
 bghost.xyz, 1
 bgkoleda.bg, 1
 bgmn.me, 1
 bgp.ee, 1
 bgp.space, 1
 bgr34.cz, 1
-bgs-game.com, 1
 bgtgames.com, 1
 bgtoyou.com, 1
 bguidinger.com, 1
 bh-oberland.de, 1
 bh.sb, 1
 bharath-g.in, 1
 bhodisoft.com, 1
 bhserralheria.com.br, 1
@@ -6748,17 +7065,16 @@ bidman.eu, 1
 bidu.com.br, 1
 bie.edu, 0
 bie08.com, 1
 bie35.com, 1
 bie79.com, 1
 biec.moe, 1
 biegal.ski, 1
 biegner-technik.de, 1
-biehl.tech, 1
 biehlsoft.info, 1
 bielefailed.de, 1
 bien-etre-sante.info, 1
 bienici.com, 1
 bienoubien.org, 1
 biensenvue.com, 1
 bienstar.tv, 1
 bierbaumer.net, 1
@@ -6928,16 +7244,17 @@ binhp.com, 1
 binimo.com, 1
 biniou.net, 1
 binkanhada.biz, 1
 binkconsulting.be, 1
 binnenmeer.de, 1
 binsp.net, 1
 bintangsyurga.com, 1
 bintelligence.info, 1
+bintelligence.nl, 1
 binti.com, 1
 bintooshoots.com, 1
 bio-disinfestazione.it, 1
 bio-feed.org, 1
 bio24.si, 1
 bioastin.de, 1
 bioatelier.it, 1
 biobuttons.ch, 1
@@ -7298,31 +7615,33 @@ blayneallan.com, 1
 blazeit.io, 1
 blazing.cz, 1
 blazor.nl, 1
 blblblblbl.fr, 1
 bleaching-tipps.de, 1
 blechbuexn.de, 1
 bleche-onlineshop.at, 1
 bleche-onlineshop.de, 1
+blechinger.io, 1
 blechschmidt.saarland, 1
 bleep.zone, 1
 blenderinsider.com, 1
 blenderrecipereviews.com, 1
 blending.kr, 1
 blendle.com, 1
 blendle.nl, 1
 blendr.com, 1
 blendstudios.com, 1
 blenheimears.com, 1
 blenneros.net, 0
 blessedguy.com, 1
 blewebprojects.com, 1
 blichmann.eu, 1
 blicy.net, 1
+blideobames.com, 1
 blidz.com, 1
 blieque.co.uk, 1
 bliesekow.net, 1
 bliker.ga, 1
 blikk.no, 1
 blikund.swedbank.se, 1
 blindaryproduction.tk, 1
 blinder.com.co, 1
@@ -7406,17 +7725,16 @@ bloglogistics.com, 1
 bloglyric.com, 1
 blognr.com, 1
 blogom.at, 1
 blogpentrusuflet.ro, 1
 blogreen.org, 1
 blogsdna.com, 1
 blogthedayaway.com, 1
 blogtroterzy.pl, 1
-blok56.nl, 1
 blokmy.com, 1
 blood4pets.tk, 1
 bloodhunt.pl, 1
 bloodsports.org, 1
 bloody.pw, 1
 bloom-avenue.com, 1
 bloom.sh, 0
 bloombrown.com, 1
@@ -7476,16 +7794,18 @@ bluetexservice.com, 1
 bluewavewebdesign.com, 1
 bluewizardart.net, 1
 bluex.im, 1
 bluex.info, 1
 bluex.net, 1
 bluex.org, 1
 blueyed.eu, 1
 bluffplumber.co.za, 1
+blui.cf, 1
+blui.xyz, 1
 bluiandaj.ml, 1
 bluicraft.tk, 1
 bluimedia.com, 1
 bluinet.com, 1
 blumen-garage.de, 1
 blumenfeldart.com, 1
 blumiges-fischbachtal.de, 0
 blundell.wedding, 1
@@ -7741,17 +8061,16 @@ born2bounce.co.uk, 1
 bornandgrazed.com, 1
 borneodictionary.com, 1
 bornfiber.dk, 1
 bornhack.dk, 1
 borowski.pw, 1
 borrelpartybus.nl, 1
 borysek.net, 1
 borysenko.se, 1
-borzoi.com.br, 1
 bosabosa.org, 1
 boschee.net, 1
 boscoyacht.ch, 1
 boskeopolis-stories.com, 1
 boss.az, 1
 bostadsportal.se, 1
 bostonadvisors.com, 1
 bostonaoii.com, 1
@@ -7781,16 +8100,17 @@ bouah.net, 1
 bouchard-mathieux.com, 1
 bouchonville-knifemaker.com, 1
 bouckaert-usedcars.be, 1
 boudah.pl, 1
 bougeret.fr, 1
 bouk.co, 1
 boukoubengo.com, 1
 bouldercolorado.gov, 1
+boulderlibrary.org, 1
 boulderswap.com, 1
 boulzicourt.fr, 1
 bounce-a-mania.co.uk, 1
 bounce-a-roo.co.uk, 1
 bounce-abouts.com, 1
 bounce-n-go.co.uk, 1
 bounce-on.co.uk, 1
 bounce-r-us.co.uk, 1
@@ -8015,25 +8335,28 @@ branchtrack.com, 1
 brandand.co.uk, 1
 brandbil.dk, 1
 brandbuilderwebsites.com, 1
 brandcodeconsulting.com, 1
 brandcodestyle.com, 1
 brandingclic.com, 1
 brandingclick.com, 1
 brando753.xyz, 1
+brandondivorcelawyer.com, 1
 brandongomez.me, 1
 brandonhaynesmd.com, 1
 brandonhubbard.com, 1
+brandonlui.com, 1
 brandonlui.ml, 1
 brandontaylor-black.com, 1
 brandonwalker.me, 1
 brandrocket.dk, 1
 brandstead.com, 1
 brandtrapselfie.nl, 1
+brandweerbarboek.nl, 1
 brandweerfraneker.nl, 1
 brandweertrainingen.nl, 1
 brandweeruitgeest.nl, 1
 brank.as, 1
 branno.org, 1
 branw.xyz, 0
 brasal.ma, 1
 brasalcosmetics.com, 1
@@ -8123,16 +8446,17 @@ brian-gordon.name, 1
 brianalaway.com, 1
 brianalawayconsulting.com, 1
 briandwells.com, 1
 brianfoshee.com, 1
 briangarcia.ga, 1
 brianjohnson.co.za, 1
 brianlanders.us, 1
 brianroadifer.com, 1
+briansemrau.com, 1
 briansmith.org, 1
 briantkatch.com, 1
 brianwesaala.com, 1
 briarproject.org, 1
 brickftp.com, 1
 brickheroes.com, 1
 brickstreettrio.com, 1
 brickvortex.com, 1
@@ -8206,16 +8530,17 @@ bro.hk, 1
 broadbandnd.com, 1
 broadleft.org, 1
 broadsheet.com.au, 1
 brockmeyer.net, 1
 brockmeyer.org, 1
 brodowski.cc, 1
 brody.digital, 1
 brody.ninja, 1
+broerict.nl, 1
 broersma.com, 1
 broerweb.nl, 1
 broeselei.at, 1
 brokenhands.io, 1
 brokernet.ie, 0
 brokervalues.com, 1
 brompton-cocktail.com, 1
 bronevichok.ru, 1
@@ -8282,16 +8607,17 @@ brycecanyonnationalpark.com, 1
 bryggebladet.dk, 1
 brzy-svoji.cz, 1
 bs-network.net, 1
 bs-security.com, 1
 bs.sb, 1
 bs.to, 1
 bs12v.ru, 1
 bsa157.org, 1
+bsaft.ml, 1
 bsalyzer.com, 1
 bsapack564.org, 1
 bsatroop794.org, 1
 bsc-rietz.at, 1
 bsc01.dyndns.org, 1
 bscc.support, 1
 bsd-box.net, 1
 bsd.com.ro, 1
@@ -8489,16 +8815,17 @@ burakogun.net.tr, 1
 burakogun.org, 1
 burcevo.info, 1
 burfordbedandbreakfast.co.uk, 1
 burg-hohnstein.com, 1
 burgawnc.gov, 1
 burgernet.nl, 1
 burgers.io, 1
 burghardt.pl, 1
+buri.be, 0
 burialinsurancenetwork.com, 1
 buricloud.fr, 1
 burienergy.com, 1
 burke.services, 1
 burkhardt.at, 1
 burlapsac.ca, 1
 burncorp.org, 1
 burnerfitness.com, 1
@@ -8524,16 +8851,17 @@ burzmedia.com, 1
 burzstudios.com, 1
 burzum.ch, 1
 busanhs.bid, 1
 buscandolosmejores.com, 1
 buserror.cn, 1
 bushbaby.com, 1
 bushcraftfriends.com, 1
 busindre.com, 1
+business-creators.ru, 1
 business-garden.com, 1
 business.facebook.com, 0
 business.gov, 1
 businessadviceperth.com.au, 0
 businessamongus.com, 1
 businesscentermarin.ch, 1
 businessesdirectory.eu, 1
 businessetmarketing.com, 1
@@ -8681,17 +9009,16 @@ byteflies.com, 1
 bytegoing.com, 1
 bytejail.com, 1
 bytema.cz, 1
 bytema.eu, 1
 bytema.re, 1
 bytema.sk, 1
 bytemix.cloud, 1
 bytenoc.nl, 1
-bytepark.de, 1
 bytepen.com, 1
 bytes.co, 1
 bytes.fyi, 1
 bytesatwork.de, 1
 byteshark.org, 1
 byteshift.ca, 1
 bytesign.de, 1
 bytesizedalex.com, 1
@@ -8733,19 +9060,24 @@ c3.pm, 1
 c3hv.cn, 1
 c3sign.de, 0
 c3vo.de, 1
 c3w.at, 1
 c3wien.at, 1
 c3woc.de, 0
 c4539.com, 1
 c4k3.net, 1
+c5197.co, 1
 c5h8no4na.net, 1
 c7dn.com, 1
 c8ms113.com, 1
+c9297.co, 1
+c9397.com, 1
+c9721.com, 1
+c9728.co, 1
 ca-key.de, 1
 ca.gparent.org, 1
 ca.search.yahoo.com, 0
 ca5.de, 1
 caaps.org.au, 1
 caarecord.org, 1
 caasd.org, 1
 cabaladada.org, 1
@@ -8754,34 +9086,32 @@ cabarave.com, 1
 cabforum.org, 1
 cabineritten.nl, 1
 cabinet-bedin.com, 1
 cabinetfurnituree.com, 1
 cablehighspeed.net, 1
 cablemod.com, 1
 cablesandkits.com, 1
 cabotfinancial.co.uk, 1
-cabuna.hr, 1
 cacao-chocolate.com, 1
 cacao.supply, 1
 cacaolalina.com, 1
 cacd.eu, 1
 caceis.bank, 1
 cachacacha.com, 1
 cachedview.nl, 1
 cachetagalong.com, 0
 cachethome.com, 1
 cachetur.no, 1
 cackette.com, 1
 cacn.pw, 1
 cacr.pw, 1
 cad-noerdlingen.de, 1
 cadafamilia.de, 1
 cadams.io, 1
-cadcreations.co.ke, 1
 cadetsge.ch, 1
 cadmail.nl, 1
 cadman.pw, 1
 cadmanlaw.ca, 1
 cadmanlaw.com, 1
 cadooz.com, 1
 cadorama.fr, 1
 cadoth.net, 1
@@ -8994,16 +9324,18 @@ canadianoutdoorequipment.com, 1
 canadiantouristboard.com, 1
 canal-onanismo.org, 1
 canalecontracting.com, 1
 canalsidehouse.be, 1
 canalsidehouse.com, 1
 canariculturacolor.com, 1
 canarymod.net, 1
 canavillage.net, 1
+canavillagepuntacana.com, 1
+canavillageresidences.com, 1
 canberraoutletcentre.com.au, 1
 cancelmyprofile.com, 1
 cancerdata.nhs.uk, 1
 candaceplayforth.com, 1
 candelec.com, 1
 candeo-books.nl, 1
 candex.com, 1
 candguchocolat.com, 1
@@ -9256,16 +9588,17 @@ carriedin.com, 1
 carrierplatform.com, 1
 carringtonrealtygroup.com, 1
 carroattrezzimilanodaluiso.it, 1
 carroceriascarluis.com, 1
 carrouselcompany.fr, 1
 cars4salecy.com, 1
 carseatchecks.ca, 1
 carshippingcarriers.com, 1
+carson-aviation-adventures.com, 1
 carson-matthews.co.uk, 1
 carsoug.com, 1
 carspneu.cz, 1
 cartelloni.roma.it, 1
 carterdan.net, 1
 carterorland.com, 1
 carterstad.se, 1
 cartertonscouts.org.nz, 1
@@ -9337,16 +9670,17 @@ casino-cash-flow.su, 1
 casino-cashflow.ru, 1
 casino-online.info, 1
 casino-trio.com, 1
 casinobonuscodes.online, 1
 casinocashflow.ru, 1
 casinochecking.com, 1
 casinolegal.pt, 1
 casinolistings.com, 1
+casinomegaslotos.com, 1
 casinomucho.com, 1
 casinomucho.org, 1
 casinomucho.se, 1
 casinoonlinesicuri.com, 1
 casinoportugal.pt, 1
 casinoreal.com, 1
 casinorewards.info, 1
 casinovergleich.com, 1
@@ -9427,22 +9761,22 @@ cathyjf.ca, 1
 cathyjf.com, 1
 cathyjf.net, 1
 cathyjf.org, 1
 cathyjfitzpatrick.com, 1
 cativa.net, 1
 catl.st, 1
 catlovingcare.com, 1
 catmoose.ca, 1
-catnet.dk, 0
 catnmeow.com, 1
 catram.org, 1
 catsmagic.pp.ua, 1
 cattivo.nl, 0
 catuniverse.org, 1
+catus.moe, 1
 catveteran.com, 1
 caughtredhanded.co.nz, 1
 caulfieldeastapartments.com.au, 1
 caulfieldracecourseapartments.com.au, 1
 caulong-ao.net, 1
 cav.ac, 1
 cavac.at, 1
 cavalierkingcharlesspaniel.com.br, 1
@@ -9470,16 +9804,21 @@ cbi-epa.gov, 1
 cbin168.com, 1
 cbintermountainrealty.com, 1
 cbk-connect.com, 1
 cbmusa.com, 1
 cbr-xml-daily.ru, 1
 cbsdeheidevlinder.nl, 1
 cbw.sh, 1
 cc-customer.de, 1
+cc5197.co, 1
+cc9297.co, 1
+cc9397.com, 1
+cc9721.com, 1
+cc9728.co, 1
 ccac.gov, 1
 ccattestprep.com, 1
 ccavenue.com, 1
 ccayearbook.com, 1
 ccc-ch.ch, 1
 ccc-cloud.de, 1
 cccwien.at, 1
 cceputnam360.com, 1
@@ -9531,29 +9870,31 @@ cdu-gebhardshain.de, 1
 cduckett.net, 1
 cdvl.org, 1
 ce-agentur.de, 0
 ce-pimkie.fr, 1
 ce-webdesign.de, 1
 ceanimalhealth.com, 1
 cebz.org, 1
 cecame.ch, 1
+cecilga.gov, 1
 ceciliacolombara.com, 1
 cecipu.gob.cl, 1
 ced-services.nl, 1
 cedarcitydining.com, 1
 cedarslodge.com, 1
 cedricbonhomme.org, 1
 cedriccassimo.ch, 1
 cedriccassimo.com, 1
 cee.io, 1
 ceebee.com, 1
 cegfw.com, 1
 cegss.org.gt, 1
 ceilingpac.org, 1
+ceiphr.com, 1
 cejhon.cz, 0
 celcomhomefibre.com.my, 1
 cele.bi, 1
 celebmasta.com, 1
 celebphotos.blog, 1
 celebrasianconference.com, 1
 celebrityhealthcritic.com, 1
 celebrityscope.net, 1
@@ -9563,16 +9904,17 @@ celestebonito.pt, 1
 celiendev.ch, 1
 celine-patisserie.fr, 1
 cell-lookup.com, 1
 cellartracker.com, 1
 cellebrite.com, 1
 celliberate.co.uk, 1
 celltek-server.de, 0
 celltesequ.com, 1
+celltick.com, 1
 celluliteorangeskin.com, 1
 celluliteremovaldiet.com, 1
 celti.ie.eu.org, 1
 celti.name, 1
 celuliteonline.com, 1
 cementscience.com, 1
 cemeteriat.com, 1
 ceml.ch, 1
@@ -9691,16 +10033,17 @@ cfc-swc.gc.ca, 1
 cfda.gov, 1
 cfdcre5.org, 1
 cfh.com, 1
 cfigura.com, 1
 cflsystems.com, 1
 cfno.org, 1
 cfo.gov, 1
 cfpa-formation.fr, 1
+cfrq.ca, 1
 cfsh.tk, 1
 cftc.gov, 1
 cftcarouge.com, 1
 cfttt.com, 1
 cfurl.cf, 1
 cfxdesign.com, 1
 cg-goerlitz.de, 1
 cg-systems.hu, 1
@@ -10059,16 +10402,17 @@ chilimathwords.com, 1
 chilio.net, 1
 chillebever.nl, 1
 chima.net, 1
 chima.us, 1
 chimeratool.com, 1
 chimerity.com, 1
 chimho.de, 1
 chimpanzee.net, 1
+chimparoo.ca, 1
 china-line.org, 1
 chinacdn.org, 1
 chinahighlights.ru, 1
 chinaspaceflight.com, 1
 chinawhale.com, 1
 ching.tv, 1
 chinookwebdesign.ca, 1
 chint.ai, 1
@@ -10108,17 +10452,16 @@ chmurakotori.ml, 1
 choc-o-lush.co.uk, 1
 chocgu.com, 1
 chocolah.com.au, 0
 chocolat-suisse.ch, 1
 chocolat.work, 1
 chocolatesandhealth.com, 1
 chocolatier-tristan.ch, 1
 chocolytech.info, 1
-chocotough.nl, 1
 chocoweb.net, 1
 choe.fi, 1
 choiceautoloan.com, 1
<