Bug 1552596 - Revert dynamic line clamping in favor of performant static line clamping a=jcristau
authorEd Lee <edilee@mozilla.com>
Mon, 17 Jun 2019 18:31:13 +0300
changeset 536982 23e2a740c60f3c90d0242cc21f0025bc0b26debe
parent 536981 1468b59c257087317384bee06b4a8250faa26a58
child 536983 c6cf0c6f2c8933b53f6112865be891feb549b301
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjcristau
bugs1552596
milestone68.0
Bug 1552596 - Revert dynamic line clamping in favor of performant static line clamping a=jcristau Reviewers: thecount Reviewed By: thecount Bug #: 1552596 Differential Revision: https://phabricator.services.mozilla.com/D34001
browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/_Hero.scss
browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/List.jsx
browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/_List.scss
browser/components/newtab/content-src/lib/clamp-total-lines.js
browser/components/newtab/content-src/styles/_mixins.scss
browser/components/newtab/css/activity-stream-linux.css
browser/components/newtab/css/activity-stream-mac.css
browser/components/newtab/css/activity-stream-windows.css
browser/components/newtab/data/content/activity-stream.bundle.js
browser/components/newtab/test/unit/content-src/lib/clamp-total-lines.test.js
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
@@ -1,10 +1,9 @@
 import {actionCreators as ac} from "common/Actions.jsm";
-import {clampTotalLines} from "content-src/lib/clamp-total-lines";
 import {DSImage} from "../DSImage/DSImage.jsx";
 import {DSLinkMenu} from "../DSLinkMenu/DSLinkMenu";
 import {ImpressionStats} from "../../DiscoveryStreamImpressionStats/ImpressionStats";
 import React from "react";
 import {SafeAnchor} from "../SafeAnchor/SafeAnchor";
 
 export class DSCard extends React.PureComponent {
   constructor(props) {
@@ -36,21 +35,19 @@ export class DSCard extends React.PureCo
           className="ds-card-link"
           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"
-              data-total-lines="7"
-              ref={clampTotalLines}>
-              <p className="source clamp" data-clamp="1">{this.props.source}</p>
-              <header className="title clamp" data-clamp="4">{this.props.title}</header>
+            <div className="info-wrap">
+              <p className="source clamp">{this.props.source}</p>
+              <header className="title clamp">{this.props.title}</header>
               {this.props.excerpt && <p className="excerpt clamp">{this.props.excerpt}</p>}
             </div>
             {this.props.context && (
               <p className="context">{this.props.context}</p>
             )}
           </div>
           <ImpressionStats
             campaignId={this.props.campaignId}
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
@@ -100,16 +100,17 @@
     }
 
     .excerpt {
       // show only 3 lines of copy
       @include limit-visibile-lines(3, $excerpt-line-height, $excerpt-font-size);
     }
 
     .source {
+      -webkit-line-clamp: 1;
       margin-bottom: 2px;
     }
 
     .context,
     .source {
       @include dark-theme-only {
         color: $grey-40;
       }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
@@ -1,11 +1,10 @@
 import {DSCard, PlaceholderDSCard} from "../DSCard/DSCard.jsx";
 import {actionCreators as ac} from "common/Actions.jsm";
-import {clampTotalLines} from "content-src/lib/clamp-total-lines";
 import {DSEmptyState} from "../DSEmptyState/DSEmptyState.jsx";
 import {DSImage} from "../DSImage/DSImage.jsx";
 import {DSLinkMenu} from "../DSLinkMenu/DSLinkMenu";
 import {ImpressionStats} from "../../DiscoveryStreamImpressionStats/ImpressionStats";
 import {List} from "../List/List.jsx";
 import React from "react";
 import {SafeAnchor} from "../SafeAnchor/SafeAnchor";
 
@@ -72,25 +71,23 @@ export class Hero extends React.PureComp
             className="wrapper"
             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"
-                data-total-lines="7"
-                ref={clampTotalLines}>
+              <div className="header-and-excerpt">
                 {heroRec.context ? (
                   <p className="context">{heroRec.context}</p>
                 ) : (
-                  <p className="source clamp" data-clamp="1">{heroRec.domain}</p>
+                  <p className="source clamp">{heroRec.domain}</p>
                 )}
-                <header className="clamp" data-clamp="4">{heroRec.title}</header>
+                <header className="clamp">{heroRec.title}</header>
                 <p className="excerpt clamp">{heroRec.excerpt}</p>
               </div>
             </div>
             <ImpressionStats
               campaignId={heroRec.campaignId}
               rows={[{id: heroRec.id, pos: heroRec.pos}]}
               dispatch={this.props.dispatch}
               source={this.props.type} />
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/_Hero.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/_Hero.scss
@@ -142,16 +142,17 @@
 
       .source {
         @include dark-theme-only {
           color: $grey-40;
         }
 
         font-size: 13px;
         color: $grey-50;
+        -webkit-line-clamp: 1;
         margin-bottom: 0;
       }
     }
   }
 
   // "2/3 width layout"
   .ds-column-5 &,
   .ds-column-6 &,
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/List.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/List.jsx
@@ -1,10 +1,9 @@
 import {actionCreators as ac} from "common/Actions.jsm";
-import {clampTotalLines} from "content-src/lib/clamp-total-lines";
 import {connect} from "react-redux";
 import {DSEmptyState} from "../DSEmptyState/DSEmptyState.jsx";
 import {DSImage} from "../DSImage/DSImage.jsx";
 import {DSLinkMenu} from "../DSLinkMenu/DSLinkMenu";
 import {ImpressionStats} from "../../DiscoveryStreamImpressionStats/ImpressionStats";
 import React from "react";
 import {SafeAnchor} from "../SafeAnchor/SafeAnchor";
 
