--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
#
# Modifying this file will now automatically clobber the buildbot machines \o/
#
# Are you updating CLOBBER because you think it's needed for your WebIDL
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
-Bug 1395486 - Moving files to content-accessible requires a clobber so as not to break Android packaging.
+Bug 1399970 / Bug 1399226 - Removed some now-unused icons. Clobbering to avoid `make package` failures, e.g., bug 1399686.
--- a/browser/extensions/activity-stream/data/content/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/data/content/activity-stream-prerendered.html
@@ -4,17 +4,17 @@
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy-Report-Only" content="script-src 'unsafe-inline'; img-src http: https: data: blob:; style-src 'unsafe-inline'; child-src 'none'; object-src 'none'; report-uri https://tiles.services.mozilla.com/v4/links/activity-stream/csp">
<title></title>
<link rel="icon" type="image/png" id="favicon" href="chrome://branding/content/icon32.png"/>
<link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
<link rel="stylesheet" href="resource://activity-stream/data/content/activity-stream.css" />
</head>
<body class="activity-stream">
- <div id="root"><div class="outer-wrapper fixed-to-top" data-reactroot="" data-reactid="1" data-react-checksum="1939271514"><main data-reactid="2"><div class="search-wrapper" data-reactid="3"><label for="newtab-search-text" class="search-label" data-reactid="4"><span class="sr-only" data-reactid="5"><span data-reactid="6">Search the Web</span></span></label><input type="search" id="newtab-search-text" maxlength="256" placeholder="Search the Web" title="Search the Web" data-reactid="7"/><button id="searchSubmit" class="search-button" title=" " data-reactid="8"><span class="sr-only" data-reactid="9"><span data-reactid="10"> </span></span></button></div><section class="top-sites" data-reactid="11"><h3 class="section-title" data-reactid="12"><span class="icon icon-small-spacer icon-topsites" data-reactid="13"></span><span data-reactid="14"> </span></h3><ul class="top-sites-list" data-reactid="15"><li class="top-site-outer placeholder" data-reactid="16"><a data-reactid="17"><div class="tile" aria-hidden="true" data-reactid="18"><span class="letter-fallback" data-reactid="19"></span><div class="screenshot" style="background-image:none;" data-reactid="20"></div></div><div class="title " data-reactid="21"><span dir="auto" data-reactid="22"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="23"><a data-reactid="24"><div class="tile" aria-hidden="true" data-reactid="25"><span class="letter-fallback" data-reactid="26"></span><div class="screenshot" style="background-image:none;" data-reactid="27"></div></div><div class="title " data-reactid="28"><span dir="auto" data-reactid="29"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="30"><a data-reactid="31"><div class="tile" aria-hidden="true" data-reactid="32"><span class="letter-fallback" data-reactid="33"></span><div class="screenshot" style="background-image:none;" data-reactid="34"></div></div><div class="title " data-reactid="35"><span dir="auto" data-reactid="36"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="37"><a data-reactid="38"><div class="tile" aria-hidden="true" data-reactid="39"><span class="letter-fallback" data-reactid="40"></span><div class="screenshot" style="background-image:none;" data-reactid="41"></div></div><div class="title " data-reactid="42"><span dir="auto" data-reactid="43"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="44"><a data-reactid="45"><div class="tile" aria-hidden="true" data-reactid="46"><span class="letter-fallback" data-reactid="47"></span><div class="screenshot" style="background-image:none;" data-reactid="48"></div></div><div class="title " data-reactid="49"><span dir="auto" data-reactid="50"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="51"><a data-reactid="52"><div class="tile" aria-hidden="true" data-reactid="53"><span class="letter-fallback" data-reactid="54"></span><div class="screenshot" style="background-image:none;" data-reactid="55"></div></div><div class="title " data-reactid="56"><span dir="auto" data-reactid="57"></span></div></a></li></ul><div class="edit-topsites-wrapper" data-reactid="58"><div class="edit-topsites-button" data-reactid="59"><button class="edit" title=" " data-reactid="60"><span data-reactid="61"> </span></button></div></div></section><div class="sections-list" data-reactid="62"><section data-reactid="63"><div class="section-top-bar" data-reactid="64"><h3 class="section-title" data-reactid="65"><span class="icon icon-small-spacer icon-pocket" data-reactid="66"></span><span data-reactid="67"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="68"><li class="card-outer placeholder" data-reactid="69"><a data-reactid="70"><div class="card" data-reactid="71"><div class="card-details no-image" data-reactid="72"><div class="card-text no-image no-host-name no-context" data-reactid="73"><h4 class="card-title" dir="auto" data-reactid="74"></h4><p class="card-description" dir="auto" data-reactid="75"></p></div><div class="card-context" data-reactid="76"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="77"><a data-reactid="78"><div class="card" data-reactid="79"><div class="card-details no-image" data-reactid="80"><div class="card-text no-image no-host-name no-context" data-reactid="81"><h4 class="card-title" dir="auto" data-reactid="82"></h4><p class="card-description" dir="auto" data-reactid="83"></p></div><div class="card-context" data-reactid="84"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="85"><a data-reactid="86"><div class="card" data-reactid="87"><div class="card-details no-image" data-reactid="88"><div class="card-text no-image no-host-name no-context" data-reactid="89"><h4 class="card-title" dir="auto" data-reactid="90"></h4><p class="card-description" dir="auto" data-reactid="91"></p></div><div class="card-context" data-reactid="92"></div></div></div></a></li></ul><div class="topic" data-reactid="93"><span data-reactid="94"><span data-reactid="95"> </span></span><ul data-reactid="96"><li data-reactid="97"><a class="topic-link" data-reactid="98"></a></li></ul></div></section><section data-reactid="99"><div class="section-top-bar" data-reactid="100"><h3 class="section-title" data-reactid="101"><span class="icon icon-small-spacer icon-highlights" data-reactid="102"></span><span data-reactid="103"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="104"><li class="card-outer placeholder" data-reactid="105"><a data-reactid="106"><div class="card" data-reactid="107"><div class="card-details no-image" data-reactid="108"><div class="card-text no-image no-host-name no-context" data-reactid="109"><h4 class="card-title" dir="auto" data-reactid="110"></h4><p class="card-description" dir="auto" data-reactid="111"></p></div><div class="card-context" data-reactid="112"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="113"><a data-reactid="114"><div class="card" data-reactid="115"><div class="card-details no-image" data-reactid="116"><div class="card-text no-image no-host-name no-context" data-reactid="117"><h4 class="card-title" dir="auto" data-reactid="118"></h4><p class="card-description" dir="auto" data-reactid="119"></p></div><div class="card-context" data-reactid="120"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="121"><a data-reactid="122"><div class="card" data-reactid="123"><div class="card-details no-image" data-reactid="124"><div class="card-text no-image no-host-name no-context" data-reactid="125"><h4 class="card-title" dir="auto" data-reactid="126"></h4><p class="card-description" dir="auto" data-reactid="127"></p></div><div class="card-context" data-reactid="128"></div></div></div></a></li></ul></section></div><!-- react-empty: 129 --></main></div></div>
+ <div id="root"><div class="outer-wrapper fixed-to-top" data-reactroot="" data-reactid="1" data-react-checksum="544412221"><main data-reactid="2"><div class="search-wrapper" data-reactid="3"><label for="newtab-search-text" class="search-label" data-reactid="4"><span class="sr-only" data-reactid="5"><span data-reactid="6">Search the Web</span></span></label><input type="search" id="newtab-search-text" maxlength="256" placeholder="Search the Web" title="Search the Web" data-reactid="7"/><button id="searchSubmit" class="search-button" title=" " data-reactid="8"><span class="sr-only" data-reactid="9"><span data-reactid="10"> </span></span></button></div><section class="top-sites" data-reactid="11"><h3 class="section-title" data-reactid="12"><span class="icon icon-small-spacer icon-topsites" data-reactid="13"></span><span data-reactid="14"> </span></h3><ul class="top-sites-list" data-reactid="15"><li class="top-site-outer placeholder" data-reactid="16"><a data-reactid="17"><div class="tile" aria-hidden="true" data-reactid="18"><span class="letter-fallback" data-reactid="19"></span><div class="screenshot" style="background-image:none;" data-reactid="20"></div></div><div class="title " data-reactid="21"><span dir="auto" data-reactid="22"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="23"><a data-reactid="24"><div class="tile" aria-hidden="true" data-reactid="25"><span class="letter-fallback" data-reactid="26"></span><div class="screenshot" style="background-image:none;" data-reactid="27"></div></div><div class="title " data-reactid="28"><span dir="auto" data-reactid="29"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="30"><a data-reactid="31"><div class="tile" aria-hidden="true" data-reactid="32"><span class="letter-fallback" data-reactid="33"></span><div class="screenshot" style="background-image:none;" data-reactid="34"></div></div><div class="title " data-reactid="35"><span dir="auto" data-reactid="36"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="37"><a data-reactid="38"><div class="tile" aria-hidden="true" data-reactid="39"><span class="letter-fallback" data-reactid="40"></span><div class="screenshot" style="background-image:none;" data-reactid="41"></div></div><div class="title " data-reactid="42"><span dir="auto" data-reactid="43"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="44"><a data-reactid="45"><div class="tile" aria-hidden="true" data-reactid="46"><span class="letter-fallback" data-reactid="47"></span><div class="screenshot" style="background-image:none;" data-reactid="48"></div></div><div class="title " data-reactid="49"><span dir="auto" data-reactid="50"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="51"><a data-reactid="52"><div class="tile" aria-hidden="true" data-reactid="53"><span class="letter-fallback" data-reactid="54"></span><div class="screenshot" style="background-image:none;" data-reactid="55"></div></div><div class="title " data-reactid="56"><span dir="auto" data-reactid="57"></span></div></a></li></ul><div class="edit-topsites-wrapper" data-reactid="58"><div class="edit-topsites-button" data-reactid="59"><button class="edit" title=" " data-reactid="60"><span data-reactid="61"> </span></button></div></div></section><div class="sections-list" data-reactid="62"><section data-reactid="63"><div class="section-top-bar" data-reactid="64"><h3 class="section-title" data-reactid="65"><span class="icon icon-small-spacer icon-pocket" data-reactid="66"></span><span data-reactid="67"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="68"><li class="card-outer placeholder" data-reactid="69"><a data-reactid="70"><div class="card" data-reactid="71"><div class="card-details no-image" data-reactid="72"><div class="card-text no-context no-description no-host-name no-image" data-reactid="73"><h4 class="card-title" dir="auto" data-reactid="74"></h4><p class="card-description" dir="auto" data-reactid="75"></p></div><div class="card-context" data-reactid="76"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="77"><a data-reactid="78"><div class="card" data-reactid="79"><div class="card-details no-image" data-reactid="80"><div class="card-text no-context no-description no-host-name no-image" data-reactid="81"><h4 class="card-title" dir="auto" data-reactid="82"></h4><p class="card-description" dir="auto" data-reactid="83"></p></div><div class="card-context" data-reactid="84"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="85"><a data-reactid="86"><div class="card" data-reactid="87"><div class="card-details no-image" data-reactid="88"><div class="card-text no-context no-description no-host-name no-image" data-reactid="89"><h4 class="card-title" dir="auto" data-reactid="90"></h4><p class="card-description" dir="auto" data-reactid="91"></p></div><div class="card-context" data-reactid="92"></div></div></div></a></li></ul><div class="topic" data-reactid="93"><span data-reactid="94"><span data-reactid="95"> </span></span><ul data-reactid="96"><li data-reactid="97"><a class="topic-link" data-reactid="98"></a></li></ul></div></section><section data-reactid="99"><div class="section-top-bar" data-reactid="100"><h3 class="section-title" data-reactid="101"><span class="icon icon-small-spacer icon-highlights" data-reactid="102"></span><span data-reactid="103"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="104"><li class="card-outer placeholder" data-reactid="105"><a data-reactid="106"><div class="card" data-reactid="107"><div class="card-details no-image" data-reactid="108"><div class="card-text no-context no-description no-host-name no-image" data-reactid="109"><h4 class="card-title" dir="auto" data-reactid="110"></h4><p class="card-description" dir="auto" data-reactid="111"></p></div><div class="card-context" data-reactid="112"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="113"><a data-reactid="114"><div class="card" data-reactid="115"><div class="card-details no-image" data-reactid="116"><div class="card-text no-context no-description no-host-name no-image" data-reactid="117"><h4 class="card-title" dir="auto" data-reactid="118"></h4><p class="card-description" dir="auto" data-reactid="119"></p></div><div class="card-context" data-reactid="120"></div></div></div></a></li><li class="card-outer placeholder" data-reactid="121"><a data-reactid="122"><div class="card" data-reactid="123"><div class="card-details no-image" data-reactid="124"><div class="card-text no-context no-description no-host-name no-image" data-reactid="125"><h4 class="card-title" dir="auto" data-reactid="126"></h4><p class="card-description" dir="auto" data-reactid="127"></p></div><div class="card-context" data-reactid="128"></div></div></div></a></li></ul></section></div><!-- react-empty: 129 --></main></div></div>
<div id="snippets-container">
<div id="snippets"></div>
</div>
<script src="resource://activity-stream/data/content/activity-stream-initial-state.js"></script>
<script src="chrome://browser/content/contentSearchUI.js"></script>
<script src="resource://activity-stream/vendor/react.js"></script>
<script src="resource://activity-stream/vendor/react-dom.js"></script>
<script src="resource://activity-stream/vendor/react-intl.js"></script>
--- a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
@@ -332,17 +332,17 @@ module.exports = g;
/***/ (function(module, exports) {
module.exports = {
TOP_SITES_SOURCE: "TOP_SITES",
TOP_SITES_CONTEXT_MENU_OPTIONS: ["CheckPinTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl", "DeleteUrl"],
// minimum size necessary to show a rich icon instead of a screenshot
MIN_RICH_FAVICON_SIZE: 96,
// minimum size necessary to show any icon in the top left corner with a screenshot
- MIN_CORNER_FAVICON_SIZE: 32
+ MIN_CORNER_FAVICON_SIZE: 16
};
/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -804,32 +804,38 @@ const { TOP_SITES_SOURCE, TOP_SITES_CONT
const TopSiteLink = props => {
const { link } = props;
const topSiteOuterClassName = `top-site-outer${props.className ? ` ${props.className}` : ""}`;
const { tippyTopIcon, faviconSize } = link;
let imageClassName;
let imageStyle;
let showSmallFavicon = false;
let smallFaviconStyle;
+ let smallFaviconFallback;
if (tippyTopIcon || faviconSize >= MIN_RICH_FAVICON_SIZE) {
// styles and class names for top sites with rich icons
imageClassName = "top-site-icon rich-icon";
imageStyle = {
backgroundColor: link.backgroundColor,
backgroundImage: `url(${tippyTopIcon || link.favicon})`
};
} else {
// styles and class names for top sites with screenshot + small icon in top left corner
imageClassName = `screenshot${link.screenshot ? " active" : ""}`;
imageStyle = { backgroundImage: link.screenshot ? `url(${link.screenshot})` : "none" };
- // only show a favicon in top left if it's greater than 32x32
+ // only show a favicon in top left if it's greater than 16x16
if (faviconSize >= MIN_CORNER_FAVICON_SIZE) {
showSmallFavicon = true;
smallFaviconStyle = { backgroundImage: `url(${link.favicon})` };
+ } else if (link.screenshot) {
+ // Don't show a small favicon if there is no screenshot, because that
+ // would result in two fallback icons
+ showSmallFavicon = true;
+ smallFaviconFallback = true;
}
}
return React.createElement(
"li",
{ className: topSiteOuterClassName, key: link.guid || link.url },
React.createElement(
"a",
{ href: link.url, onClick: props.onClick },
@@ -837,17 +843,21 @@ const TopSiteLink = props => {
"div",
{ className: "tile", "aria-hidden": true },
React.createElement(
"span",
{ className: "letter-fallback" },
props.title[0]
),
React.createElement("div", { className: imageClassName, style: imageStyle }),
- showSmallFavicon && React.createElement("div", { className: "top-site-icon default-icon", style: smallFaviconStyle })
+ showSmallFavicon && React.createElement(
+ "div",
+ { className: "top-site-icon default-icon", style: smallFaviconStyle },
+ smallFaviconFallback && props.title[0]
+ )
),
React.createElement(
"div",
{ className: `title ${link.isPinned ? "pinned" : ""}` },
link.isPinned && React.createElement("div", { className: "icon icon-pin-small" }),
React.createElement(
"span",
{ dir: "auto" },
@@ -858,17 +868,17 @@ const TopSiteLink = props => {
props.children
);
};
TopSiteLink.defaultProps = {
title: "",
link: {}
};
-class TopSite extends React.Component {
+class TopSite extends React.PureComponent {
constructor(props) {
super(props);
this.state = { showContextMenu: false, activeTile: null };
this.onLinkClick = this.onLinkClick.bind(this);
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
this.onDismissButtonClick = this.onDismissButtonClick.bind(this);
this.onPinButtonClick = this.onPinButtonClick.bind(this);
@@ -1001,17 +1011,17 @@ module.exports.TopSitePlaceholder = TopS
const React = __webpack_require__(1);
const { injectIntl } = __webpack_require__(2);
const ContextMenu = __webpack_require__(17);
const { actionCreators: ac } = __webpack_require__(0);
const linkMenuOptions = __webpack_require__(18);
const DEFAULT_SITE_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
-class LinkMenu extends React.Component {
+class LinkMenu extends React.PureComponent {
getOptions() {
const props = this.props;
const { site, index, source } = props;
// Handle special case of default site
const propOptions = !site.isDefault ? props.options : DEFAULT_SITE_MENU_OPTIONS;
const options = propOptions.map(o => linkMenuOptions[o](site, index, source)).map(option => {
@@ -1113,17 +1123,17 @@ const { PrerenderData } = __webpack_requ
// this just uses english locale data. We can make this more sophisticated if
// more features are needed.
function addLocaleDataForReactIntl({ locale, textDirection }) {
addLocaleData([{ locale, parentLocale: "en" }]);
document.documentElement.lang = locale;
document.documentElement.dir = textDirection;
}
-class Base extends React.Component {
+class Base extends React.PureComponent {
componentWillMount() {
this.sendNewTabRehydrated(this.props.App);
}
componentDidMount() {
// Request state AFTER the first render to ensure we don't cause the
// prerendered DOM to be unmounted. Otherwise, NEW_TAB_STATE_REQUEST is
// dispatched right after the store is ready.
@@ -1270,19 +1280,19 @@ const { perfService: perfSvc } = __webpa
* we want to get to the closest reliable paint for measuring, and
* setTimeout is often throttled or queued by browsers in ways that could
* make it lag too long.
*
* XXX Should be made more generic by using this.props.children, or potentially
* even split out into a higher-order component to wrap whatever.
*
* @class TopSitesPerfTimer
- * @extends {React.Component}
+ * @extends {React.PureComponent}
*/
-class TopSitesPerfTimer extends React.Component {
+class TopSitesPerfTimer extends React.PureComponent {
constructor(props) {
super(props);
// Just for test dependency injection:
this.perfSvc = this.props.perfSvc || perfSvc;
this._sendPaintedEvent = this._sendPaintedEvent.bind(this);
this._timestampHandled = false;
}
@@ -1378,17 +1388,17 @@ const { FormattedMessage, injectIntl } =
const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
const TopSiteForm = __webpack_require__(16);
const { TopSite, TopSitePlaceholder } = __webpack_require__(8);
const { TOP_SITES_DEFAULT_LENGTH, TOP_SITES_SHOWMORE_LENGTH } = __webpack_require__(6);
const { TOP_SITES_SOURCE } = __webpack_require__(5);
-class TopSitesEdit extends React.Component {
+class TopSitesEdit extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
showEditModal: false,
showAddForm: false,
showEditForm: false,
editIndex: -1 // Index of top site being edited
};
@@ -1551,17 +1561,17 @@ module.exports._unconnected = TopSitesEd
/***/ (function(module, exports, __webpack_require__) {
const React = __webpack_require__(1);
const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
const { FormattedMessage } = __webpack_require__(2);
const { TOP_SITES_SOURCE } = __webpack_require__(5);
-class TopSiteForm extends React.Component {
+class TopSiteForm extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
label: props.label || "",
url: props.url || "",
validationError: false
};
this.onLabelChange = this.onLabelChange.bind(this);
@@ -1734,17 +1744,17 @@ TopSiteForm.defaultProps = {
module.exports = TopSiteForm;
/***/ }),
/* 17 */
/***/ (function(module, exports, __webpack_require__) {
const React = __webpack_require__(1);
-class ContextMenu extends React.Component {
+class ContextMenu extends React.PureComponent {
constructor(props) {
super(props);
this.hideContext = this.hideContext.bind(this);
}
hideContext() {
this.props.onUpdate(false);
}
componentWillMount() {
@@ -1771,17 +1781,17 @@ class ContextMenu extends React.Componen
"ul",
{ role: "menu", className: "context-menu-list" },
this.props.options.map((option, i) => option.type === "separator" ? React.createElement("li", { key: i, className: "separator" }) : React.createElement(ContextMenuItem, { key: i, option: option, hideContext: this.hideContext }))
)
);
}
}
-class ContextMenuItem extends React.Component {
+class ContextMenuItem extends React.PureComponent {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
}
onClick() {
this.props.hideContext();
this.props.option.onClick();
@@ -1887,17 +1897,17 @@ module.exports = {
userEvent: "BLOCK"
}),
DeleteUrl: site => ({
id: "menu_action_delete",
icon: "delete",
action: {
type: at.DIALOG_OPEN,
data: {
- onConfirm: [ac.SendToMain({ type: at.DELETE_HISTORY_URL, data: site.url }), ac.UserEvent({ event: "DELETE" })],
+ onConfirm: [ac.SendToMain({ type: at.DELETE_HISTORY_URL, data: { url: site.url, forceBlock: site.bookmarkGuid } }), ac.UserEvent({ event: "DELETE" })],
body_string_id: ["confirm_history_delete_p1", "confirm_history_delete_notice_p2"],
confirm_button_string_id: "menu_action_delete"
}
},
userEvent: "DIALOG_OPEN"
}),
PinTopSite: (site, index) => ({
id: "menu_action_pin",
@@ -1946,17 +1956,17 @@ module.exports.CheckPinTopSite = (site,
const React = __webpack_require__(1);
const { connect } = __webpack_require__(3);
const { FormattedMessage, injectIntl } = __webpack_require__(2);
const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
const { IS_NEWTAB } = __webpack_require__(20);
-class Search extends React.Component {
+class Search extends React.PureComponent {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.onInputMount = this.onInputMount.bind(this);
}
handleEvent(event) {
// Also track search events with our own telemetry
@@ -2178,17 +2188,17 @@ const { actionTypes: at, actionCreators:
/**
* Manual migration component used to start the profile import wizard.
* Message is presented temporarily and will go away if:
* 1. User clicks "No Thanks"
* 2. User completed the data import
* 3. After 3 active days
* 4. User clicks "Cancel" on the import wizard (currently not implemented).
*/
-class ManualMigration extends React.Component {
+class ManualMigration extends React.PureComponent {
constructor(props) {
super(props);
this.onLaunchTour = this.onLaunchTour.bind(this);
this.onCancelTour = this.onCancelTour.bind(this);
}
onLaunchTour() {
this.props.dispatch(ac.SendToMain({ type: at.MIGRATION_START }));
this.props.dispatch(ac.UserEvent({ event: at.MIGRATION_START }));
@@ -2257,17 +2267,17 @@ const PreferencesInput = props => React.
),
props.descString && React.createElement(
"p",
{ className: "prefs-input-description" },
getFormattedMessage(props.descString)
)
);
-class PreferencesPane extends React.Component {
+class PreferencesPane extends React.PureComponent {
constructor(props) {
super(props);
this.state = { visible: false };
this.handleClickOutside = this.handleClickOutside.bind(this);
this.handlePrefChange = this.handlePrefChange.bind(this);
this.handleSectionChange = this.handleSectionChange.bind(this);
this.togglePane = this.togglePane.bind(this);
this.onWrapperMount = this.onWrapperMount.bind(this);
@@ -2394,17 +2404,17 @@ const Card = __webpack_require__(25);
const { PlaceholderCard } = Card;
const Topics = __webpack_require__(27);
const { actionCreators: ac } = __webpack_require__(0);
const VISIBLE = "visible";
const VISIBILITY_CHANGE_EVENT = "visibilitychange";
const CARDS_PER_ROW = 3;
-class Section extends React.Component {
+class Section extends React.PureComponent {
constructor(props) {
super(props);
this.onInfoEnter = this.onInfoEnter.bind(this);
this.onInfoLeave = this.onInfoLeave.bind(this);
this.state = { infoActive: false };
}
/**
@@ -2604,17 +2614,17 @@ Section.defaultProps = {
document: global.document,
rows: [],
emptyState: {},
title: ""
};
const SectionIntl = injectIntl(Section);
-class Sections extends React.Component {
+class Sections extends React.PureComponent {
render() {
const sections = this.props.Sections;
return React.createElement(
"div",
{ className: "sections-list" },
sections.filter(section => section.enabled).map(section => React.createElement(SectionIntl, _extends({ key: section.id }, section, { dispatch: this.props.dispatch })))
);
}
@@ -2640,17 +2650,17 @@ const { actionCreators: ac, actionTypes:
* Card component.
* Cards are found within a Section component and contain information about a link such
* as preview image, page title, page description, and some context about if the page
* was visited, bookmarked, trending etc...
* Each Section can make an unordered list of Cards which will create one instane of
* this class. Each card will then get a context menu which reflects the actions that
* can be done on this Card.
*/
-class Card extends React.Component {
+class Card extends React.PureComponent {
constructor(props) {
super(props);
this.state = { showContextMenu: false, activeCard: null };
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
this.onLinkClick = this.onLinkClick.bind(this);
}
onMenuButtonClick(event) {
@@ -2710,17 +2720,17 @@ class Card extends React.Component {
{ className: `card-details${hasImage ? "" : " no-image"}` },
link.hostname && React.createElement(
"div",
{ className: "card-host-name" },
link.hostname
),
React.createElement(
"div",
- { className: `card-text${hasImage ? "" : " no-image"}${link.hostname ? "" : " no-host-name"}${icon ? "" : " no-context"}` },
+ { className: ["card-text", icon ? "" : "no-context", link.description ? "" : "no-description", link.hostname ? "" : "no-host-name", hasImage ? "" : "no-image"].join(" ") },
React.createElement(
"h4",
{ className: "card-title", dir: "auto" },
link.title
),
React.createElement(
"p",
{ className: "card-description", dir: "auto" },
@@ -2799,32 +2809,32 @@ module.exports = {
/***/ }),
/* 27 */
/***/ (function(module, exports, __webpack_require__) {
const React = __webpack_require__(1);
const { FormattedMessage } = __webpack_require__(2);
-class Topic extends React.Component {
+class Topic extends React.PureComponent {
render() {
const { url, name } = this.props;
return React.createElement(
"li",
null,
React.createElement(
"a",
{ key: name, className: "topic-link", href: url },
name
)
);
}
}
-class Topics extends React.Component {
+class Topics extends React.PureComponent {
render() {
const { topics, read_more_endpoint } = this.props;
return React.createElement(
"div",
{ className: "topic" },
React.createElement(
"span",
null,
--- a/browser/extensions/activity-stream/data/content/activity-stream.css
+++ b/browser/extensions/activity-stream/data/content/activity-stream.css
@@ -47,17 +47,17 @@ input {
background-image: url("assets/glyph-info-16.svg"); }
.icon.icon-import {
background-image: url("assets/glyph-import-16.svg"); }
.icon.icon-new-window {
background-image: url("assets/glyph-newWindow-16.svg"); }
.icon.icon-new-window-private {
background-image: url("chrome://browser/skin/privateBrowsing.svg"); }
.icon.icon-settings {
- background-image: url("assets/glyph-settings-16.svg"); }
+ background-image: url("chrome://browser/skin/settings.svg"); }
.icon.icon-pin {
background-image: url("assets/glyph-pin-16.svg"); }
.icon.icon-unpin {
background-image: url("assets/glyph-unpin-16.svg"); }
.icon.icon-edit {
background-image: url("assets/glyph-edit-16.svg"); }
.icon.icon-pocket {
background-image: url("assets/glyph-pocket-16.svg"); }
@@ -172,17 +172,17 @@ a {
height: 100%;
flex-grow: 1; }
.outer-wrapper.fixed-to-top {
height: auto; }
main {
margin: auto;
width: 224px;
- padding-bottom: 120px; }
+ padding-bottom: 48px; }
@media (min-width: 416px) {
main {
width: 352px; } }
@media (min-width: 544px) {
main {
width: 480px; } }
@media (min-width: 800px) {
main {
@@ -198,19 +198,19 @@ main {
.section-title span {
color: #737373;
fill: #737373;
vertical-align: middle; }
.top-sites-list {
list-style: none;
margin: 0;
+ margin-bottom: -18px;
padding: 0;
- margin-inline-end: -32px;
- margin-bottom: -18px; }
+ margin-inline-end: -32px; }
@media (max-width: 416px) {
.top-sites-list :nth-child(2n+1) .context-menu {
margin-inline-start: auto;
margin-inline-end: auto;
offset-inline-start: -32px;
offset-inline-end: auto; }
.top-sites-list :nth-child(2n) .context-menu {
margin-inline-start: auto;
@@ -244,17 +244,17 @@ main {
@media (min-width: 800px) and (max-width: 1024px) {
.top-sites-list :nth-child(6n+5) .context-menu {
margin-inline-start: auto;
margin-inline-end: 5px;
offset-inline-start: auto;
offset-inline-end: 0; } }
.top-sites-list li {
display: inline-block;
- margin: 0 0 18px;
+ margin: 0 0 8px;
margin-inline-end: 32px; }
.top-sites-list .top-site-outer {
position: relative; }
.top-sites-list .top-site-outer > a {
display: block;
color: inherit;
outline: none; }
.top-sites-list .top-site-outer > a.active .tile, .top-sites-list .top-site-outer > a:focus .tile {
@@ -335,17 +335,21 @@ main {
width: 100%;
background-size: 96px; }
.top-sites-list .top-site-outer .default-icon {
z-index: 1;
top: -6px;
offset-inline-start: -6px;
height: 42px;
width: 42px;
- background-size: 32px; }
+ background-size: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 20px; }
.top-sites-list .top-site-outer .title {
font: message-box;
height: 30px;
line-height: 30px;
text-align: center;
width: 96px;
position: relative; }
.top-sites-list .top-site-outer .title .icon {
@@ -383,16 +387,20 @@ main {
border: 0;
border-right: 1px solid #B1B1B3;
background-color: #FFF;
cursor: pointer;
height: 100%;
width: 25px; }
.top-sites-list .top-site-outer .edit-menu button:hover {
background-color: #EDEDF0; }
+ .top-sites-list .top-site-outer .edit-menu button:first-child:dir(ltr), .top-sites-list .top-site-outer .edit-menu button:last-child:dir(rtl) {
+ width: 30px; }
+ .top-sites-list .top-site-outer .edit-menu button:last-child:dir(ltr), .top-sites-list .top-site-outer .edit-menu button:first-child:dir(rtl) {
+ width: 28px; }
.top-sites-list .top-site-outer .edit-menu button:last-child:dir(ltr) {
border-right: 0; }
.top-sites-list .top-site-outer .edit-menu button:first-child:dir(rtl) {
border-right: 0; }
.top-sites-list .top-site-outer:hover .edit-menu, .top-sites-list .top-site-outer:focus .edit-menu, .top-sites-list .top-site-outer.active .edit-menu {
transform: scale(1);
opacity: 1; }
@@ -410,17 +418,18 @@ main {
.edit-topsites-wrapper .edit-topsites-button button:focus {
background: #EDEDF0;
border-bottom: dotted 1px #737373; }
.edit-topsites-wrapper .modal {
offset-inline-start: -31px;
position: absolute;
top: -29px;
- width: calc(100% + 62px); }
+ width: calc(100% + 62px);
+ box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); }
.edit-topsites-wrapper .edit-topsites-inner-wrapper {
margin: 0;
padding: 15px 30px; }
.edit-topsites-wrapper .show-more,
.edit-topsites-wrapper .show-less {
background-position: left 10px center;
@@ -507,17 +516,18 @@ main {
background-image: url("assets/glyph-info-option-12.svg");
background-size: 12px 12px;
background-repeat: no-repeat;
background-position: center;
fill: rgba(12, 12, 13, 0.6);
-moz-context-properties: fill;
height: 16px;
width: 16px;
- display: inline-block; }
+ display: inline-block;
+ margin-bottom: -2px; }
.sections-list .section-top-bar .info-option-icon[aria-expanded="true"] {
fill: rgba(12, 12, 13, 0.8); }
.sections-list .section-top-bar .section-info-option .info-option {
visibility: hidden;
opacity: 0;
transition: visibility 0.2s, opacity 0.2s ease-out;
transition-delay: 0.5s; }
.sections-list .section-top-bar .info-option-icon[aria-expanded="true"] + .info-option {
@@ -527,21 +537,22 @@ main {
.sections-list .section-top-bar .info-option {
z-index: 9999;
position: absolute;
background: #FFF;
border: 1px solid #D7D7DB;
border-radius: 3px;
font-size: 13px;
line-height: 120%;
- margin-inline-end: -1px;
+ margin-inline-end: -13px;
offset-inline-end: 0;
top: 20px;
width: 320px;
padding: 24px;
+ box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
-moz-user-select: none; }
.sections-list .section-top-bar .info-option-header {
font-size: 15px;
font-weight: 600; }
.sections-list .section-top-bar .info-option-body {
margin: 0;
margin-top: 12px; }
.sections-list .section-top-bar .info-option-link {
@@ -650,47 +661,51 @@ main {
display: flex;
position: relative;
margin: 0 0 40px;
width: 100%;
height: 36px; }
.search-wrapper input {
border: 0;
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
+ border-radius: 3px;
border-radius: 4px;
color: inherit;
padding: 0;
padding-inline-end: 36px;
padding-inline-start: 35px;
- width: 100%; }
+ width: 100%;
+ font-size: 15px; }
.search-wrapper input:focus {
border-color: #0060DF;
box-shadow: 0 0 0 2px #0060DF;
z-index: 1; }
.search-wrapper input:focus + .search-button {
z-index: 1;
background-color: #0060DF;
background-image: url("chrome://browser/skin/forward.svg");
fill: #FFF;
-moz-context-properties: fill; }
.search-wrapper .search-label {
- background: url("assets/glyph-search-16.svg") no-repeat center center/20px;
- fill: rgba(12, 12, 13, 0.6);
+ background: url("chrome://browser/skin/find.svg") no-repeat 12px center/16px;
+ fill: rgba(12, 12, 13, 0.4);
-moz-context-properties: fill;
position: absolute;
offset-inline-start: 0;
height: 100%;
width: 35px;
z-index: 2; }
.search-wrapper .search-button {
background: url("chrome://browser/skin/forward.svg") no-repeat center center;
border-radius: 0 3px 3px 0;
border: 0;
width: 36px;
- fill: rgba(12, 12, 13, 0.6);
+ fill: rgba(12, 12, 13, 0.4);
-moz-context-properties: fill;
background-size: 16px 16px;
height: 100%;
offset-inline-end: 0;
position: absolute; }
.search-wrapper .search-button:hover {
z-index: 1;
background-color: #0060DF;
@@ -732,22 +747,24 @@ main {
line-height: 16px;
display: flex;
align-items: center; }
.context-menu > ul > li > a:hover, .context-menu > ul > li > a:focus {
background: #0060DF;
color: #FFF; }
.context-menu > ul > li > a:hover a, .context-menu > ul > li > a:focus a {
color: #0C0C0D; }
+ .context-menu > ul > li > a:hover .icon, .context-menu > ul > li > a:focus .icon {
+ fill: #FFF; }
.context-menu > ul > li > a:hover:hover, .context-menu > ul > li > a:hover:focus, .context-menu > ul > li > a:focus:hover, .context-menu > ul > li > a:focus:focus {
color: #FFF; }
.prefs-pane {
color: #4A4A4F;
- font-size: 15px;
+ font-size: 14px;
line-height: 21px; }
.prefs-pane .sidebar {
background: #FFF;
border-left: 1px solid #D7D7DB;
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
height: 100%;
offset-inline-end: 0;
overflow-y: auto;
@@ -758,54 +775,54 @@ main {
transition-property: transform;
width: 400px;
z-index: 12000; }
.prefs-pane .sidebar.hidden {
transform: translateX(100%); }
.prefs-pane .sidebar.hidden:dir(rtl) {
transform: translateX(-100%); }
.prefs-pane .sidebar h1 {
- font-size: 24px;
+ font-size: 21px;
margin: 0;
padding-top: 20px; }
.prefs-pane hr {
border: 0;
border-bottom: 1px solid #D7D7DB;
- margin: 28px 0; }
+ margin: 20px 0; }
.prefs-pane .prefs-modal-inner-wrapper {
padding-bottom: 100px; }
.prefs-pane .prefs-modal-inner-wrapper section {
margin: 20px 0; }
.prefs-pane .prefs-modal-inner-wrapper section p {
margin: 5px 0 5px 30px; }
.prefs-pane .prefs-modal-inner-wrapper section label {
display: inline-block;
position: relative;
width: 100%; }
.prefs-pane .prefs-modal-inner-wrapper section label input {
offset-inline-start: -30px;
position: absolute;
top: 0; }
.prefs-pane .prefs-modal-inner-wrapper section > label {
- font-size: 17px;
+ font-size: 16px;
font-weight: bold;
line-height: 19px; }
.prefs-pane .prefs-modal-inner-wrapper .options {
background: #F9F9FA;
border: 1px solid #D7D7DB;
border-radius: 2px;
margin: -10px 0 20px;
margin-inline-start: 30px;
padding: 10px; }
.prefs-pane .prefs-modal-inner-wrapper .options label {
background-position-x: 35px;
background-position-y: 2.5px;
background-repeat: no-repeat;
display: inline-block;
- font-size: 13px;
+ font-size: 14px;
font-weight: normal;
height: auto;
line-height: 21px;
width: 100%; }
.prefs-pane .prefs-modal-inner-wrapper .options label:dir(rtl) {
background-position-x: 217px; }
.prefs-pane .prefs-modal-inner-wrapper .options [type='checkbox']:not(:checked) + label,
.prefs-pane .prefs-modal-inner-wrapper .options [type='checkbox']:checked + label {
@@ -1014,16 +1031,19 @@ main {
.card-outer .card-text.no-host-name, .card-outer .card-text.no-context {
max-height: 97px; }
.card-outer .card-text.no-image.no-host-name, .card-outer .card-text.no-image.no-context {
max-height: 211px; }
.card-outer .card-text.no-host-name.no-context {
max-height: 116px; }
.card-outer .card-text.no-image.no-host-name.no-context {
max-height: 230px; }
+ .card-outer .card-text:not(.no-description) .card-title {
+ max-height: 57px;
+ overflow: hidden; }
.card-outer .card-host-name {
color: #737373;
font-size: 10px;
padding-bottom: 4px;
text-transform: uppercase; }
.card-outer .card-title {
margin: 0 0 2px;
font-size: 14px;
@@ -1031,17 +1051,17 @@ main {
line-height: 19px; }
.card-outer .card-description {
font-size: 12px;
margin: 0;
word-wrap: break-word;
overflow: hidden;
line-height: 19px; }
.card-outer .card-context {
- padding: 16px 16px 8px 14px;
+ padding: 12px 16px 12px 14px;
position: absolute;
bottom: 0;
left: 0;
right: 0;
color: #737373;
font-size: 11px;
display: flex; }
.card-outer .card-context-icon {
deleted file mode 100644
--- a/browser/extensions/activity-stream/data/content/assets/glyph-search-16.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M30.989 28.571l-2.2 2.2-9.533-9.534a11.436 11.436 0 1 1 2.2-2.2zM12.37 3.745a8.407 8.407 0 1 0 8.406 8.406 8.406 8.406 0 0 0-8.406-8.406z" fill="context-fill"/></svg>
\ No newline at end of file
deleted file mode 100644
--- a/browser/extensions/activity-stream/data/content/assets/glyph-settings-16.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><g fill="none" stroke="context-fill" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M8 1v3m0 8v3m-3.5-3.5l-1.45 1.45m9.9-9.9L11 5M1 8h3m8 0h3"/><circle cx="8" cy="8" r="4"/><path d="M3.05 3.05L5 5m6 6l1.95 1.95"/></g></svg>
\ No newline at end of file
--- a/browser/extensions/activity-stream/data/locales.json
+++ b/browser/extensions/activity-stream/data/locales.json
@@ -587,16 +587,17 @@
"settings_pane_button_label": "Personelait ho pajenn Ivinell Nevez",
"settings_pane_header": "Gwellvezioù an ivinell nevez"
},
"ca": {
"newtab_page_title": "Pestanya nova",
"default_label_loading": "S'està carregant…",
"header_top_sites": "Llocs principals",
"header_stories": "Articles populars",
+ "header_highlights": "Destacats",
"header_visit_again": "Torneu a visitar",
"header_bookmarks": "Adreces d'interès recents",
"header_recommended_by": "Recomanat per {provider}",
"header_bookmarks_placeholder": "Encara no teniu cap adreça d'interès.",
"header_stories_from": "de",
"type_label_visited": "Visitats",
"type_label_bookmarked": "A les adreces d'interès",
"type_label_synced": "Sincronitzat des d'un altre dispositiu",
@@ -618,37 +619,41 @@
"confirm_history_delete_notice_p2": "Aquesta acció no es pot desfer.",
"menu_action_save_to_pocket": "Desa al Pocket",
"search_for_something_with": "Cerca {search_term} amb:",
"search_button": "Cerca",
"search_header": "Cerca de {search_engine_name}",
"search_web_placeholder": "Cerca al web",
"search_settings": "Canvia els paràmetres de cerca",
"section_info_option": "Informació",
+ "section_info_send_feedback": "Doneu la vostra opinió",
+ "section_info_privacy_notice": "Avís de privadesa",
"welcome_title": "Us donem la benvinguda a la pestanya nova",
"welcome_body": "El Firefox utilitzarà aquest espai per mostrar-vos les adreces d'interès, els articles i els vídeos més rellevants, així com les pàgines que heu visitat recentment, per tal que hi pugueu accedir fàcilment.",
"welcome_label": "S'estan identificant els vostres llocs destacats",
"time_label_less_than_minute": "<1 m",
"time_label_minute": "{number} m",
"time_label_hour": "{number} h",
"time_label_day": "{number} d",
"settings_pane_button_label": "Personalitzeu la pàgina de pestanya nova",
"settings_pane_header": "Preferències de pestanya nova",
- "settings_pane_body": "Trieu què voleu veure quan obriu una pestanya nova.",
+ "settings_pane_body2": "Trieu què voleu veure en aquesta pàgina.",
"settings_pane_search_header": "Cerca",
"settings_pane_search_body": "Cerca al web des de la pestanya nova.",
"settings_pane_topsites_header": "Llocs principals",
"settings_pane_topsites_body": "Accediu als llocs web que visiteu més sovint.",
"settings_pane_topsites_options_showmore": "Mostra dues files",
"settings_pane_bookmarks_header": "Adreces d'interès recents",
"settings_pane_bookmarks_body": "Les adreces d'interès que aneu creant, en un lloc còmode.",
"settings_pane_visit_again_header": "Torneu a visitar",
"settings_pane_visit_again_body": "El Firefox us mostrarà parts del vostre historial de navegació que potser us agradaria recordar o tornar a visitar.",
- "settings_pane_pocketstories_header": "Articles populars",
- "settings_pane_pocketstories_body": "El Pocket, membre de la família Mozilla, us permet accedir a contingut d'alta qualitat que d'altra manera potser no trobaríeu.",
+ "settings_pane_highlights_header": "Destacats",
+ "settings_pane_highlights_options_bookmarks": "Adreces d'interès",
+ "settings_pane_highlights_options_visited": "Llocs visitats",
+ "settings_pane_snippets_header": "Retalls",
"settings_pane_done_button": "Fet",
"edit_topsites_button_text": "Edita",
"edit_topsites_button_label": "Personalitzeu la secció Llocs principals",
"edit_topsites_showmore_button": "Mostra'n més",
"edit_topsites_showless_button": "Mostra'n menys",
"edit_topsites_done_button": "Fet",
"edit_topsites_pin_button": "Fixa aquest lloc",
"edit_topsites_unpin_button": "No fixis aquest lloc",
@@ -661,20 +666,18 @@
"topsites_form_url_placeholder": "Escriviu o enganxeu un URL",
"topsites_form_add_button": "Afegeix",
"topsites_form_save_button": "Desa",
"topsites_form_cancel_button": "Cancel·la",
"topsites_form_url_validation": "Es necessita un URL vàlid",
"pocket_read_more": "Temes populars:",
"pocket_read_even_more": "Mostra més articles",
"pocket_feedback_header": "El millor del web, seleccionat per més de 25 milions de persones.",
- "pocket_feedback_body": "El Pocket, membre de la família Mozilla, us permet accedir a contingut d'alta qualitat que d'altra manera potser no trobaríeu.",
- "pocket_send_feedback": "Doneu la vostra opinió",
"topstories_empty_state": "Ja esteu al dia. Torneu més tard per veure més articles populars de {provider}. No podeu esperar? Trieu un tema popular per descobrir els articles més interessants de tot el web.",
- "manual_migration_explanation": "Proveu el Firefox amb els vostres llocs preferits i les adreces d'interès d'un altre navegador.",
+ "manual_migration_explanation2": "Proveu el Firefox amb les adreces d'interès, l'historial i les contrasenyes d'un altre navegador.",
"manual_migration_cancel_button": "No, gràcies",
"manual_migration_import_button": "Importa-ho ara"
},
"cak": {
"newtab_page_title": "K'ak'a' ruwi'",
"default_label_loading": "Tajin nusamajij…",
"header_top_sites": "Utziläj taq Ruxaq K'amaya'l",
"header_stories": "Utziläj taq B'anob'äl",
@@ -3134,16 +3137,26 @@
"welcome_title": "Բարի գալուստ նոր ներդիր",
"welcome_body": "Firefox-ը կօգտագործի այս բացատը՝ ցուցադրելու ձեզ համար առավել կարևոր էջանիշերը, հոդվածները և ձեր այցելած վերջին էջերը, որպեսզի հեշտությամբ վերադառնաք դրանց:",
"welcome_label": "Նույնացնում է ձեր գունանշումը",
"time_label_less_than_minute": "<1 ր",
"time_label_minute": "{number} ր",
"time_label_hour": "{number} ժ",
"time_label_day": "{number} օր"
},
+ "ia": {
+ "newtab_page_title": "Nove scheda",
+ "default_label_loading": "Cargamento…",
+ "header_top_sites": "Sitos principal",
+ "header_stories": "Articulos popular",
+ "header_highlights": "In evidentia",
+ "header_visit_again": "Visita de novo",
+ "header_bookmarks": "Marcatores recente",
+ "header_recommended_by": "Recommendate per {provider}"
+ },
"id": {
"newtab_page_title": "Tab Baru",
"default_label_loading": "Memuat…",
"header_top_sites": "Situs Teratas",
"header_stories": "Cerita Utama",
"header_highlights": "Sorotan",
"header_visit_again": "Kunjungi Lagi",
"header_bookmarks": "Markah Terbaru",
--- a/browser/extensions/activity-stream/install.rdf.in
+++ b/browser/extensions/activity-stream/install.rdf.in
@@ -3,17 +3,17 @@
#filter substitution
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>activity-stream@mozilla.org</em:id>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:unpack>false</em:unpack>
- <em:version>2017.09.14.0590-7fa80d82</em:version>
+ <em:version>2017.09.14.1322-706b3303</em:version>
<em:name>Activity Stream</em:name>
<em:description>A rich visual history feed and a reimagined home page make it easier than ever to find exactly what you're looking for in Firefox.</em:description>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
--- a/browser/extensions/activity-stream/lib/PlacesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/PlacesFeed.jsm
@@ -239,19 +239,24 @@ class PlacesFeed {
NewTabUtils.activityStreamLinks.blockURL({url: action.data});
break;
case at.BOOKMARK_URL:
NewTabUtils.activityStreamLinks.addBookmark(action.data, action._target.browser);
break;
case at.DELETE_BOOKMARK_BY_ID:
NewTabUtils.activityStreamLinks.deleteBookmark(action.data);
break;
- case at.DELETE_HISTORY_URL:
- NewTabUtils.activityStreamLinks.deleteHistoryEntry(action.data);
+ case at.DELETE_HISTORY_URL: {
+ const {url, forceBlock} = action.data;
+ NewTabUtils.activityStreamLinks.deleteHistoryEntry(url);
+ if (forceBlock) {
+ NewTabUtils.activityStreamLinks.blockURL({url});
+ }
break;
+ }
case at.OPEN_NEW_WINDOW:
this.openNewWindow(action);
break;
case at.OPEN_PRIVATE_WINDOW:
this.openNewWindow(action, true);
break;
case at.SAVE_TO_POCKET:
Pocket.savePage(action._target.browser, action.data.site.url, action.data.site.title);
--- a/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TopSitesFeed.jsm
@@ -101,51 +101,57 @@ this.TopSitesFeed = class TopSitesFeed {
if (link && link.screenshot) {
currentScreenshots[link.url] = link.screenshot;
}
}
// Now, get a tippy top icon, a rich icon, or screenshot for every item
for (let link of links) {
if (!link) { continue; }
-
- // Check for tippy top icon or a rich icon.
- link = this._tippyTopProvider.processSite(link);
- if (link.tippyTopIcon || link.faviconSize >= MIN_FAVICON_SIZE) { continue; }
-
- // If no tippy top, then we get a screenshot.
- if (currentScreenshots[link.url]) {
- link.screenshot = currentScreenshots[link.url];
- } else {
- this.getScreenshot(link.url);
- }
+ this._fetchIcon(link, currentScreenshots);
}
const newAction = {type: at.TOP_SITES_UPDATED, data: links};
if (target) {
// Send an update to content so the preloaded tab can get the updated content
this.store.dispatch(ac.SendToContent(newAction, target));
} else {
// Broadcast an update to all open content pages
this.store.dispatch(ac.BroadcastToContent(newAction));
}
this.lastUpdated = Date.now();
}
+ _fetchIcon(link, screenshotCache = {}) {
+ // Check for tippy top icon or a rich icon.
+ this._tippyTopProvider.processSite(link);
+ if (!link.tippyTopIcon && (!link.favicon || link.faviconSize < MIN_FAVICON_SIZE)) {
+ // If no tippy top, then we get a screenshot.
+ if (screenshotCache[link.url]) {
+ link.screenshot = screenshotCache[link.url];
+ } else {
+ this.getScreenshot(link.url);
+ }
+ }
+ }
_getPinnedWithData(links) {
// Augment the pinned links with any other extra data we have for them already in the store.
// Alternatively you can pass in some links that you know have data you want the pinned links
// to also have. This is useful for start up to make sure pinned links have favicons
// (See github ticket #3428 fore more details)
- let originalLinks = links ? links : this.store.getState().TopSites.rows;
+ const originalLinks = links || this.store.getState().TopSites.rows;
const pinned = NewTabUtils.pinnedLinks.links;
return pinned.map(pinnedLink => {
if (pinnedLink) {
const hostname = shortURL(pinnedLink);
const originalLink = originalLinks.find(link => link && link.url === pinnedLink.url);
- return Object.assign(pinnedLink, originalLink || {hostname});
+ // If it's a new link then it won't have an icon, so fetch one
+ if (!originalLink) {
+ this._fetchIcon(pinnedLink);
+ }
+ return Object.assign(originalLink || {hostname}, pinnedLink);
}
return pinnedLink;
});
}
_broadcastPinnedSitesUpdated() {
this.store.dispatch(ac.BroadcastToContent({
type: at.PINNED_SITES_UPDATED,
data: this._getPinnedWithData()
--- a/browser/extensions/activity-stream/lib/TopStoriesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TopStoriesFeed.jsm
@@ -160,22 +160,37 @@ this.TopStoriesFeed = class TopStoriesFe
// If personalization is turned on we have to rotate stories on the client.
// An item can only be on top for two iterations (1hr) before it gets moved
// to the end. This will later be improved based on interactions/impressions.
rotate(items) {
if (!this.personalized || items.length <= 3) {
return items;
}
+ if (!this.topItems) {
+ this.topItems = new Map();
+ }
+
+ // This avoids an infinite recursion if for some reason the feed stops
+ // changing. Otherwise, there's a chance we'd be rotating forever to
+ // find an item we haven't displayed on top yet.
+ if (this.topItems.size >= items.length) {
+ this.topItems.clear();
+ }
+
const guid = items[0].guid;
- if (!this.topItem || !(guid in this.topItem)) {
- this.topItem = {[guid]: 0};
- } else if (++this.topItem[guid] === 2) {
- items.push(items.shift());
- this.topItem = {[items[0].guid]: 0};
+ if (!this.topItems.has(guid)) {
+ this.topItems.set(guid, 0);
+ } else {
+ const val = this.topItems.get(guid) + 1;
+ this.topItems.set(guid, val);
+ if (val >= 2) {
+ items.push(items.shift());
+ this.rotate(items);
+ }
}
return items;
}
getApiKeyFromPref(apiKeyPref) {
if (!apiKeyPref) {
return apiKeyPref;
}
--- a/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/PlacesFeed.test.js
@@ -93,18 +93,24 @@ describe("PlacesFeed", () => {
feed.onAction({type: at.BOOKMARK_URL, data, _target});
assert.calledWith(global.NewTabUtils.activityStreamLinks.addBookmark, data, _target.browser);
});
it("should delete a bookmark on DELETE_BOOKMARK_BY_ID", () => {
feed.onAction({type: at.DELETE_BOOKMARK_BY_ID, data: "g123kd"});
assert.calledWith(global.NewTabUtils.activityStreamLinks.deleteBookmark, "g123kd");
});
it("should delete a history entry on DELETE_HISTORY_URL", () => {
- feed.onAction({type: at.DELETE_HISTORY_URL, data: "guava.com"});
+ feed.onAction({type: at.DELETE_HISTORY_URL, data: {url: "guava.com", forceBlock: null}});
assert.calledWith(global.NewTabUtils.activityStreamLinks.deleteHistoryEntry, "guava.com");
+ assert.notCalled(global.NewTabUtils.activityStreamLinks.blockURL);
+ });
+ it("should delete a history entry on DELETE_HISTORY_URL and force a site to be blocked if specified", () => {
+ feed.onAction({type: at.DELETE_HISTORY_URL, data: {url: "guava.com", forceBlock: "g123kd"}});
+ assert.calledWith(global.NewTabUtils.activityStreamLinks.deleteHistoryEntry, "guava.com");
+ assert.calledWith(global.NewTabUtils.activityStreamLinks.blockURL, {url: "guava.com"});
});
it("should call openNewWindow with the correct url on OPEN_NEW_WINDOW", () => {
sinon.stub(feed, "openNewWindow");
const openWindowAction = {type: at.OPEN_NEW_WINDOW, data: {url: "foo.com"}};
feed.onAction(openWindowAction);
assert.calledWith(feed.openNewWindow, openWindowAction);
});
it("should call openNewWindow with the correct url and privacy args on OPEN_PRIVATE_WINDOW", () => {
--- a/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TopSitesFeed.test.js
@@ -282,63 +282,78 @@ describe("Top Sites Feed", () => {
sandbox.stub(feed, "getScreenshot");
await feed.refresh(action);
const reference = links.map(site => Object.assign({}, site, {hostname: shortURLStub(site)}));
assert.calledOnce(feed.store.dispatch);
assert.propertyVal(feed.store.dispatch.firstCall.args[0], "type", at.TOP_SITES_UPDATED);
assert.deepEqual(feed.store.dispatch.firstCall.args[0].data, reference);
});
- it("should reuse screenshots for existing links, and call feed.getScreenshot for others", async () => {
- sandbox.stub(feed, "getScreenshot");
+ it("should call _fetchIcon for each link and pass in existing screenshots", async () => {
feed.store.state.TopSites.rows = [{url: FAKE_LINKS[0].url, screenshot: "foo.jpg"}];
+ const expectedScreenshotCache = {};
+ expectedScreenshotCache[FAKE_LINKS[0].url] = "foo.jpg";
+ sinon.spy(feed, "_fetchIcon");
await feed.refresh(action);
-
const results = feed.store.dispatch.firstCall.args[0].data;
-
+ assert.callCount(feed._fetchIcon, results.length);
results.forEach(link => {
- if (link.url === FAKE_LINKS[0].url) {
- assert.equal(link.screenshot, "foo.jpg");
- } else {
- assert.calledWith(feed.getScreenshot, link.url);
- }
+ assert.calledWith(feed._fetchIcon, link, expectedScreenshotCache);
});
});
it("should handle empty slots in the resulting top sites array", async () => {
links = [FAKE_LINKS[0]];
fakeNewTabUtils.pinnedLinks.links = [null, null, FAKE_LINKS[1], null, null, null, null, null, FAKE_LINKS[2]];
sandbox.stub(feed, "getScreenshot");
await feed.refresh(action);
assert.calledOnce(feed.store.dispatch);
});
- it("should skip getting screenshot if there is a tippy top icon", async () => {
+ });
+ describe("#_fetchIcon", () => {
+ it("should reuse screenshots for existing links, and call feed.getScreenshot for others", () => {
+ sandbox.stub(feed, "getScreenshot");
+ const screenshotCache = {};
+ screenshotCache[FAKE_LINKS[0].url] = "foo.jpg";
+ screenshotCache[FAKE_LINKS[1].url] = "bar.png";
+
+ feed._fetchIcon(FAKE_LINKS[0], screenshotCache);
+ assert.notCalled(feed.getScreenshot);
+ assert.propertyVal(FAKE_LINKS[0], "screenshot", "foo.jpg");
+
+ feed._fetchIcon(FAKE_LINKS[1], screenshotCache);
+ assert.notCalled(feed.getScreenshot);
+ assert.propertyVal(FAKE_LINKS[1], "screenshot", "bar.png");
+
+ feed._fetchIcon(FAKE_LINKS[2], screenshotCache);
+ assert.calledOnce(feed.getScreenshot);
+ assert.calledWith(feed.getScreenshot, FAKE_LINKS[2].url);
+ });
+ it("should skip getting a screenshot if there is a tippy top icon", () => {
sandbox.stub(feed, "getScreenshot");
feed._tippyTopProvider.processSite = site => {
site.tippyTopIcon = "icon.png";
site.backgroundColor = "#fff";
return site;
};
- await feed.refresh(action);
- assert.calledOnce(feed.store.dispatch);
+ const link = {url: "example.com"};
+ feed._fetchIcon(link);
+ assert.propertyVal(link, "tippyTopIcon", "icon.png");
+ assert.notProperty(link, "screenshot");
assert.notCalled(feed.getScreenshot);
});
- it("should skip getting screenshot if there is an icon of size greater than 96x96 and no tippy top", async () => {
+ it("should skip getting a screenshot if there is an icon of size greater than 96x96 and no tippy top", () => {
sandbox.stub(feed, "getScreenshot");
- feed.getLinksWithDefaults = () => [{
+ const link = {
url: "foo.com",
favicon: "data:foo",
faviconSize: 196
- }];
- feed._tippyTopProvider.processSite = site => {
- site.tippyTopIcon = null;
- site.backgroundColor = null;
- return site;
};
- await feed.refresh(action);
- assert.calledOnce(feed.store.dispatch);
+ feed._fetchIcon(link);
+ assert.notProperty(link, "tippyTopIcon");
+ assert.notProperty(link, "screenshot");
assert.notCalled(feed.getScreenshot);
});
});
describe("getScreenshot", () => {
it("should call Screenshots.getScreenshotForURL with the right url", async () => {
const url = "foo.com";
await feed.getScreenshot(url);
assert.calledWith(fakeScreenshot.getScreenshotForURL, url);
@@ -412,20 +427,42 @@ describe("Top Sites Feed", () => {
});
it("should compare against links if available, instead of getting from store", () => {
const frecentSite = {url: "foo.com", faviconSize: 32, favicon: "favicon.png"};
const pinnedSite1 = {url: "bar.com"};
const pinnedSite2 = {url: "foo.com"};
fakeNewTabUtils.pinnedLinks.links = [pinnedSite1, pinnedSite2];
feed.store = {getState() { return {TopSites: {rows: sinon.spy()}}; }};
let result = feed._getPinnedWithData([frecentSite]);
- assert.deepEqual(result[0], pinnedSite1);
- assert.deepEqual(result[1], Object.assign({}, frecentSite, pinnedSite2));
+ assert.include(result[0], pinnedSite1);
+ assert.include(result[1], Object.assign({}, frecentSite, pinnedSite2));
assert.notCalled(feed.store.getState().TopSites.rows);
});
+ it("should fetch an icon on TOP_SITES_PIN and TOP_SITES_ADD for new urls", () => {
+ feed.store.getState = () => ({TopSites: {rows: FAKE_LINKS}});
+ fakeNewTabUtils.pinnedLinks = {
+ links: new Array(6).fill(null),
+ pin(site, index) {
+ this.links[index] = site;
+ }
+ };
+ sinon.spy(feed, "_fetchIcon");
+
+ const pinExistingAction = {type: at.TOP_SITES_PIN, data: {site: FAKE_LINKS[4], index: 4}};
+ const addAction = {type: at.TOP_SITES_ADD, data: {site: {url: "foo.com"}}};
+ const pinNewAction = {type: at.TOP_SITES_PIN, data: {site: {url: "bar.net"}, index: 0}};
+ feed.onAction(pinExistingAction);
+ feed.onAction(addAction);
+ feed.onAction(pinNewAction);
+
+ assert.calledTwice(feed._fetchIcon);
+ assert.neverCalledWithMatch(feed._fetchIcon, {url: FAKE_LINKS[4].url});
+ assert.calledWithMatch(feed._fetchIcon, {url: "foo.com"});
+ assert.calledWithMatch(feed._fetchIcon, {url: "bar.net"});
+ });
it("should call unpin with correct parameters on TOP_SITES_UNPIN", () => {
fakeNewTabUtils.pinnedLinks.links = [null, null, {url: "foo.com"}, null, null, null, null, null, FAKE_LINKS[0]];
const unpinAction = {
type: at.TOP_SITES_UNPIN,
data: {site: {url: "foo.com"}}
};
feed.onAction(unpinAction);
assert.calledOnce(fakeNewTabUtils.pinnedLinks.unpin);
--- a/browser/extensions/activity-stream/test/unit/lib/TopStoriesFeed.test.js
+++ b/browser/extensions/activity-stream/test/unit/lib/TopStoriesFeed.test.js
@@ -332,37 +332,46 @@ describe("Top Stories Feed", () => {
assert.deepEqual(items, [{"score": 0.2}, {"score": 0.1}]);
});
it("should rotate items if personalization is preffed on", () => {
let items = [{"guid": "g1"}, {"guid": "g2"}, {"guid": "g3"}, {"guid": "g4"}];
instance.personalized = true;
let rotated = instance.rotate(items);
- assert.deepEqual({"g1": 0}, instance.topItem);
+ assert.deepEqual(new Map([["g1", 0]]), instance.topItems);
assert.deepEqual(items, rotated);
rotated = instance.rotate(items);
- assert.deepEqual({"g1": 1}, instance.topItem);
+ assert.deepEqual(new Map([["g1", 1]]), instance.topItems);
assert.deepEqual(items, rotated);
rotated = instance.rotate(items);
- assert.deepEqual({"g2": 0}, instance.topItem);
+ assert.deepEqual(new Map([["g1", 2], ["g2", 0]]), instance.topItems);
assert.deepEqual([{"guid": "g2"}, {"guid": "g3"}, {"guid": "g4"}, {"guid": "g1"}], rotated);
- rotated = instance.rotate(items);
- assert.deepEqual({"g2": 1}, instance.topItem);
+ // Simulate g1 on top again which should again be rotated to the end
+ rotated = instance.rotate([{"guid": "g1"}, {"guid": "g2"}, {"guid": "g3"}, {"guid": "g4"}]);
+ assert.deepEqual(new Map([["g1", 3], ["g2", 1]]), instance.topItems);
assert.deepEqual([{"guid": "g2"}, {"guid": "g3"}, {"guid": "g4"}, {"guid": "g1"}], rotated);
});
- it("should note rotate items if personalization is preffed off", () => {
+ it("should not rotate items if personalization is preffed off", () => {
let items = [{"guid": "g1"}, {"guid": "g2"}, {"guid": "g3"}, {"guid": "g4"}];
instance.personalized = false;
- instance.topItem = {"g1": 1};
+ instance.topItems = new Map([["g1", 1]]);
+ const rotated = instance.rotate(items);
+ assert.deepEqual(items, rotated);
+ });
+ it("should stop rotating if all items have been on top", () => {
+ let items = [{"guid": "g1"}, {"guid": "g2"}, {"guid": "g3"}, {"guid": "g4"}];
+ instance.topItems = new Map([["g1", 2], ["g2", 2], ["g3", 2], ["g4", 2]]);
+ instance.personalized = true;
+
const rotated = instance.rotate(items);
assert.deepEqual(items, rotated);
});
it("should insert spoc at provided interval", async () => {
let fetchStub = globals.sandbox.stub();
globals.set("fetch", fetchStub);
globals.set("NewTabUtils", {blockedLinks: {isBlocked: globals.sandbox.spy()}});