@@ -39,25 +38,24 @@ export class ListItem extends React.Pure
     return (
       <li className={`ds-list-item${this.props.placeholder ? " placeholder" : ""}`} >
         <SafeAnchor
           className="ds-list-item-link"
           dispatch={this.props.dispatch}
           onLinkClick={!this.props.placeholder ? this.onLinkClick : undefined}
           url={this.props.url}>
           <div className="ds-list-item-text">
-            <div data-total-lines="4"
-              ref={clampTotalLines}>
+            <div>
               <div className="ds-list-item-title clamp">{this.props.title}</div>
               {this.props.excerpt && <div className="ds-list-item-excerpt clamp">{this.props.excerpt}</div>}
             </div>
             <p>
               {this.props.context && (
                 <span>
-                  <span className="ds-list-item-context">{this.props.context}</span>
+                  <span className="ds-list-item-context clamp">{this.props.context}</span>
                   <br />
                 </span>
               )}
               <span className="ds-list-item-info clamp">{this.props.domain}</span>
             </p>
           </div>
           <DSImage extraClassNames="ds-list-image" source={this.props.image_src} rawSource={this.props.raw_image_src} />
           <ImpressionStats
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/_List.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/_List.scss
@@ -223,17 +223,16 @@
   .ds-list-item-context {
     @include limit-visibile-lines(1, $item-line-height, $item-font-size);
     @include dark-theme-only {
       color: $grey-40;
     }
 
     color: $grey-50;
     font-size: 13px;
-    -webkit-line-clamp: 1;
   }
 
   .ds-list-item-title {
     font-weight: 600;
     margin-bottom: 4px;
   }
 
   .ds-list-item-text {
deleted file mode 100644
--- a/browser/components/newtab/content-src/lib/clamp-total-lines.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Adjusts line-clamps of a node's children to fill a desired number of lines.
- *
- * This is a React callback ref that should be set on a parent node that also
- * has a data-total-lines attribute. Children with "clamp" class name are
- * clamped to allow as many lines to earlier children while making sure every
- * child gets at least one line. Each child can be explicitly clamped to a max
- * lines with a data-clamp attribute.
- */
-export function clampTotalLines(parentNode) {
-  // Nothing to do if the node is removed or didn't configure how many lines
-  if (!parentNode || !parentNode.dataset.totalLines) {
-    return;
-  }
-
-  // Only handle clamp-able children that are displayed (not hidden)
-  const toClamp = Array.from(parentNode.querySelectorAll(".clamp"))
-    .filter(child => child.scrollHeight);
-
-  // Start with total allowed lines while reserving 1 line for each child
-  let maxLines = parentNode.dataset.totalLines - toClamp.length + 1;
-  toClamp.forEach(child => {
-    // Clamp to the remaining allowed, explicit limit or the natural line count
-    const lines = Math.min(maxLines,
-      child.dataset.clamp || Infinity,
-      child.scrollHeight / parseInt(global.getComputedStyle(child).lineHeight, 10));
-    child.style.webkitLineClamp = `${lines}`;
-
-    // Update the remaining line allowance less the already reserved 1 line
-    maxLines -= lines - 1;
-  });
-}
--- a/browser/components/newtab/content-src/styles/_mixins.scss
+++ b/browser/components/newtab/content-src/styles/_mixins.scss
@@ -4,19 +4,20 @@
   background-position: center;
   background-repeat: no-repeat;
   background-size: cover;
   border-radius: 4px;
   box-shadow: inset 0 0 0 0.5px $black-15;
 }
 
 // Note: lineHeight and fontSize should be unitless but can be derived from pixel values
-// Bug 1550624 to clean up / remove this mixin that no longer limits lines
+// Bug 1550624 to clean up / remove this mixin to avoid duplicate styles
 @mixin limit-visibile-lines($line-count, $line-height, $font-size) {
   font-size: $font-size * 1px;
+  -webkit-line-clamp: $line-count;
   line-height: $line-height * 1px;
 }
 
 @mixin dark-theme-only {
   [lwt-newtab-brighttext] & {
     @content;
   }
 }
--- a/browser/components/newtab/css/activity-stream-linux.css
+++ b/browser/components/newtab/css/activity-stream-linux.css
@@ -1951,29 +1951,31 @@ main {
       .ds-column-12 .ds-card-grid.ds-card-grid-divisible-by-3 .title {
         font-size: 17px;
         line-height: 24px; }
     .ds-column-9 .ds-card-grid.ds-card-grid-divisible-by-4 .title,
     .ds-column-10 .ds-card-grid.ds-card-grid-divisible-by-4 .title,
     .ds-column-11 .ds-card-grid.ds-card-grid-divisible-by-4 .title,
     .ds-column-12 .ds-card-grid.ds-card-grid-divisible-by-4 .title {
       font-size: 15px;
+      -webkit-line-clamp: 3;
       line-height: 20px; }
   .ds-card-grid.empty {
     grid-template-columns: auto; }
 
 .ds-hero {
   position: relative; }
   .ds-hero header {
     font-weight: 600; }
   .ds-hero p {
     line-height: 1.538;
     margin: 8px 0; }
   .ds-hero .excerpt {
     font-size: 14px;
+    -webkit-line-clamp: 3;
     line-height: 20px;
     color: #0C0C0D;
     margin: 0 0 10px; }
     [lwt-newtab-brighttext] .ds-hero .excerpt {
       color: #F9F9FA; }
   .ds-hero .ds-card:not(.placeholder) {
     border: 0;
     padding-bottom: 20px; }
@@ -2031,31 +2033,33 @@ main {
         border-radius: 4px;
         box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15); }
     .ds-hero .wrapper .meta {
       display: block;
       flex-direction: column;
       justify-content: space-between; }
       .ds-hero .wrapper .meta header {
         font-size: 22px;
+        -webkit-line-clamp: 4;
         line-height: 28px;
         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;
+        -webkit-line-clamp: 1;
         margin-bottom: 0; }
         [lwt-newtab-brighttext] .ds-hero .wrapper .meta .source {
           color: #B1B1B3; }
   .ds-column-5 .ds-hero .wrapper,
   .ds-column-6 .ds-hero .wrapper,
   .ds-column-7 .ds-hero .wrapper,
   .ds-column-8 .ds-hero .wrapper {
     display: grid;
@@ -2147,16 +2151,17 @@ main {
         flex-grow: 1;
         display: flex;
         padding: 0 24px 0 0; }
         .ds-column-9 .ds-hero .wrapper .meta header,
         .ds-column-10 .ds-hero .wrapper .meta header,
         .ds-column-11 .ds-hero .wrapper .meta header,
         .ds-column-12 .ds-hero .wrapper .meta header {
           font-size: 22px;
+          -webkit-line-clamp: 3;
           line-height: 28px; }
         .ds-column-9 .ds-hero .wrapper .meta .source,
         .ds-column-10 .ds-hero .wrapper .meta .source,
         .ds-column-11 .ds-hero .wrapper .meta .source,
         .ds-column-12 .ds-hero .wrapper .meta .source {
           margin-bottom: 0; }
     .ds-column-9 .ds-hero .cards,
     .ds-column-10 .ds-hero .cards,
@@ -2181,16 +2186,17 @@ main {
       .ds-column-11 .ds-hero .cards .ds-card:active .title, [lwt-newtab-brighttext]
       .ds-column-12 .ds-hero .cards .ds-card:active .title {
         color: #0A84FF; }
       .ds-column-9 .ds-hero .cards .ds-card .title,
       .ds-column-10 .ds-hero .cards .ds-card .title,
       .ds-column-11 .ds-hero .cards .ds-card .title,
       .ds-column-12 .ds-hero .cards .ds-card .title {
         font-size: 14px;
+        -webkit-line-clamp: 3;
         line-height: 20px; }
         [lwt-newtab-brighttext] .ds-column-9 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]
         .ds-column-10 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]
         .ds-column-11 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]
         .ds-column-12 .ds-hero .cards .ds-card .title {
           color: #FFF; }
   .ds-hero.empty {
     grid-template-columns: auto; }
@@ -2208,16 +2214,17 @@ main {
   grid-column-gap: 24px;
   padding-inline-start: 0; }
   .ds-list:not(.ds-list-full-width) .ds-list-item {
     font-size: 14px;
     line-height: 20px;
     position: relative; }
   .ds-list:not(.ds-list-full-width) .ds-list-item-title {
     font-size: 14px;
+    -webkit-line-clamp: 3;
     line-height: 20px; }
   .ds-list:not(.ds-list-full-width) .ds-list-image {
     min-width: 80px;
     width: 80px; }
   .ds-column-5 .ds-list:not(.ds-list-full-width),
   .ds-column-6 .ds-list:not(.ds-list-full-width),
   .ds-column-7 .ds-list:not(.ds-list-full-width),
   .ds-column-8 .ds-list:not(.ds-list-full-width) {
@@ -2320,16 +2327,17 @@ main {
 
 .ds-list-full-width .ds-list-item {
   font-size: 17px;
   line-height: 24px;
   position: relative; }
 
 .ds-list-full-width .ds-list-item-title {
   font-size: 17px;
+  -webkit-line-clamp: 3;
   line-height: 24px; }
 
 .ds-list-full-width .ds-list-image {
   min-width: 160px;
   width: 160px; }
 
 .ds-list-item {
   display: block;
@@ -2345,32 +2353,33 @@ main {
       opacity: 0; }
   .ds-list-item .ds-list-item-link {
     mix-blend-mode: normal;
     display: flex;
     justify-content: space-between;
     height: 100%; }
   .ds-list-item .ds-list-item-excerpt {
     font-size: 14px;
+    -webkit-line-clamp: 2;
     line-height: 20px;
     color: #737373;
     margin: 4px 0 8px; }
     [lwt-newtab-brighttext] .ds-list-item .ds-list-item-excerpt {
       color: rgba(249, 249, 250, 0.8); }
   .ds-list-item p {
     font-size: 14px;
     line-height: 20px;
     margin: 0; }
   .ds-list-item .ds-list-item-info,
   .ds-list-item .ds-list-item-context {
     font-size: 14px;
+    -webkit-line-clamp: 1;
     line-height: 20px;
     color: #737373;
-    font-size: 13px;
-    -webkit-line-clamp: 1; }
+    font-size: 13px; }
     [lwt-newtab-brighttext] .ds-list-item .ds-list-item-info, [lwt-newtab-brighttext]
     .ds-list-item .ds-list-item-context {
       color: #B1B1B3; }
   .ds-list-item .ds-list-item-title {
     font-weight: 600;
     margin-bottom: 4px; }
   .ds-list-item .ds-list-item-text {
     display: flex;
@@ -2638,22 +2647,25 @@ main {
     flex-direction: column;
     flex-grow: 1;
     padding: 12px; }
     .ds-card .meta .info-wrap {
       flex-grow: 1;
       margin: 0 0 12px; }
     .ds-card .meta .title {
       font-size: 17px;
+      -webkit-line-clamp: 3;
       line-height: 24px;
       font-weight: 600; }
     .ds-card .meta .excerpt {
       font-size: 14px;
+      -webkit-line-clamp: 3;
       line-height: 20px; }
     .ds-card .meta .source {
+      -webkit-line-clamp: 1;
       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; }
--- a/browser/components/newtab/css/activity-stream-mac.css
+++ b/browser/components/newtab/css/activity-stream-mac.css
@@ -1954,29 +1954,31 @@ main {
       .ds-column-12 .ds-card-grid.ds-card-grid-divisible-by-3 .title {
         font-size: 17px;
         line-height: 24px; }
     .ds-column-9 .ds-card-grid.ds-card-grid-divisible-by-4 .title,
     .ds-column-10 .ds-card-grid.ds-card-grid-divisible-by-4 .title,
     .ds-column-11 .ds-card-grid.ds-card-grid-divisible-by-4 .title,
     .ds-column-12 .ds-card-grid.ds-card-grid-divisible-by-4 .title {
       font-size: 15px;
+      -webkit-line-clamp: 3;
       line-height: 20px; }
   .ds-card-grid.empty {
     grid-template-columns: auto; }
 
 .ds-hero {
   position: relative; }
   .ds-hero header {
     font-weight: 600; }
   .ds-hero p {
     line-height: 1.538;
     margin: 8px 0; }
   .ds-hero .excerpt {
     font-size: 14px;
+    -webkit-line-clamp: 3;
     line-height: 20px;
     color: #0C0C0D;
     margin: 0 0 10px; }
     [lwt-newtab-brighttext] .ds-hero .excerpt {
       color: #F9F9FA; }
   .ds-hero .ds-card:not(.placeholder) {
     border: 0;
     padding-bottom: 20px; }
@@ -2034,31 +2036,33 @@ main {
         border-radius: 4px;
         box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15); }
     .ds-hero .wrapper .meta {
       display: block;
       flex-direction: column;
       justify-content: space-between; }
       .ds-hero .wrapper .meta header {
         font-size: 22px;
+        -webkit-line-clamp: 4;
         line-height: 28px;
         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;
+        -webkit-line-clamp: 1;
         margin-bottom: 0; }
         [lwt-newtab-brighttext] .ds-hero .wrapper .meta .source {
           color: #B1B1B3; }
   .ds-column-5 .ds-hero .wrapper,
   .ds-column-6 .ds-hero .wrapper,
   .ds-column-7 .ds-hero .wrapper,
   .ds-column-8 .ds-hero .wrapper {
     display: grid;
@@ -2150,16 +2154,17 @@ main {
         flex-grow: 1;
         display: flex;
         padding: 0 24px 0 0; }
         .ds-column-9 .ds-hero .wrapper .meta header,
         .ds-column-10 .ds-hero .wrapper .meta header,
         .ds-column-11 .ds-hero .wrapper .meta header,
         .ds-column-12 .ds-hero .wrapper .meta header {
           font-size: 22px;
+          -webkit-line-clamp: 3;
           line-height: 28px; }
         .ds-column-9 .ds-hero .wrapper .meta .source,
         .ds-column-10 .ds-hero .wrapper .meta .source,
         .ds-column-11 .ds-hero .wrapper .meta .source,
         .ds-column-12 .ds-hero .wrapper .meta .source {
           margin-bottom: 0; }
     .ds-column-9 .ds-hero .cards,
     .ds-column-10 .ds-hero .cards,
@@ -2184,16 +2189,17 @@ main {
       .ds-column-11 .ds-hero .cards .ds-card:active .title, [lwt-newtab-brighttext]
       .ds-column-12 .ds-hero .cards .ds-card:active .title {
         color: #0A84FF; }
       .ds-column-9 .ds-hero .cards .ds-card .title,
       .ds-column-10 .ds-hero .cards .ds-card .title,
       .ds-column-11 .ds-hero .cards .ds-card .title,
       .ds-column-12 .ds-hero .cards .ds-card .title {
         font-size: 14px;
+        -webkit-line-clamp: 3;
         line-height: 20px; }
         [lwt-newtab-brighttext] .ds-column-9 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]
         .ds-column-10 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]
         .ds-column-11 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]
         .ds-column-12 .ds-hero .cards .ds-card .title {
           color: #FFF; }
   .ds-hero.empty {
     grid-template-columns: auto; }
@@ -2211,16 +2217,17 @@ main {
   grid-column-gap: 24px;
   padding-inline-start: 0; }
   .ds-list:not(.ds-list-full-width) .ds-list-item {
     font-size: 14px;
     line-height: 20px;
     position: relative; }
   .ds-list:not(.ds-list-full-width) .ds-list-item-title {
     font-size: 14px;
+    -webkit-line-clamp: 3;
     line-height: 20px; }
   .ds-list:not(.ds-list-full-width) .ds-list-image {
     min-width: 80px;
     width: 80px; }
   .ds-column-5 .ds-list:not(.ds-list-full-width),
   .ds-column-6 .ds-list:not(.ds-list-full-width),
   .ds-column-7 .ds-list:not(.ds-list-full-width),
   .ds-column-8 .ds-list:not(.ds-list-full-width) {
@@ -2323,16 +2330,17 @@ main {
 
 .ds-list-full-width .ds-list-item {
   font-size: 17px;
   line-height: 24px;
   position: relative; }
 
 .ds-list-full-width .ds-list-item-title {
   font-size: 17px;
+  -webkit-line-clamp: 3;
   line-height: 24px; }
 
 .ds-list-full-width .ds-list-image {
   min-width: 160px;
   width: 160px; }
 
 .ds-list-item {
   display: block;
@@ -2348,32 +2356,33 @@ main {
       opacity: 0; }
   .ds-list-item .ds-list-item-link {
     mix-blend-mode: normal;
     display: flex;
     justify-content: space-between;
     height: 100%; }
   .ds-list-item .ds-list-item-excerpt {
     font-size: 14px;
+    -webkit-line-clamp: 2;
     line-height: 20px;
     color: #737373;
     margin: 4px 0 8px; }
     [lwt-newtab-brighttext] .ds-list-item .ds-list-item-excerpt {
       color: rgba(249, 249, 250, 0.8); }
   .ds-list-item p {
     font-size: 14px;
     line-height: 20px;
     margin: 0; }
   .ds-list-item .ds-list-item-info,
   .ds-list-item .ds-list-item-context {
     font-size: 14px;
+    -webkit-line-clamp: 1;
     line-height: 20px;
     color: #737373;
-    font-size: 13px;
-    -webkit-line-clamp: 1; }
+    font-size: 13px; }
     [lwt-newtab-brighttext] .ds-list-item .ds-list-item-info, [lwt-newtab-brighttext]
     .ds-list-item .ds-list-item-context {
       color: #B1B1B3; }
   .ds-list-item .ds-list-item-title {
     font-weight: 600;
     margin-bottom: 4px; }
   .ds-list-item .ds-list-item-text {
     display: flex;
@@ -2641,22 +2650,25 @@ main {
     flex-direction: column;
     flex-grow: 1;
     padding: 12px; }
     .ds-card .meta .info-wrap {
       flex-grow: 1;
       margin: 0 0 12px; }
     .ds-card .meta .title {
       font-size: 17px;
+      -webkit-line-clamp: 3;
       line-height: 24px;
       font-weight: 600; }
     .ds-card .meta .excerpt {
       font-size: 14px;
+      -webkit-line-clamp: 3;
       line-height: 20px; }
     .ds-card .meta .source {
+      -webkit-line-clamp: 1;
       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; }
--- a/browser/components/newtab/css/activity-stream-windows.css
+++ b/browser/components/newtab/css/activity-stream-windows.css
@@ -1951,29 +1951,31 @@ main {
       .ds-column-12 .ds-card-grid.ds-card-grid-divisible-by-3 .title {
         font-size: 17px;
         line-height: 24px; }
     .ds-column-9 .ds-card-grid.ds-card-grid-divisible-by-4 .title,
     .ds-column-10 .ds-card-grid.ds-card-grid-divisible-by-4 .title,
     .ds-column-11 .ds-card-grid.ds-card-grid-divisible-by-4 .title,
     .ds-column-12 .ds-card-grid.ds-card-grid-divisible-by-4 .title {
       font-size: 15px;
+      -webkit-line-clamp: 3;
       line-height: 20px; }
   .ds-card-grid.empty {
     grid-template-columns: auto; }
 
 .ds-hero {
   position: relative; }
   .ds-hero header {
     font-weight: 600; }
   .ds-hero p {
     line-height: 1.538;
     margin: 8px 0; }
   .ds-hero .excerpt {
     font-size: 14px;
+    -webkit-line-clamp: 3;
     line-height: 20px;
     color: #0C0C0D;
     margin: 0 0 10px; }
     [lwt-newtab-brighttext] .ds-hero .excerpt {
       color: #F9F9FA; }
   .ds-hero .ds-card:not(.placeholder) {
     border: 0;
     padding-bottom: 20px; }
@@ -2031,31 +2033,33 @@ main {
         border-radius: 4px;
         box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15); }
     .ds-hero .wrapper .meta {
       display: block;
       flex-direction: column;
       justify-content: space-between; }
       .ds-hero .wrapper .meta header {
         font-size: 22px;
+        -webkit-line-clamp: 4;
         line-height: 28px;
         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;
+        -webkit-line-clamp: 1;
         margin-bottom: 0; }
         [lwt-newtab-brighttext] .ds-hero .wrapper .meta .source {
           color: #B1B1B3; }
   .ds-column-5 .ds-hero .wrapper,
   .ds-column-6 .ds-hero .wrapper,
   .ds-column-7 .ds-hero .wrapper,
   .ds-column-8 .ds-hero .wrapper {
     display: grid;
@@ -2147,16 +2151,17 @@ main {
         flex-grow: 1;
         display: flex;
         padding: 0 24px 0 0; }
         .ds-column-9 .ds-hero .wrapper .meta header,
         .ds-column-10 .ds-hero .wrapper .meta header,
         .ds-column-11 .ds-hero .wrapper .meta header,
         .ds-column-12 .ds-hero .wrapper .meta header {
           font-size: 22px;
+          -webkit-line-clamp: 3;
           line-height: 28px; }
         .ds-column-9 .ds-hero .wrapper .meta .source,
         .ds-column-10 .ds-hero .wrapper .meta .source,
         .ds-column-11 .ds-hero .wrapper .meta .source,
         .ds-column-12 .ds-hero .wrapper .meta .source {
           margin-bottom: 0; }
     .ds-column-9 .ds-hero .cards,
     .ds-column-10 .ds-hero .cards,
@@ -2181,16 +2186,17 @@ main {
       .ds-column-11 .ds-hero .cards .ds-card:active .title, [lwt-newtab-brighttext]
       .ds-column-12 .ds-hero .cards .ds-card:active .title {
         color: #0A84FF; }
       .ds-column-9 .ds-hero .cards .ds-card .title,
       .ds-column-10 .ds-hero .cards .ds-card .title,
       .ds-column-11 .ds-hero .cards .ds-card .title,
       .ds-column-12 .ds-hero .cards .ds-card .title {
         font-size: 14px;
+        -webkit-line-clamp: 3;
         line-height: 20px; }
         [lwt-newtab-brighttext] .ds-column-9 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]
         .ds-column-10 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]
         .ds-column-11 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]
         .ds-column-12 .ds-hero .cards .ds-card .title {
           color: #FFF; }
   .ds-hero.empty {
     grid-template-columns: auto; }
@@ -2208,16 +2214,17 @@ main {
   grid-column-gap: 24px;
   padding-inline-start: 0; }
   .ds-list:not(.ds-list-full-width) .ds-list-item {
     font-size: 14px;
     line-height: 20px;
     position: relative; }
   .ds-list:not(.ds-list-full-width) .ds-list-item-title {
     font-size: 14px;
+    -webkit-line-clamp: 3;
     line-height: 20px; }
   .ds-list:not(.ds-list-full-width) .ds-list-image {
     min-width: 80px;
     width: 80px; }
   .ds-column-5 .ds-list:not(.ds-list-full-width),
   .ds-column-6 .ds-list:not(.ds-list-full-width),
   .ds-column-7 .ds-list:not(.ds-list-full-width),
   .ds-column-8 .ds-list:not(.ds-list-full-width) {
@@ -2320,16 +2327,17 @@ main {
 
 .ds-list-full-width .ds-list-item {
   font-size: 17px;
   line-height: 24px;
   position: relative; }
 
 .ds-list-full-width .ds-list-item-title {
   font-size: 17px;
+  -webkit-line-clamp: 3;
   line-height: 24px; }
 
 .ds-list-full-width .ds-list-image {
   min-width: 160px;
   width: 160px; }
 
 .ds-list-item {
   display: block;
@@ -2345,32 +2353,33 @@ main {
       opacity: 0; }
   .ds-list-item .ds-list-item-link {
     mix-blend-mode: normal;
     display: flex;
     justify-content: space-between;
     height: 100%; }
   .ds-list-item .ds-list-item-excerpt {
     font-size: 14px;
+    -webkit-line-clamp: 2;
     line-height: 20px;
     color: #737373;
     margin: 4px 0 8px; }
     [lwt-newtab-brighttext] .ds-list-item .ds-list-item-excerpt {
       color: rgba(249, 249, 250, 0.8); }
   .ds-list-item p {
     font-size: 14px;
     line-height: 20px;
     margin: 0; }
   .ds-list-item .ds-list-item-info,
   .ds-list-item .ds-list-item-context {
     font-size: 14px;
+    -webkit-line-clamp: 1;
     line-height: 20px;
     color: #737373;
-    font-size: 13px;
-    -webkit-line-clamp: 1; }
+    font-size: 13px; }
     [lwt-newtab-brighttext] .ds-list-item .ds-list-item-info, [lwt-newtab-brighttext]
     .ds-list-item .ds-list-item-context {
       color: #B1B1B3; }
   .ds-list-item .ds-list-item-title {
     font-weight: 600;
     margin-bottom: 4px; }
   .ds-list-item .ds-list-item-text {
     display: flex;
@@ -2638,22 +2647,25 @@ main {
     flex-direction: column;
     flex-grow: 1;
     padding: 12px; }
     .ds-card .meta .info-wrap {
       flex-grow: 1;
       margin: 0 0 12px; }
     .ds-card .meta .title {
       font-size: 17px;
+      -webkit-line-clamp: 3;
       line-height: 24px;
       font-weight: 600; }
     .ds-card .meta .excerpt {
       font-size: 14px;
+      -webkit-line-clamp: 3;
       line-height: 20px; }
     .ds-card .meta .source {
+      -webkit-line-clamp: 1;
       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; }
--- a/browser/components/newtab/data/content/activity-stream.bundle.js
+++ b/browser/components/newtab/data/content/activity-stream.bundle.js
@@ -87,25 +87,25 @@
 /******/ ([
 /* 0 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var content_src_components_Base_Base__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
-/* harmony import */ var content_src_lib_detect_user_session_start__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(52);
+/* harmony import */ var content_src_lib_detect_user_session_start__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(51);
 /* harmony import */ var content_src_lib_init_store__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(7);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(26);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_4__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_5__);
 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(16);
 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_6__);
-/* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(58);
+/* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(57);
 
 
 
 
 
 
 
 
@@ -565,23 +565,23 @@ var actionUtils = {
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var content_src_components_ASRouterAdmin_ASRouterAdmin__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
 /* harmony import */ var _asrouter_asrouter_content__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6);
 /* harmony import */ var content_src_components_ConfirmDialog_ConfirmDialog__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(29);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(26);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_5__);
-/* harmony import */ var content_src_components_DiscoveryStreamBase_DiscoveryStreamBase__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(53);
-/* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(36);
-/* harmony import */ var common_PrerenderData_jsm__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(46);
+/* harmony import */ var content_src_components_DiscoveryStreamBase_DiscoveryStreamBase__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(52);
+/* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(35);
+/* harmony import */ var common_PrerenderData_jsm__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(45);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_9__);
-/* harmony import */ var content_src_components_Search_Search__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(47);
-/* harmony import */ var content_src_components_Sections_Sections__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(48);
+/* harmony import */ var content_src_components_Search_Search__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(46);
+/* harmony import */ var content_src_components_Sections_Sections__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(47);
 function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
 
 
 
 
 
 
 
@@ -1765,25 +1765,25 @@ const ASRouterAdmin = Object(react_redux
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ASRouterUtils", function() { return ASRouterUtils; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ASRouterUISurface", function() { return ASRouterUISurface; });
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
 /* harmony import */ var content_src_lib_init_store__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
 /* harmony import */ var _rich_text_strings__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
 /* harmony import */ var _components_ImpressionsWrapper_ImpressionsWrapper__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(10);
-/* harmony import */ var fluent_react__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(56);
+/* harmony import */ var fluent_react__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(55);
 /* harmony import */ var content_src_lib_constants__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(13);
 /* harmony import */ var _templates_OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(14);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_8__);
 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(16);
 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_9__);
 /* harmony import */ var _templates_ReturnToAMO_ReturnToAMO__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(17);
-/* harmony import */ var _templates_template_manifest__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(54);
+/* harmony import */ var _templates_template_manifest__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(53);
 /* harmony import */ var _templates_StartupOverlay_StartupOverlay__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(25);
 /* harmony import */ var _templates_Trailhead_Trailhead__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(27);
 function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
 
 
 
 
 
@@ -2437,17 +2437,17 @@ module.exports = Redux;
 /***/ }),
 /* 9 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RICH_TEXT_KEYS", function() { return RICH_TEXT_KEYS; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "generateMessages", function() { return generateMessages; });
-/* harmony import */ var fluent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(55);
+/* harmony import */ var fluent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(54);
 
 /**
  * Properties that allow rich text MUST be added to this list.
  *   key: the localization_id that should be used
  *   value: a property or array of properties on the message.content object
  */
 
 const RICH_TEXT_CONFIG = {
@@ -2874,17 +2874,17 @@ class ReturnToAMO extends react__WEBPACK
 /***/ }),
 /* 18 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "convertLinks", function() { return convertLinks; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RichText", function() { return RichText; });
-/* harmony import */ var fluent_react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56);
+/* harmony import */ var fluent_react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(55);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _rich_text_strings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9);
 /* harmony import */ var _template_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(19);
 function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
 
 
 
@@ -3906,61 +3906,25 @@ class _ConfirmDialog extends react__WEBP
 const ConfirmDialog = Object(react_redux__WEBPACK_IMPORTED_MODULE_1__["connect"])(state => state.Dialog)(_ConfirmDialog);
 
 /***/ }),
 /* 30 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
-/* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clampTotalLines", function() { return clampTotalLines; });
-/**
- * Adjusts line-clamps of a node's children to fill a desired number of lines.
- *
- * This is a React callback ref that should be set on a parent node that also
- * has a data-total-lines attribute. Children with "clamp" class name are
- * clamped to allow as many lines to earlier children while making sure every
- * child gets at least one line. Each child can be explicitly clamped to a max
- * lines with a data-clamp attribute.
- */
-function clampTotalLines(parentNode) {
-  // Nothing to do if the node is removed or didn't configure how many lines
-  if (!parentNode || !parentNode.dataset.totalLines) {
-    return;
-  } // Only handle clamp-able children that are displayed (not hidden)
-
-
-  const toClamp = Array.from(parentNode.querySelectorAll(".clamp")).filter(child => child.scrollHeight); // Start with total allowed lines while reserving 1 line for each child
-
-  let maxLines = parentNode.dataset.totalLines - toClamp.length + 1;
-  toClamp.forEach(child => {
-    // Clamp to the remaining allowed, explicit limit or the natural line count
-    const lines = Math.min(maxLines, child.dataset.clamp || Infinity, child.scrollHeight / parseInt(global.getComputedStyle(child).lineHeight, 10));
-    child.style.webkitLineClamp = `${lines}`; // Update the remaining line allowance less the already reserved 1 line
-
-    maxLines -= lines - 1;
-  });
-}
-/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
-
-/***/ }),
-/* 31 */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_LinkMenu", function() { return _LinkMenu; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LinkMenu", function() { return LinkMenu; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(26);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_1__);
-/* harmony import */ var content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(32);
+/* harmony import */ var content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(31);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_3__);
-/* harmony import */ var content_src_lib_link_menu_options__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(33);
+/* harmony import */ var content_src_lib_link_menu_options__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(32);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_5__);
 
 
 
 
 
 
@@ -4036,17 +4000,17 @@ class _LinkMenu extends react__WEBPACK_I
 const getState = state => ({
   isPrivateBrowsingEnabled: state.Prefs.values.isPrivateBrowsingEnabled,
   platform: state.Prefs.values.platform
 });
 
 const LinkMenu = Object(react_redux__WEBPACK_IMPORTED_MODULE_1__["connect"])(getState)(Object(react_intl__WEBPACK_IMPORTED_MODULE_3__["injectIntl"])(_LinkMenu));
 
 /***/ }),
-/* 32 */
+/* 31 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ContextMenu", function() { return ContextMenu; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ContextMenuItem", function() { return ContextMenuItem; });
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
@@ -4156,17 +4120,17 @@ class ContextMenuItem extends react__WEB
       className: `icon icon-spacer icon-${option.icon}`
     }), option.label));
   }
 
 }
 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
 
 /***/ }),
-/* 33 */
+/* 32 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GetPlatformString", function() { return GetPlatformString; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LinkMenuOptions", function() { return LinkMenuOptions; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 
@@ -4449,17 +4413,17 @@ const LinkMenuOptions = {
   CheckBookmark: site => site.bookmarkGuid ? LinkMenuOptions.RemoveBookmark(site) : LinkMenuOptions.AddBookmark(site),
   CheckPinTopSite: (site, index) => site.isPinned ? LinkMenuOptions.UnpinTopSite(site) : LinkMenuOptions.PinTopSite(site, index),
   CheckSavedToPocket: (site, index) => site.pocket_id ? LinkMenuOptions.DeleteFromPocket(site) : LinkMenuOptions.SaveToPocket(site, index),
   CheckBookmarkOrArchive: site => site.pocket_id ? LinkMenuOptions.ArchiveFromPocket(site) : LinkMenuOptions.CheckBookmark(site),
   OpenInPrivateWindow: (site, index, eventSource, isEnabled) => isEnabled ? _OpenInPrivateWindow(site) : LinkMenuOptions.EmptyItem()
 };
 
 /***/ }),
-/* 34 */
+/* 33 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "INTERSECTION_RATIO", function() { return INTERSECTION_RATIO; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ImpressionStats", function() { return ImpressionStats; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11);
@@ -4670,31 +4634,31 @@ ImpressionStats.defaultProps = {
   IntersectionObserver: global.IntersectionObserver,
   document: global.document,
   rows: [],
   source: ""
 };
 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
 
 /***/ }),
-/* 35 */
+/* 34 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_CollapsibleSection", function() { return _CollapsibleSection; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CollapsibleSection", function() { return CollapsibleSection; });
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
-/* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(36);
+/* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(35);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_3__);
-/* harmony import */ var content_src_components_SectionMenu_SectionMenu__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(37);
-/* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(38);
+/* harmony import */ var content_src_components_SectionMenu_SectionMenu__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(36);
+/* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(37);
 
 
 
 
 
 
 const VISIBLE = "visible";
 const VISIBILITY_CHANGE_EVENT = "visibilitychange";
@@ -4966,17 +4930,17 @@ class _CollapsibleSection extends react_
   Prefs: {
     values: {}
   }
 };
 const CollapsibleSection = Object(react_intl__WEBPACK_IMPORTED_MODULE_0__["injectIntl"])(_CollapsibleSection);
 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
 
 /***/ }),
-/* 36 */
+/* 35 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ErrorBoundaryFallback", function() { return ErrorBoundaryFallback; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ErrorBoundary", function() { return ErrorBoundary; });
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_0__);
@@ -5055,30 +5019,30 @@ class ErrorBoundary extends react__WEBPA
   }
 
 }
 ErrorBoundary.defaultProps = {
   FallbackComponent: ErrorBoundaryFallback
 };
 
 /***/ }),
-/* 37 */
+/* 36 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_SectionMenu", function() { return _SectionMenu; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SectionMenu", function() { return SectionMenu; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* harmony import */ var content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(32);
+/* harmony import */ var content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(31);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_3__);
-/* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(38);
+/* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(37);
 
 
 
 
 
 const DEFAULT_SECTION_MENU_OPTIONS = ["MoveUp", "MoveDown", "Separator", "RemoveSection", "CheckCollapsed", "Separator", "ManageSection"];
 const WEBEXT_SECTION_MENU_OPTIONS = ["MoveUp", "MoveDown", "Separator", "CheckCollapsed", "Separator", "ManageWebExtension"];
 class _SectionMenu extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent {
@@ -5143,17 +5107,17 @@ class _SectionMenu extends react__WEBPAC
       options: this.getOptions()
     });
   }
 
 }
 const SectionMenu = Object(react_intl__WEBPACK_IMPORTED_MODULE_2__["injectIntl"])(_SectionMenu);
 
 /***/ }),
-/* 38 */
+/* 37 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SectionMenuOptions", function() { return SectionMenuOptions; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 
 /**
@@ -5270,37 +5234,37 @@ const SectionMenuOptions = {
       }
     }),
     userEvent: "MENU_PRIVACY_NOTICE"
   }),
   CheckCollapsed: section => section.collapsed ? SectionMenuOptions.ExpandSection(section) : SectionMenuOptions.CollapseSection(section)
 };
 
 /***/ }),
-/* 39 */
+/* 38 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_TopSites", function() { return _TopSites; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSites", function() { return TopSites; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(40);
-/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(35);
-/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(41);
+/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(39);
+/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(34);
+/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(40);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(26);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_4__);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_5__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_6__);
-/* harmony import */ var _SearchShortcutsForm__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(43);
-/* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(58);
-/* harmony import */ var _TopSiteForm__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(57);
-/* harmony import */ var _TopSite__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(44);
+/* harmony import */ var _SearchShortcutsForm__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(42);
+/* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(57);
+/* harmony import */ var _TopSiteForm__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(56);
+/* harmony import */ var _TopSite__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(43);
 function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
 
 
 
 
 
 
 
@@ -5500,17 +5464,17 @@ class _TopSites extends react__WEBPACK_I
 const TopSites = Object(react_redux__WEBPACK_IMPORTED_MODULE_4__["connect"])(state => ({
   TopSites: state.TopSites,
   Prefs: state.Prefs,
   TopSitesRows: state.Prefs.values.topSitesRows
 }))(Object(react_intl__WEBPACK_IMPORTED_MODULE_5__["injectIntl"])(_TopSites));
 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
 
 /***/ }),
-/* 40 */
+/* 39 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOP_SITES_SOURCE", function() { return TOP_SITES_SOURCE; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOP_SITES_CONTEXT_MENU_OPTIONS", function() { return TOP_SITES_CONTEXT_MENU_OPTIONS; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS", function() { return TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MIN_RICH_FAVICON_SIZE", function() { return MIN_RICH_FAVICON_SIZE; });
@@ -5520,24 +5484,24 @@ const TOP_SITES_CONTEXT_MENU_OPTIONS = [
 
 const TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "BlockUrl"]; // minimum size necessary to show a rich icon instead of a screenshot
 
 const MIN_RICH_FAVICON_SIZE = 96; // minimum size necessary to show any icon in the top left corner with a screenshot
 
 const MIN_CORNER_FAVICON_SIZE = 16;
 
 /***/ }),
-/* 41 */
+/* 40 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ComponentPerfTimer", function() { return ComponentPerfTimer; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* harmony import */ var common_PerfService_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(42);
+/* harmony import */ var common_PerfService_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(41);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_2__);
 
 
  // Currently record only a fixed set of sections. This will prevent data
 // from custom sections from showing up or from topstories.
 
 const RECORDED_SECTIONS = ["highlights", "topsites"];
@@ -5697,17 +5661,17 @@ class ComponentPerfTimer extends react__
     }
 
     return this.props.children;
   }
 
 }
 
 /***/ }),
-/* 42 */
+/* 41 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_PerfService", function() { return _PerfService; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "perfService", function() { return perfService; });
 
 
@@ -5835,29 +5799,29 @@ function _PerfService(options) {
     let mostRecentEntry = entries[entries.length - 1];
     return this._perf.timeOrigin + mostRecentEntry.startTime;
   }
 
 };
 var perfService = new _PerfService();
 
 /***/ }),
-/* 43 */
+/* 42 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SelectableSearchShortcut", function() { return SelectableSearchShortcut; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SearchShortcutsForm", function() { return SearchShortcutsForm; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_2__);
-/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(40);
+/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(39);
 
 
 
 
 class SelectableSearchShortcut extends react__WEBPACK_IMPORTED_MODULE_2___default.a.PureComponent {
   render() {
     const {
       shortcut,
@@ -6029,35 +5993,35 @@ class SearchShortcutsForm extends react_
     }, react__WEBPACK_IMPORTED_MODULE_2___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__["FormattedMessage"], {
       id: "topsites_form_save_button"
     }))));
   }
 
 }
 
 /***/ }),
-/* 44 */
+/* 43 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSiteLink", function() { return TopSiteLink; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSite", function() { return TopSite; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSitePlaceholder", function() { return TopSitePlaceholder; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_TopSiteList", function() { return _TopSiteList; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSiteList", function() { return TopSiteList; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
-/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(40);
-/* harmony import */ var content_src_components_LinkMenu_LinkMenu__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(31);
+/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(39);
+/* harmony import */ var content_src_components_LinkMenu_LinkMenu__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(30);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_4__);
-/* harmony import */ var content_src_lib_screenshot_utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(45);
-/* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(58);
+/* harmony import */ var content_src_lib_screenshot_utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(44);
+/* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(57);
 function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
 
 
 
 
 
 
 
@@ -6688,17 +6652,17 @@ class _TopSiteList extends react__WEBPAC
       className: `top-sites-list${this.state.draggedSite ? " dnd-active" : ""}`
     }, topSitesUI);
   }
 
 }
 const TopSiteList = Object(react_intl__WEBPACK_IMPORTED_MODULE_1__["injectIntl"])(_TopSiteList);
 
 /***/ }),
-/* 45 */
+/* 44 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ScreenshotUtils", function() { return ScreenshotUtils; });
 /**
  * List of helper functions for screenshot-based images.
  *
@@ -6753,17 +6717,17 @@ const ScreenshotUtils = {
 
     return !remoteImage && !localImage;
   }
 
 };
 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
 
 /***/ }),
-/* 46 */
+/* 45 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_PrerenderData", function() { return _PrerenderData; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PrerenderData", function() { return PrerenderData; });
 class _PrerenderData {
   constructor(options) {
@@ -6891,17 +6855,17 @@ var PrerenderData = new _PrerenderData({
     order: 2,
     title: {
       id: "header_highlights"
     }
   }]
 });
 
 /***/ }),
-/* 47 */
+/* 46 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_Search", function() { return _Search; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Search", function() { return Search; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
@@ -7099,39 +7063,39 @@ class _Search extends react__WEBPACK_IMP
       ref: this.onInputMount
     })));
   }
 
 }
 const Search = Object(react_redux__WEBPACK_IMPORTED_MODULE_2__["connect"])()(Object(react_intl__WEBPACK_IMPORTED_MODULE_1__["injectIntl"])(_Search));
 
 /***/ }),
-/* 48 */
+/* 47 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Section", function() { return Section; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SectionIntl", function() { return SectionIntl; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_Sections", function() { return _Sections; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Sections", function() { return Sections; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* harmony import */ var content_src_components_Card_Card__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(59);
+/* harmony import */ var content_src_components_Card_Card__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(58);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_2__);
-/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(35);
-/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(41);
+/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(34);
+/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(40);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(26);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_5__);
-/* harmony import */ var content_src_components_MoreRecommendations_MoreRecommendations__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(49);
-/* harmony import */ var content_src_components_PocketLoggedInCta_PocketLoggedInCta__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(50);
+/* harmony import */ var content_src_components_MoreRecommendations_MoreRecommendations__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(48);
+/* harmony import */ var content_src_components_PocketLoggedInCta_PocketLoggedInCta__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(49);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_8__);
-/* harmony import */ var content_src_components_Topics_Topics__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(51);
-/* harmony import */ var content_src_components_TopSites_TopSites__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(39);
+/* harmony import */ var content_src_components_Topics_Topics__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(50);
+/* harmony import */ var content_src_components_TopSites_TopSites__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(38);
 function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
 
 
 
 
 
 
 
@@ -7470,17 +7434,17 @@ class _Sections extends react__WEBPACK_I
 }
 const Sections = Object(react_redux__WEBPACK_IMPORTED_MODULE_5__["connect"])(state => ({
   Sections: state.Sections,
   Prefs: state.Prefs
 }))(_Sections);
 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
 
 /***/ }),
-/* 49 */
+/* 48 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MoreRecommendations", function() { return MoreRecommendations; });
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11);
@@ -7503,17 +7467,17 @@ class MoreRecommendations extends react_
     }
 
     return null;
   }
 
 }
 
 /***/ }),
-/* 50 */
+/* 49 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_PocketLoggedInCta", function() { return _PocketLoggedInCta; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PocketLoggedInCta", function() { return PocketLoggedInCta; });
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(26);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_0__);
@@ -7546,17 +7510,17 @@ class _PocketLoggedInCta extends react__
   }
 
 }
 const PocketLoggedInCta = Object(react_redux__WEBPACK_IMPORTED_MODULE_0__["connect"])(state => ({
   Pocket: state.Pocket
 }))(_PocketLoggedInCta);
 
 /***/ }),
-/* 51 */
+/* 50 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Topic", function() { return Topic; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Topics", function() { return Topics; });
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_0__);
@@ -7591,24 +7555,24 @@ class Topics extends react__WEBPACK_IMPO
       url: t.url,
       name: t.name
     }))));
   }
 
 }
 
 /***/ }),
-/* 52 */
+/* 51 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DetectUserSessionStart", function() { return DetectUserSessionStart; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* harmony import */ var common_PerfService_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(42);
+/* harmony import */ var common_PerfService_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(41);
 
 
 const VISIBLE = "visible";
 const VISIBILITY_CHANGE_EVENT = "visibilitychange";
 class DetectUserSessionStart {
   constructor(store, options = {}) {
     this._store = store; // Overrides for testing
 
@@ -7670,28 +7634,25 @@ class DetectUserSessionStart {
       this.document.removeEventListener(VISIBILITY_CHANGE_EVENT, this._onVisibilityChange);
     }
   }
 
 }
 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
 
 /***/ }),
-/* 53 */
+/* 52 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 
 // EXTERNAL MODULE: ./common/Actions.jsm
 var Actions = __webpack_require__(2);
 
-// EXTERNAL MODULE: ./content-src/lib/clamp-total-lines.js
-var clamp_total_lines = __webpack_require__(30);
-
 // EXTERNAL MODULE: external "React"
 var external_React_ = __webpack_require__(11);
 var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
 
 // EXTERNAL MODULE: external "ReactDOM"
 var external_ReactDOM_ = __webpack_require__(16);
 var external_ReactDOM_default = /*#__PURE__*/__webpack_require__.n(external_ReactDOM_);
 
@@ -7809,17 +7770,17 @@ DSImage_DSImage.defaultProps = {
   // Additional classnames to append to component
   optimize: true // Measure parent container to request exact sizes
 
 };
 // EXTERNAL MODULE: external "ReactIntl"
 var external_ReactIntl_ = __webpack_require__(4);
 
 // EXTERNAL MODULE: ./content-src/components/LinkMenu/LinkMenu.jsx
-var LinkMenu = __webpack_require__(31);
+var LinkMenu = __webpack_require__(30);
 
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx
 
 
 
 class DSLinkMenu_DSLinkMenu extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
@@ -7903,17 +7864,17 @@ class DSLinkMenu_DSLinkMenu extends exte
         bookmarkGuid: this.props.bookmarkGuid
       }
     }));
   }
 
 }
 const DSLinkMenu = Object(external_ReactIntl_["injectIntl"])(DSLinkMenu_DSLinkMenu);
 // EXTERNAL MODULE: ./content-src/components/DiscoveryStreamImpressionStats/ImpressionStats.jsx
-var ImpressionStats = __webpack_require__(34);
+var ImpressionStats = __webpack_require__(33);
 
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx
 
 
 class SafeAnchor_SafeAnchor extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onClick = this.onClick.bind(this);
@@ -7988,17 +7949,16 @@ class SafeAnchor_SafeAnchor extends exte
 }
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
 
 
 
 
 
 
-
 class DSCard_DSCard extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onLinkClick = this.onLinkClick.bind(this);
   }
 
   onLinkClick(event) {
     if (this.props.dispatch) {
@@ -8030,25 +7990,21 @@ class DSCard_DSCard extends external_Rea
       className: "img-wrapper"
     }, 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",
-      "data-total-lines": "7",
-      ref: clamp_total_lines["clampTotalLines"]
+      className: "info-wrap"
     }, external_React_default.a.createElement("p", {
-      className: "source clamp",
-      "data-clamp": "1"
+      className: "source clamp"
     }, this.props.source), external_React_default.a.createElement("header", {
-      className: "title clamp",
-      "data-clamp": "4"
+      className: "title clamp"
     }, this.props.title), this.props.excerpt && external_React_default.a.createElement("p", {
       className: "excerpt clamp"
     }, this.props.excerpt)), this.props.context && external_React_default.a.createElement("p", {
       className: "context"
     }, this.props.context)), external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
       campaignId: this.props.campaignId,
       rows: [{
         id: this.props.id,
@@ -8151,17 +8107,17 @@ class CardGrid_CardGrid extends external
 
 }
 CardGrid_CardGrid.defaultProps = {
   border: `border`,
   items: 4 // Number of stories to display
 
 };
 // EXTERNAL MODULE: ./content-src/components/CollapsibleSection/CollapsibleSection.jsx
-var CollapsibleSection = __webpack_require__(35);
+var CollapsibleSection = __webpack_require__(34);
 
 // EXTERNAL MODULE: external "ReactRedux"
 var external_ReactRedux_ = __webpack_require__(26);
 
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSMessage/DSMessage.jsx
 
 
 class DSMessage_DSMessage extends external_React_default.a.PureComponent {
@@ -8188,17 +8144,16 @@ class DSMessage_DSMessage extends extern
 
 
 
 
 
 
 
 
-
 /**
  * @note exported for testing only
  */
 
 class List_ListItem extends external_React_default.a.PureComponent {
   // TODO performance: get feeds to send appropriately sized images rather
   // than waiting longer and scaling down on client?
   constructor(props) {
@@ -8229,25 +8184,22 @@ class List_ListItem extends external_Rea
       className: `ds-list-item${this.props.placeholder ? " placeholder" : ""}`
     }, external_React_default.a.createElement(SafeAnchor_SafeAnchor, {
       className: "ds-list-item-link",
       dispatch: this.props.dispatch,
       onLinkClick: !this.props.placeholder ? this.onLinkClick : undefined,
       url: this.props.url
     }, external_React_default.a.createElement("div", {
       className: "ds-list-item-text"
-    }, external_React_default.a.createElement("div", {
-      "data-total-lines": "4",
-      ref: clamp_total_lines["clampTotalLines"]
-    }, external_React_default.a.createElement("div", {
+    }, external_React_default.a.createElement("div", null, external_React_default.a.createElement("div", {
       className: "ds-list-item-title clamp"
     }, this.props.title), this.props.excerpt && external_React_default.a.createElement("div", {
       className: "ds-list-item-excerpt clamp"
     }, 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", {
-      className: "ds-list-item-context"
+      className: "ds-list-item-context clamp"
     }, this.props.context), external_React_default.a.createElement("br", null)), external_React_default.a.createElement("span", {
       className: "ds-list-item-info clamp"
     }, this.props.domain))), external_React_default.a.createElement(DSImage_DSImage, {
       extraClassNames: "ds-list-image",
       source: this.props.image_src,
       rawSource: this.props.raw_image_src
     }), external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
       campaignId: this.props.campaignId,
@@ -8349,17 +8301,16 @@ const List = Object(external_ReactRedux_
 
 
 
 
 
 
 
 
-
 class Hero_Hero extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onLinkClick = this.onLinkClick.bind(this);
   }
 
   onLinkClick(event) {
     if (this.props.dispatch) {
@@ -8422,27 +8373,23 @@ class Hero_Hero extends external_React_d
         className: "img-wrapper"
       }, 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",
-        "data-total-lines": "7",
-        ref: clamp_total_lines["clampTotalLines"]
+        className: "header-and-excerpt"
       }, heroRec.context ? external_React_default.a.createElement("p", {
         className: "context"
       }, heroRec.context) : external_React_default.a.createElement("p", {
-        className: "source clamp",
-        "data-clamp": "1"
+        className: "source clamp"
       }, heroRec.domain), external_React_default.a.createElement("header", {
-        className: "clamp",
-        "data-clamp": "4"
+        className: "clamp"
       }, heroRec.title), external_React_default.a.createElement("p", {
         className: "excerpt clamp"
       }, heroRec.excerpt))), external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
         campaignId: heroRec.campaignId,
         rows: [{
           id: heroRec.id,
           pos: heroRec.pos
         }],
@@ -8761,17 +8708,17 @@ const selectLayoutRender = (state, prefs
   }
 
   return {
     spocsFill,
     layoutRender
   };
 };
 // EXTERNAL MODULE: ./content-src/components/TopSites/TopSites.jsx
-var TopSites = __webpack_require__(39);
+var TopSites = __webpack_require__(38);
 
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/TopSites/TopSites.jsx
 
 
 
 class TopSites_TopSites extends external_React_default.a.PureComponent {
   render() {
     const header = this.props.header || {};
@@ -9074,17 +9021,17 @@ class DiscoveryStreamBase_DiscoveryStrea
 }
 const DiscoveryStreamBase = Object(external_ReactRedux_["connect"])(state => ({
   DiscoveryStream: state.DiscoveryStream,
   Prefs: state.Prefs,
   Sections: state.Sections
 }))(DiscoveryStreamBase_DiscoveryStreamBase);
 
 /***/ }),
-/* 54 */
+/* 53 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 
 // EXTERNAL MODULE: external "React"
 var external_React_ = __webpack_require__(11);
 var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
@@ -10125,17 +10072,17 @@ const SnippetsTemplates = {
   newsletter_snippet: NewsletterSnippet,
   fxa_signup_snippet: FXASignupSnippet,
   send_to_device_snippet: SendToDeviceSnippet,
   eoy_snippet: EOYSnippet,
   simple_below_search_snippet: SimpleBelowSearchSnippet_SimpleBelowSearchSnippet
 };
 
 /***/ }),
-/* 55 */
+/* 54 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 
 // CONCATENATED MODULE: ./node_modules/fluent/src/parser.js
 /*  eslint no-magic-numbers: [0]  */
 const MAX_PLACEABLES = 100;
@@ -12328,31 +12275,31 @@ function ftl(strings) {
 
 
 
 
 
 
 
 /***/ }),
-/* 56 */
+/* 55 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 
 // EXTERNAL MODULE: external "React"
 var external_React_ = __webpack_require__(11);
 
 // EXTERNAL MODULE: external "PropTypes"
 var external_PropTypes_ = __webpack_require__(12);
 var external_PropTypes_default = /*#__PURE__*/__webpack_require__.n(external_PropTypes_);
 
 // EXTERNAL MODULE: ./node_modules/fluent/src/index.js + 8 modules
-var src = __webpack_require__(55);
+var src = __webpack_require__(54);
 
 // CONCATENATED MODULE: ./node_modules/fluent-react/src/localization.js
 
 /*
  * `ReactLocalization` handles translation formatting and fallback.
  *
  * The current negotiated fallback chain of languages is stored in the
  * `ReactLocalization` instance in form of an iterable of `MessageContext`
@@ -12856,17 +12803,17 @@ localized_Localized.propTypes = {
  * components for more information.
  */
 
 
 
 
 
 /***/ }),
-/* 57 */
+/* 56 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 
 // EXTERNAL MODULE: ./common/Actions.jsm
 var Actions = __webpack_require__(2);
 
@@ -12891,17 +12838,17 @@ function A11yLinkButton(props) {
   }, props, {
     className: className
   }), props.children);
 }
 // EXTERNAL MODULE: external "ReactIntl"
 var external_ReactIntl_ = __webpack_require__(4);
 
 // EXTERNAL MODULE: ./content-src/components/TopSites/TopSitesConstants.js
-var TopSitesConstants = __webpack_require__(40);
+var TopSitesConstants = __webpack_require__(39);
 
 // CONCATENATED MODULE: ./content-src/components/TopSites/TopSiteFormInput.jsx
 
 
 class TopSiteFormInput_TopSiteFormInput extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.state = {
@@ -13007,17 +12954,17 @@ class TopSiteFormInput_TopSiteFormInput 
 
 }
 TopSiteFormInput_TopSiteFormInput.defaultProps = {
   showClearButton: false,
   value: "",
   validationError: false
 };
 // EXTERNAL MODULE: ./content-src/components/TopSites/TopSite.jsx
-var TopSite = __webpack_require__(44);
+var TopSite = __webpack_require__(43);
 
 // CONCATENATED MODULE: ./content-src/components/TopSites/TopSiteForm.jsx
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSiteForm", function() { return TopSiteForm_TopSiteForm; });
 
 
 
 
 
@@ -13310,17 +13257,17 @@ class TopSiteForm_TopSiteForm extends ex
 
 }
 TopSiteForm_TopSiteForm.defaultProps = {
   site: null,
   index: -1
 };
 
 /***/ }),
-/* 58 */
+/* 57 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 
 // EXTERNAL MODULE: ./common/Actions.jsm
 var Actions = __webpack_require__(2);
 
@@ -14158,17 +14105,17 @@ var reducers = {
   Dialog,
   Sections,
   Pocket,
   DiscoveryStream,
   Search
 };
 
 /***/ }),
-/* 59 */
+/* 58 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 
 // EXTERNAL MODULE: ./common/Actions.jsm
 var Actions = __webpack_require__(2);
 
@@ -14201,27 +14148,27 @@ const cardContextTypes = {
     intlID: "type_label_downloaded",
     icon: "download"
   }
 };
 // EXTERNAL MODULE: external "ReactRedux"
 var external_ReactRedux_ = __webpack_require__(26);
 
 // EXTERNAL MODULE: ./content-src/lib/link-menu-options.js
-var link_menu_options = __webpack_require__(33);
+var link_menu_options = __webpack_require__(32);
 
 // EXTERNAL MODULE: ./content-src/components/LinkMenu/LinkMenu.jsx
-var LinkMenu = __webpack_require__(31);
+var LinkMenu = __webpack_require__(30);
 
 // EXTERNAL MODULE: external "React"
 var external_React_ = __webpack_require__(11);
 var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
 
 // EXTERNAL MODULE: ./content-src/lib/screenshot-utils.js
-var screenshot_utils = __webpack_require__(45);
+var screenshot_utils = __webpack_require__(44);
 
 // CONCATENATED MODULE: ./content-src/components/Card/Card.jsx
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_Card", function() { return Card_Card; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Card", function() { return Card; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PlaceholderCard", function() { return PlaceholderCard; });
 
 
 
deleted file mode 100644
--- a/browser/components/newtab/test/unit/content-src/lib/clamp-total-lines.test.js
+++ /dev/null
@@ -1,154 +0,0 @@
-import {clampTotalLines} from "content-src/lib/clamp-total-lines";
-import {GlobalOverrider} from "test/unit/utils";
-
-const HEIGHT = 20;
-
-describe("clampTotalLines", () => {
-  let globals;
-  let sandbox;
-  let children;
-
-  const node = () => document.createElement("div");
-  function child(lines, clamp) {
-    const ret = node();
-    ret.classList.add("clamp");
-    sandbox.stub(ret, "scrollHeight").get(() => lines * HEIGHT);
-    if (clamp) {
-      ret.dataset.clamp = clamp;
-    }
-    return ret;
-  }
-  function test(totalLines) {
-    const parentNode = node();
-    parentNode.setAttribute("data-total-lines", totalLines);
-
-    // Convert children line sizes into clamp nodes with appropriate height
-    children = children.map(childLines => parentNode.appendChild(
-      typeof childLines === "number" ? child(childLines) : childLines));
-
-    clampTotalLines(parentNode);
-  }
-  function check(index, lines) {
-    assert.propertyVal(children[index].style, "webkitLineClamp", `${lines}`);
-  }
-
-  beforeEach(() => {
-    globals = new GlobalOverrider();
-    ({sandbox} = globals);
-    children = [];
-    globals.set("getComputedStyle", () => ({lineHeight: `${HEIGHT}px`}));
-  });
-  afterEach(() => {
-    globals.restore();
-  });
-
-  it("should do nothing with nothing", () => {
-    clampTotalLines();
-  });
-  it("should clamp single long child to total", () => {
-    children = [10];
-
-    test(6);
-
-    check(0, 6);
-  });
-  it("should clamp single short child to its height", () => {
-    children = [2];
-
-    test(6);
-
-    check(0, 2);
-  });
-  it("should clamp long children preferring first", () => {
-    children = [10, 10];
-
-    test(6);
-
-    check(0, 5);
-    check(1, 1);
-  });
-  it("should clamp short children to their heights", () => {
-    children = [2, 2];
-
-    test(6);
-
-    check(0, 2);
-    check(1, 2);
-  });
-  it("should give remainder to last child", () => {
-    children = [4, 4];
-
-    test(6);
-
-    check(0, 4);
-    check(1, 2);
-  });
-  it("should handle smaller totals", () => {
-    children = [3, 3];
-
-    test(4);
-
-    check(0, 3);
-    check(1, 1);
-  });
-  it("should allow explicit child clamp", () => {
-    children = [child(3, 2), 3];
-
-    test(4);
-
-    check(0, 2);
-    check(1, 2);
-  });
-  it("should skip non-clamp children", () => {
-    children = [node(), 3, node(), 3];
-
-    test(4);
-
-    check(1, 3);
-    check(3, 1);
-  });
-  it("should skip no-height children", () => {
-    children = [0, 3, 0, 3];
-
-    test(4);
-
-    check(1, 3);
-    check(3, 1);
-  });
-  it("should handle larger totals", () => {
-    children = [4, 4];
-
-    test(8);
-
-    check(0, 4);
-    check(1, 4);
-  });
-  it("should handle even larger totals", () => {
-    children = [4, 4];
-
-    test(10);
-
-    check(0, 4);
-    check(1, 4);
-  });
-  it("should clamp many children preferring earlier", () => {
-    children = [2, 2, 2, 2];
-
-    test(6);
-
-    check(0, 2);
-    check(1, 2);
-    check(2, 1);
-    check(3, 1);
-  });
-  it("should give lines to children that need them", () => {
-    children = [1, 2, 3, 4];
-
-    test(8);
-
-    check(0, 1);
-    check(1, 2);
-    check(2, 3);
-    check(3, 2);
-  });
-});