diff --git a/browser/components/newtab/common/Actions.jsm b/browser/components/newtab/common/Actions.jsm
index 58aba384f7b3..44679f8834d5 100644
--- a/browser/components/newtab/common/Actions.jsm
+++ b/browser/components/newtab/common/Actions.jsm
@@ -56,6 +56,7 @@ for (const type of [
"DISCOVERY_STREAM_SPOCS_ENDPOINT",
"DISCOVERY_STREAM_SPOCS_FILL",
"DISCOVERY_STREAM_SPOCS_UPDATE",
+ "DISCOVERY_STREAM_SPOC_BLOCKED",
"DISCOVERY_STREAM_SPOC_IMPRESSION",
"DOWNLOAD_CHANGED",
"FAKE_FOCUS_SEARCH",
diff --git a/browser/components/newtab/common/Reducers.jsm b/browser/components/newtab/common/Reducers.jsm
index e63603a0fcab..a2e5374ca45b 100644
--- a/browser/components/newtab/common/Reducers.jsm
+++ b/browser/components/newtab/common/Reducers.jsm
@@ -66,6 +66,7 @@ const INITIAL_STATE = {
data: {}, // {spocs: []}
loaded: false,
frequency_caps: [],
+ blocked: [],
},
},
Search: {
@@ -612,6 +613,14 @@ function DiscoveryStream(prevState = INITIAL_STATE.DiscoveryStream, action) {
};
}
return prevState;
+ case at.DISCOVERY_STREAM_SPOC_BLOCKED:
+ return {
+ ...prevState,
+ spocs: {
+ ...prevState.spocs,
+ blocked: [...prevState.spocs.blocked, action.data.url],
+ },
+ };
case at.DISCOVERY_STREAM_LINK_BLOCKED:
return isNotReady()
? prevState
diff --git a/browser/components/newtab/content-src/asrouter/components/ModalOverlay/ModalOverlay.jsx b/browser/components/newtab/content-src/asrouter/components/ModalOverlay/ModalOverlay.jsx
index 47c496d81883..fa1fe5e9f159 100644
--- a/browser/components/newtab/content-src/asrouter/components/ModalOverlay/ModalOverlay.jsx
+++ b/browser/components/newtab/content-src/asrouter/components/ModalOverlay/ModalOverlay.jsx
@@ -19,11 +19,26 @@ export class ModalOverlayWrapper extends React.PureComponent {
componentWillMount() {
this.props.document.addEventListener("keydown", this.onKeyDown);
this.props.document.body.classList.add("modal-open");
+ this.header = this.props.document.getElementById(
+ "header-asrouter-container"
+ );
+
+ if (this.header) {
+ this.header.classList.add("modal-scroll");
+ this.props.document.getElementById("root").classList.add("modal-height");
+ }
}
componentWillUnmount() {
this.props.document.removeEventListener("keydown", this.onKeyDown);
this.props.document.body.classList.remove("modal-open");
+
+ if (this.header) {
+ this.header.classList.remove("modal-scroll");
+ this.props.document
+ .getElementById("root")
+ .classList.remove("modal-height");
+ }
}
render() {
diff --git a/browser/components/newtab/content-src/asrouter/components/ModalOverlay/_ModalOverlay.scss b/browser/components/newtab/content-src/asrouter/components/ModalOverlay/_ModalOverlay.scss
index 13f44417235e..97b9c21f4a4c 100644
--- a/browser/components/newtab/content-src/asrouter/components/ModalOverlay/_ModalOverlay.scss
+++ b/browser/components/newtab/content-src/asrouter/components/ModalOverlay/_ModalOverlay.scss
@@ -19,6 +19,19 @@
}
}
+.modal-scroll {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+}
+
+.modal-height {
+ // "Welcome header" has 40px of padding and 36px font size that get neglected using position absolute
+ // causing this to visually collide with the newtab searchbar
+ padding-top: 80px;
+}
+
.modalOverlayInner {
width: 960px;
position: fixed;
@@ -30,7 +43,6 @@
display: none;
z-index: 1101;
-
// modal takes over entire screen
@media(max-width: 960px) {
width: 100%;
diff --git a/browser/components/newtab/content-src/asrouter/templates/Trailhead/_Trailhead.scss b/browser/components/newtab/content-src/asrouter/templates/Trailhead/_Trailhead.scss
index 3fedd5effa64..9a6eb68dbeb7 100644
--- a/browser/components/newtab/content-src/asrouter/templates/Trailhead/_Trailhead.scss
+++ b/browser/components/newtab/content-src/asrouter/templates/Trailhead/_Trailhead.scss
@@ -401,7 +401,7 @@
.inline-onboarding {
&.activity-stream.welcome {
- overflow-y: scroll;
+ overflow-y: hidden;
}
.modalOverlayInner {
diff --git a/browser/components/newtab/content-src/components/Card/Card.jsx b/browser/components/newtab/content-src/components/Card/Card.jsx
index 2ee3c813732f..d7730fffcfd7 100644
--- a/browser/components/newtab/content-src/components/Card/Card.jsx
+++ b/browser/components/newtab/content-src/components/Card/Card.jsx
@@ -5,6 +5,7 @@
import { actionCreators as ac, actionTypes as at } from "common/Actions.jsm";
import { cardContextTypes } from "./types";
import { connect } from "react-redux";
+import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
import React from "react";
import { ScreenshotUtils } from "content-src/lib/screenshot-utils";
@@ -27,11 +28,9 @@ export class _Card extends React.PureComponent {
this.state = {
activeCard: null,
imageLoaded: false,
- showContextMenu: false,
cardImage: null,
};
- this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
- this.onMenuUpdate = this.onMenuUpdate.bind(this);
+ this.onMenuButtonUpdate = this.onMenuButtonUpdate.bind(this);
this.onLinkClick = this.onLinkClick.bind(this);
}
@@ -117,12 +116,12 @@ export class _Card extends React.PureComponent {
return nextState;
}
- onMenuButtonClick(event) {
- event.preventDefault();
- this.setState({
- activeCard: this.props.index,
- showContextMenu: true,
- });
+ onMenuButtonUpdate(isOpen) {
+ if (isOpen) {
+ this.setState({ activeCard: this.props.index });
+ } else {
+ this.setState({ activeCard: null });
+ }
}
/**
@@ -191,10 +190,6 @@ export class _Card extends React.PureComponent {
}
}
- onMenuUpdate(showContextMenu) {
- this.setState({ showContextMenu });
- }
-
componentDidMount() {
this.maybeLoadImage();
}
@@ -239,8 +234,7 @@ export class _Card extends React.PureComponent {
} = this.props;
const { props } = this;
const title = link.title || link.hostname;
- const isContextMenuOpen =
- this.state.showContextMenu && this.state.activeCard === index;
+ const isContextMenuOpen = this.state.activeCard === index;
// Display "now" as "trending" until we have new strings #3402
const { icon, fluentID } =
cardContextTypes[link.type === "now" ? "trending" : link.type] || {};
@@ -329,25 +323,21 @@ export class _Card extends React.PureComponent {
{!props.placeholder && (
-
- )}
- {isContextMenuOpen && (
-
+
+
+
)}
);
diff --git a/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx b/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx
index bea61a7ea776..d2b75b70cd7c 100644
--- a/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx
+++ b/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx
@@ -8,6 +8,7 @@ import { FluentOrText } from "content-src/components/FluentOrText/FluentOrText";
import React from "react";
import { SectionMenu } from "content-src/components/SectionMenu/SectionMenu";
import { SectionMenuOptions } from "content-src/lib/section-menu-options";
+import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
const VISIBLE = "visible";
const VISIBILITY_CHANGE_EVENT = "visibilitychange";
@@ -20,7 +21,6 @@ export class CollapsibleSection extends React.PureComponent {
this.onKeyPress = this.onKeyPress.bind(this);
this.onTransitionEnd = this.onTransitionEnd.bind(this);
this.enableOrDisableAnimation = this.enableOrDisableAnimation.bind(this);
- this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuButtonMouseEnter = this.onMenuButtonMouseEnter.bind(this);
this.onMenuButtonMouseLeave = this.onMenuButtonMouseLeave.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
@@ -158,11 +158,6 @@ export class CollapsibleSection extends React.PureComponent {
);
}
- onMenuButtonClick(event) {
- event.preventDefault();
- this.setState({ showContextMenu: true });
- }
-
onMenuButtonMouseEnter() {
this.setState({ menuButtonHover: true });
}
@@ -258,14 +253,11 @@ export class CollapsibleSection extends React.PureComponent {
-
- {showContextMenu && (
+
- )}
+
diff --git a/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx b/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx
index 0856e2218e0b..0af1522ddbb0 100644
--- a/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx
+++ b/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx
@@ -60,6 +60,7 @@ export class ContextMenu extends React.PureComponent {
key={i}
option={option}
hideContext={this.hideContext}
+ keyboardAccess={this.props.keyboardAccess}
tabIndex="0"
/>
)
@@ -76,6 +77,7 @@ export class ContextMenuItem extends React.PureComponent {
super(props);
this.onClick = this.onClick.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
+ this.focusFirst = this.focusFirst.bind(this);
}
onClick() {
@@ -83,6 +85,13 @@ export class ContextMenuItem extends React.PureComponent {
this.props.option.onClick();
}
+ // Focus the first menu item if the menu was accessed via the keyboard.
+ focusFirst(button) {
+ if (this.props.keyboardAccess && button) {
+ button.focus();
+ }
+ }
+
// This selects the correct node based on the key pressed
focusSibling(target, key) {
const parent = target.parentNode;
@@ -138,6 +147,7 @@ export class ContextMenuItem extends React.PureComponent {
tabIndex="0"
onClick={this.onClick}
onKeyDown={this.onKeyDown}
+ ref={option.first ? this.focusFirst : null}
>
{option.icon && (
diff --git a/browser/components/newtab/content-src/components/ContextMenu/ContextMenuButton.jsx b/browser/components/newtab/content-src/components/ContextMenu/ContextMenuButton.jsx
new file mode 100644
index 000000000000..56a9e2de9e63
--- /dev/null
+++ b/browser/components/newtab/content-src/components/ContextMenu/ContextMenuButton.jsx
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import React from "react";
+
+export class ContextMenuButton extends React.PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ showContextMenu: false,
+ contextMenuKeyboard: false,
+ };
+ this.onClick = this.onClick.bind(this);
+ this.onKeyDown = this.onKeyDown.bind(this);
+ this.onUpdate = this.onUpdate.bind(this);
+ }
+
+ openContextMenu(isKeyBoard, event) {
+ if (this.props.onUpdate) {
+ this.props.onUpdate(true);
+ }
+ this.setState({
+ showContextMenu: true,
+ contextMenuKeyboard: isKeyBoard,
+ });
+ }
+
+ onClick(event) {
+ event.preventDefault();
+ this.openContextMenu(false, event);
+ }
+
+ onKeyDown(event) {
+ if (event.key === "Enter") {
+ event.preventDefault();
+ this.openContextMenu(true, event);
+ }
+ }
+
+ onUpdate(showContextMenu) {
+ if (this.props.onUpdate) {
+ this.props.onUpdate(showContextMenu);
+ }
+ this.setState({ showContextMenu });
+ }
+
+ render() {
+ const { tooltipArgs, tooltip, children, refFunction } = this.props;
+ const { showContextMenu, contextMenuKeyboard } = this.state;
+
+ return (
+
+
+ {showContextMenu
+ ? React.cloneElement(children, {
+ keyboardAccess: contextMenuKeyboard,
+ onUpdate: this.onUpdate,
+ })
+ : null}
+
+ );
+ }
+}
diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx
index 1bfcf8e027b0..4636efcc091a 100644
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx
@@ -3,36 +3,23 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
+import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
import React from "react";
export class DSLinkMenu extends React.PureComponent {
constructor(props) {
super(props);
- this.state = {
- activeCard: null,
- showContextMenu: false,
- };
this.windowObj = this.props.windowObj || window; // Added to support unit tests
- this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
this.onMenuShow = this.onMenuShow.bind(this);
this.contextMenuButtonRef = React.createRef();
}
- onMenuButtonClick(event) {
- event.preventDefault();
- this.setState({
- activeCard: this.props.index,
- showContextMenu: true,
- });
- }
-
onMenuUpdate(showContextMenu) {
if (!showContextMenu) {
const dsLinkMenuHostDiv = this.contextMenuButtonRef.current.parentElement;
dsLinkMenuHostDiv.parentElement.classList.remove("active", "last-item");
}
- this.setState({ showContextMenu });
}
nextAnimationFrame() {
@@ -51,8 +38,6 @@ export class DSLinkMenu extends React.PureComponent {
render() {
const { index, dispatch } = this.props;
- const isContextMenuOpen =
- this.state.showContextMenu && this.state.activeCard === index;
const TOP_STORIES_CONTEXT_MENU_OPTIONS = [
"CheckBookmarkOrArchive",
"CheckSavedToPocket",
@@ -67,20 +52,16 @@ export class DSLinkMenu extends React.PureComponent {
return (
-
- {isContextMenuOpen && (
+
- )}
+
);
}
diff --git a/browser/components/newtab/content-src/components/LinkMenu/LinkMenu.jsx b/browser/components/newtab/content-src/components/LinkMenu/LinkMenu.jsx
index a04821413dba..18c300ca1c66 100644
--- a/browser/components/newtab/content-src/components/LinkMenu/LinkMenu.jsx
+++ b/browser/components/newtab/content-src/components/LinkMenu/LinkMenu.jsx
@@ -85,6 +85,7 @@ export class _LinkMenu extends React.PureComponent {
onUpdate={this.props.onUpdate}
onShow={this.props.onShow}
options={this.getOptions()}
+ keyboardAccess={this.props.keyboardAccess}
/>
);
}
diff --git a/browser/components/newtab/content-src/components/SectionMenu/SectionMenu.jsx b/browser/components/newtab/content-src/components/SectionMenu/SectionMenu.jsx
index 6aa3a335b8c6..54db541119dc 100644
--- a/browser/components/newtab/content-src/components/SectionMenu/SectionMenu.jsx
+++ b/browser/components/newtab/content-src/components/SectionMenu/SectionMenu.jsx
@@ -26,6 +26,19 @@ const WEBEXT_SECTION_MENU_OPTIONS = [
];
export class _SectionMenu extends React.PureComponent {
+ handleAddWhileCollapsed() {
+ const { action, userEvent } = SectionMenuOptions.ExpandSection(this.props);
+ this.props.dispatch(action);
+ if (userEvent) {
+ this.props.dispatch(
+ ac.UserEvent({
+ event: userEvent,
+ source: this.props.source,
+ })
+ );
+ }
+ }
+
getOptions() {
const { props } = this;
@@ -51,6 +64,14 @@ export class _SectionMenu extends React.PureComponent {
const { action, id, type, userEvent } = option;
if (!type && id) {
option.onClick = () => {
+ const hasAddEvent =
+ userEvent === "MENU_ADD_TOPSITE" ||
+ userEvent === "MENU_ADD_SEARCH";
+
+ if (props.collapsed && hasAddEvent) {
+ this.handleAddWhileCollapsed();
+ }
+
props.dispatch(action);
if (userEvent) {
props.dispatch(
@@ -75,7 +96,11 @@ export class _SectionMenu extends React.PureComponent {
render() {
return (
-
+
);
}
}
diff --git a/browser/components/newtab/content-src/components/TopSites/TopSite.jsx b/browser/components/newtab/content-src/components/TopSites/TopSite.jsx
index ddc786b79f08..845a91aa3c5a 100644
--- a/browser/components/newtab/content-src/components/TopSites/TopSite.jsx
+++ b/browser/components/newtab/content-src/components/TopSites/TopSite.jsx
@@ -14,6 +14,7 @@ import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
import React from "react";
import { ScreenshotUtils } from "content-src/lib/screenshot-utils";
import { TOP_SITES_MAX_SITES_PER_ROW } from "common/Reducers.jsm";
+import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
export class TopSiteLink extends React.PureComponent {
constructor(props) {
@@ -273,7 +274,6 @@ export class TopSite extends React.PureComponent {
super(props);
this.state = { showContextMenu: false };
this.onLinkClick = this.onLinkClick.bind(this);
- this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
}
@@ -335,21 +335,18 @@ export class TopSite extends React.PureComponent {
}
}
- onMenuButtonClick(event) {
- event.preventDefault();
- this.props.onActivate(this.props.index);
- this.setState({ showContextMenu: true });
- }
-
- onMenuUpdate(showContextMenu) {
- this.setState({ showContextMenu });
+ onMenuUpdate(isOpen) {
+ if (isOpen) {
+ this.props.onActivate(this.props.index);
+ } else {
+ this.props.onActivate();
+ }
}
render() {
const { props } = this;
const { link } = props;
- const isContextMenuOpen =
- this.state.showContextMenu && props.activeIndex === props.index;
+ const isContextMenuOpen = props.activeIndex === props.index;
const title = link.label || link.hostname;
return (
-
- {isContextMenuOpen && (
+
- )}
+
);
diff --git a/browser/components/newtab/content-src/lib/selectLayoutRender.js b/browser/components/newtab/content-src/lib/selectLayoutRender.js
index 34245b1bf3e3..a83b71d04762 100644
--- a/browser/components/newtab/content-src/lib/selectLayoutRender.js
+++ b/browser/components/newtab/content-src/lib/selectLayoutRender.js
@@ -30,8 +30,10 @@ export const selectLayoutRender = (state, prefs, rickRollCache) => {
if (rickRoll <= spocsConfig.probability) {
spocIndex++;
- recommendations.splice(position.index, 0, spoc);
- chosenSpocs.add(spoc);
+ if (!spocs.blocked.includes(spoc.url)) {
+ recommendations.splice(position.index, 0, spoc);
+ chosenSpocs.add(spoc);
+ }
} else {
unchosenSpocs.add(spoc);
}
diff --git a/browser/components/newtab/css/activity-stream-linux.css b/browser/components/newtab/css/activity-stream-linux.css
index 32b53cd46ec8..3c4ac9371132 100644
--- a/browser/components/newtab/css/activity-stream-linux.css
+++ b/browser/components/newtab/css/activity-stream-linux.css
@@ -3003,6 +3003,15 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
.modalOverlayOuter.active {
display: block; }
+.modal-scroll {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ overflow: auto; }
+
+.modal-height {
+ padding-top: 80px; }
+
.modalOverlayInner {
width: 960px;
position: fixed;
@@ -4108,7 +4117,7 @@ a.firstrun-link {
text-align: center; }
.inline-onboarding.activity-stream.welcome {
- overflow-y: scroll; }
+ overflow-y: hidden; }
.inline-onboarding .modalOverlayInner {
position: absolute; }
diff --git a/browser/components/newtab/css/activity-stream-mac.css b/browser/components/newtab/css/activity-stream-mac.css
index 28c195037b7f..71f20ae0bc2c 100644
--- a/browser/components/newtab/css/activity-stream-mac.css
+++ b/browser/components/newtab/css/activity-stream-mac.css
@@ -3006,6 +3006,15 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
.modalOverlayOuter.active {
display: block; }
+.modal-scroll {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ overflow: auto; }
+
+.modal-height {
+ padding-top: 80px; }
+
.modalOverlayInner {
width: 960px;
position: fixed;
@@ -4111,7 +4120,7 @@ a.firstrun-link {
text-align: center; }
.inline-onboarding.activity-stream.welcome {
- overflow-y: scroll; }
+ overflow-y: hidden; }
.inline-onboarding .modalOverlayInner {
position: absolute; }
diff --git a/browser/components/newtab/css/activity-stream-windows.css b/browser/components/newtab/css/activity-stream-windows.css
index c71147f7c5c5..b95a4bcf7006 100644
--- a/browser/components/newtab/css/activity-stream-windows.css
+++ b/browser/components/newtab/css/activity-stream-windows.css
@@ -3003,6 +3003,15 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
.modalOverlayOuter.active {
display: block; }
+.modal-scroll {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ overflow: auto; }
+
+.modal-height {
+ padding-top: 80px; }
+
.modalOverlayInner {
width: 960px;
position: fixed;
@@ -4108,7 +4117,7 @@ a.firstrun-link {
text-align: center; }
.inline-onboarding.activity-stream.welcome {
- overflow-y: scroll; }
+ overflow-y: hidden; }
.inline-onboarding .modalOverlayInner {
position: absolute; }
diff --git a/browser/components/newtab/data/content/activity-stream.bundle.js b/browser/components/newtab/data/content/activity-stream.bundle.js
index 46197a84571c..b4044a4ab753 100644
--- a/browser/components/newtab/data/content/activity-stream.bundle.js
+++ b/browser/components/newtab/data/content/activity-stream.bundle.js
@@ -93,7 +93,7 @@
__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__(48);
+/* harmony import */ var content_src_lib_detect_user_session_start__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(49);
/* harmony import */ var content_src_lib_init_store__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6);
/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(24);
/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_4__);
@@ -101,7 +101,7 @@ __webpack_require__.r(__webpack_exports__);
/* 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__(14);
/* 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__(53);
+/* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(54);
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -194,7 +194,7 @@ const globalImportContext = typeof Window === "undefined" ? BACKGROUND_PROCESS :
// }
const actionTypes = {};
-for (const type of ["ADDONS_INFO_REQUEST", "ADDONS_INFO_RESPONSE", "ARCHIVE_FROM_POCKET", "AS_ROUTER_INITIALIZED", "AS_ROUTER_PREF_CHANGED", "AS_ROUTER_TARGETING_UPDATE", "AS_ROUTER_TELEMETRY_USER_EVENT", "BLOCK_URL", "BOOKMARK_URL", "COPY_DOWNLOAD_LINK", "DELETE_BOOKMARK_BY_ID", "DELETE_FROM_POCKET", "DELETE_HISTORY_URL", "DIALOG_CANCEL", "DIALOG_OPEN", "DISCOVERY_STREAM_CONFIG_CHANGE", "DISCOVERY_STREAM_CONFIG_SETUP", "DISCOVERY_STREAM_CONFIG_SET_VALUE", "DISCOVERY_STREAM_FEEDS_UPDATE", "DISCOVERY_STREAM_FEED_UPDATE", "DISCOVERY_STREAM_IMPRESSION_STATS", "DISCOVERY_STREAM_LAYOUT_RESET", "DISCOVERY_STREAM_LAYOUT_UPDATE", "DISCOVERY_STREAM_LINK_BLOCKED", "DISCOVERY_STREAM_LOADED_CONTENT", "DISCOVERY_STREAM_RETRY_FEED", "DISCOVERY_STREAM_SPOCS_CAPS", "DISCOVERY_STREAM_SPOCS_ENDPOINT", "DISCOVERY_STREAM_SPOCS_FILL", "DISCOVERY_STREAM_SPOCS_UPDATE", "DISCOVERY_STREAM_SPOC_IMPRESSION", "DOWNLOAD_CHANGED", "FAKE_FOCUS_SEARCH", "FILL_SEARCH_TERM", "HANDOFF_SEARCH_TO_AWESOMEBAR", "HIDE_SEARCH", "INIT", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_DOWNLOAD_FILE", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "OPEN_WEBEXT_SETTINGS", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_CHANGED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PLACES_SAVED_TO_POCKET", "POCKET_CTA", "POCKET_LINK_DELETED_OR_ARCHIVED", "POCKET_LOGGED_IN", "POCKET_WAITING_FOR_SPOC", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "PREVIEW_REQUEST", "PREVIEW_REQUEST_CANCEL", "PREVIEW_RESPONSE", "REMOVE_DOWNLOAD_FILE", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_MOVE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_DOWNLOAD_FILE", "SHOW_FIREFOX_ACCOUNTS", "SHOW_SEARCH", "SKIPPED_SIGNIN", "SNIPPETS_BLOCKLIST_CLEARED", "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_PREVIEW_MODE", "SNIPPETS_RESET", "SNIPPET_BLOCKED", "SUBMIT_EMAIL", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_CLOSE_SEARCH_SHORTCUTS_MODAL", "TOP_SITES_EDIT", "TOP_SITES_INSERT", "TOP_SITES_OPEN_SEARCH_SHORTCUTS_MODAL", "TOP_SITES_PIN", "TOP_SITES_PREFS_UPDATED", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "TOTAL_BOOKMARKS_REQUEST", "TOTAL_BOOKMARKS_RESPONSE", "TRAILHEAD_ENROLL_EVENT", "UNINIT", "UPDATE_PINNED_SEARCH_SHORTCUTS", "UPDATE_SEARCH_SHORTCUTS", "UPDATE_SECTION_PREFS", "WEBEXT_CLICK", "WEBEXT_DISMISS"]) {
+for (const type of ["ADDONS_INFO_REQUEST", "ADDONS_INFO_RESPONSE", "ARCHIVE_FROM_POCKET", "AS_ROUTER_INITIALIZED", "AS_ROUTER_PREF_CHANGED", "AS_ROUTER_TARGETING_UPDATE", "AS_ROUTER_TELEMETRY_USER_EVENT", "BLOCK_URL", "BOOKMARK_URL", "COPY_DOWNLOAD_LINK", "DELETE_BOOKMARK_BY_ID", "DELETE_FROM_POCKET", "DELETE_HISTORY_URL", "DIALOG_CANCEL", "DIALOG_OPEN", "DISCOVERY_STREAM_CONFIG_CHANGE", "DISCOVERY_STREAM_CONFIG_SETUP", "DISCOVERY_STREAM_CONFIG_SET_VALUE", "DISCOVERY_STREAM_FEEDS_UPDATE", "DISCOVERY_STREAM_FEED_UPDATE", "DISCOVERY_STREAM_IMPRESSION_STATS", "DISCOVERY_STREAM_LAYOUT_RESET", "DISCOVERY_STREAM_LAYOUT_UPDATE", "DISCOVERY_STREAM_LINK_BLOCKED", "DISCOVERY_STREAM_LOADED_CONTENT", "DISCOVERY_STREAM_RETRY_FEED", "DISCOVERY_STREAM_SPOCS_CAPS", "DISCOVERY_STREAM_SPOCS_ENDPOINT", "DISCOVERY_STREAM_SPOCS_FILL", "DISCOVERY_STREAM_SPOCS_UPDATE", "DISCOVERY_STREAM_SPOC_BLOCKED", "DISCOVERY_STREAM_SPOC_IMPRESSION", "DOWNLOAD_CHANGED", "FAKE_FOCUS_SEARCH", "FILL_SEARCH_TERM", "HANDOFF_SEARCH_TO_AWESOMEBAR", "HIDE_SEARCH", "INIT", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_DOWNLOAD_FILE", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "OPEN_WEBEXT_SETTINGS", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_CHANGED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PLACES_SAVED_TO_POCKET", "POCKET_CTA", "POCKET_LINK_DELETED_OR_ARCHIVED", "POCKET_LOGGED_IN", "POCKET_WAITING_FOR_SPOC", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "PREVIEW_REQUEST", "PREVIEW_REQUEST_CANCEL", "PREVIEW_RESPONSE", "REMOVE_DOWNLOAD_FILE", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_MOVE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_DOWNLOAD_FILE", "SHOW_FIREFOX_ACCOUNTS", "SHOW_SEARCH", "SKIPPED_SIGNIN", "SNIPPETS_BLOCKLIST_CLEARED", "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_PREVIEW_MODE", "SNIPPETS_RESET", "SNIPPET_BLOCKED", "SUBMIT_EMAIL", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_CLOSE_SEARCH_SHORTCUTS_MODAL", "TOP_SITES_EDIT", "TOP_SITES_INSERT", "TOP_SITES_OPEN_SEARCH_SHORTCUTS_MODAL", "TOP_SITES_PIN", "TOP_SITES_PREFS_UPDATED", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "TOTAL_BOOKMARKS_REQUEST", "TOTAL_BOOKMARKS_RESPONSE", "TRAILHEAD_ENROLL_EVENT", "UNINIT", "UPDATE_PINNED_SEARCH_SHORTCUTS", "UPDATE_SEARCH_SHORTCUTS", "UPDATE_SECTION_PREFS", "WEBEXT_CLICK", "WEBEXT_DISMISS"]) {
actionTypes[type] = type;
} // These are acceptable actions for AS Router messages to have. They can show up
// as call-to-action buttons in snippets, onboarding tour, etc.
@@ -565,12 +565,12 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var content_src_components_ConfirmDialog_ConfirmDialog__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(27);
/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(24);
/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_4__);
-/* harmony import */ var content_src_components_DiscoveryStreamBase_DiscoveryStreamBase__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(49);
-/* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(31);
+/* harmony import */ var content_src_components_DiscoveryStreamBase_DiscoveryStreamBase__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(50);
+/* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(32);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(9);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_7__);
-/* harmony import */ var content_src_components_Search_Search__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(47);
-/* harmony import */ var content_src_components_Sections_Sections__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(36);
+/* harmony import */ var content_src_components_Search_Search__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(48);
+/* harmony import */ var content_src_components_Sections_Sections__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(37);
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); }
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -1719,9 +1719,9 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ASRouterUISurface", function() { return ASRouterUISurface; });
/* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
/* harmony import */ var content_src_lib_init_store__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6);
-/* harmony import */ var _rich_text_strings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(52);
+/* harmony import */ var _rich_text_strings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(53);
/* harmony import */ var _components_ImpressionsWrapper_ImpressionsWrapper__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
-/* harmony import */ var fluent_react__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(50);
+/* harmony import */ var fluent_react__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(51);
/* harmony import */ var content_src_lib_constants__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(11);
/* harmony import */ var _templates_OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(12);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(9);
@@ -1729,7 +1729,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(14);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_8__);
/* harmony import */ var _templates_ReturnToAMO_ReturnToAMO__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(15);
-/* harmony import */ var _templates_template_manifest__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(51);
+/* harmony import */ var _templates_template_manifest__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(52);
/* harmony import */ var _templates_StartupOverlay_StartupOverlay__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(23);
/* harmony import */ var _templates_Trailhead_Trailhead__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(25);
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); }
@@ -2641,11 +2641,22 @@ class ModalOverlayWrapper extends react__WEBPACK_IMPORTED_MODULE_0___default.a.P
componentWillMount() {
this.props.document.addEventListener("keydown", this.onKeyDown);
this.props.document.body.classList.add("modal-open");
+ this.header = this.props.document.getElementById("header-asrouter-container");
+
+ if (this.header) {
+ this.header.classList.add("modal-scroll");
+ this.props.document.getElementById("root").classList.add("modal-height");
+ }
}
componentWillUnmount() {
this.props.document.removeEventListener("keydown", this.onKeyDown);
this.props.document.body.classList.remove("modal-open");
+
+ if (this.header) {
+ this.header.classList.remove("modal-scroll");
+ this.props.document.getElementById("root").classList.remove("modal-height");
+ }
}
render() {
@@ -2800,10 +2811,10 @@ class ReturnToAMO extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureCompo
__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__(50);
+/* harmony import */ var fluent_react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
/* 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__(52);
+/* harmony import */ var _rich_text_strings__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(53);
/* harmony import */ var _template_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(17);
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); }
@@ -3876,6 +3887,7 @@ class ContextMenu extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureCompo
key: i,
option: option,
hideContext: this.hideContext,
+ keyboardAccess: this.props.keyboardAccess,
tabIndex: "0"
}))))
);
@@ -3887,11 +3899,19 @@ class ContextMenuItem extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureC
super(props);
this.onClick = this.onClick.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
+ this.focusFirst = this.focusFirst.bind(this);
}
onClick() {
this.props.hideContext();
this.props.option.onClick();
+ } // Focus the first menu item if the menu was accessed via the keyboard.
+
+
+ focusFirst(button) {
+ if (this.props.keyboardAccess && button) {
+ button.focus();
+ }
} // This selects the correct node based on the key pressed
@@ -3954,7 +3974,8 @@ class ContextMenuItem extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureC
className: option.disabled ? "disabled" : "",
tabIndex: "0",
onClick: this.onClick,
- onKeyDown: this.onKeyDown
+ onKeyDown: this.onKeyDown,
+ ref: option.first ? this.focusFirst : null
}, option.icon && react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", {
className: `icon icon-spacer icon-${option.icon}`
}), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", {
@@ -3969,6 +3990,91 @@ class ContextMenuItem extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureC
/* 29 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ContextMenuButton", function() { return ContextMenuButton; });
+/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9);
+/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+class ContextMenuButton extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ showContextMenu: false,
+ contextMenuKeyboard: false
+ };
+ this.onClick = this.onClick.bind(this);
+ this.onKeyDown = this.onKeyDown.bind(this);
+ this.onUpdate = this.onUpdate.bind(this);
+ }
+
+ openContextMenu(isKeyBoard, event) {
+ if (this.props.onUpdate) {
+ this.props.onUpdate(true);
+ }
+
+ this.setState({
+ showContextMenu: true,
+ contextMenuKeyboard: isKeyBoard
+ });
+ }
+
+ onClick(event) {
+ event.preventDefault();
+ this.openContextMenu(false, event);
+ }
+
+ onKeyDown(event) {
+ if (event.key === "Enter") {
+ event.preventDefault();
+ this.openContextMenu(true, event);
+ }
+ }
+
+ onUpdate(showContextMenu) {
+ if (this.props.onUpdate) {
+ this.props.onUpdate(showContextMenu);
+ }
+
+ this.setState({
+ showContextMenu
+ });
+ }
+
+ render() {
+ const {
+ tooltipArgs,
+ tooltip,
+ children,
+ refFunction
+ } = this.props;
+ const {
+ showContextMenu,
+ contextMenuKeyboard
+ } = this.state;
+ return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_0___default.a.Fragment, null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", {
+ "aria-haspopup": "true",
+ "data-l10n-id": tooltip,
+ "data-l10n-args": tooltipArgs ? JSON.stringify(tooltipArgs) : null,
+ className: "context-menu-button icon",
+ onKeyDown: this.onKeyDown,
+ onClick: this.onClick,
+ ref: refFunction
+ }), showContextMenu ? react__WEBPACK_IMPORTED_MODULE_0___default.a.cloneElement(children, {
+ keyboardAccess: contextMenuKeyboard,
+ onUpdate: this.onUpdate
+ }) : null);
+ }
+
+}
+
+/***/ }),
+/* 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__, "INTERSECTION_RATIO", function() { return INTERSECTION_RATIO; });
@@ -4193,19 +4299,20 @@ ImpressionStats.defaultProps = {
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
/***/ }),
-/* 30 */
+/* 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__, "CollapsibleSection", function() { return CollapsibleSection; });
/* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(31);
-/* harmony import */ var content_src_components_FluentOrText_FluentOrText__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(33);
+/* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(32);
+/* harmony import */ var content_src_components_FluentOrText_FluentOrText__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(34);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
/* 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__(34);
-/* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(35);
+/* harmony import */ var content_src_components_SectionMenu_SectionMenu__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(35);
+/* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(36);
+/* harmony import */ var content_src_components_ContextMenu_ContextMenuButton__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(29);
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -4215,6 +4322,7 @@ __webpack_require__.r(__webpack_exports__);
+
const VISIBLE = "visible";
const VISIBILITY_CHANGE_EVENT = "visibilitychange";
class CollapsibleSection extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent {
@@ -4225,7 +4333,6 @@ class CollapsibleSection extends react__WEBPACK_IMPORTED_MODULE_3___default.a.Pu
this.onKeyPress = this.onKeyPress.bind(this);
this.onTransitionEnd = this.onTransitionEnd.bind(this);
this.enableOrDisableAnimation = this.enableOrDisableAnimation.bind(this);
- this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuButtonMouseEnter = this.onMenuButtonMouseEnter.bind(this);
this.onMenuButtonMouseLeave = this.onMenuButtonMouseLeave.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
@@ -4355,13 +4462,6 @@ class CollapsibleSection extends react__WEBPACK_IMPORTED_MODULE_3___default.a.Pu
});
}
- onMenuButtonClick(event) {
- event.preventDefault();
- this.setState({
- showContextMenu: true
- });
- }
-
onMenuButtonMouseEnter() {
this.setState({
menuButtonHover: true
@@ -4452,26 +4552,23 @@ class CollapsibleSection extends react__WEBPACK_IMPORTED_MODULE_3___default.a.Pu
message: learnMore.link.message
}, react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("a", {
href: learnMore.link.href
- })))))), react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("div", null, react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("button", {
- "aria-haspopup": "true",
- className: "context-menu-button icon",
- "data-l10n-id": "newtab-menu-section-tooltip",
- onClick: this.onMenuButtonClick,
- ref: this.setContextMenuButtonRef
- }), showContextMenu && react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(content_src_components_SectionMenu_SectionMenu__WEBPACK_IMPORTED_MODULE_4__["SectionMenu"], {
+ })))))), react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("div", null, react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(content_src_components_ContextMenu_ContextMenuButton__WEBPACK_IMPORTED_MODULE_6__["ContextMenuButton"], {
+ tooltip: "newtab-menu-section-tooltip",
+ onUpdate: this.onMenuUpdate,
+ refFunction: this.setContextMenuButtonRef
+ }, react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(content_src_components_SectionMenu_SectionMenu__WEBPACK_IMPORTED_MODULE_4__["SectionMenu"], {
id: id,
extraOptions: extraMenuOptions,
eventSource: eventSource,
showPrefName: showPrefName,
privacyNoticeURL: privacyNoticeURL,
collapsed: collapsed,
- onUpdate: this.onMenuUpdate,
isFixed: isFixed,
isFirst: isFirst,
isLast: isLast,
dispatch: dispatch,
isWebExtension: isWebExtension
- }))), react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_1__["ErrorBoundary"], {
+ })))), react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_1__["ErrorBoundary"], {
className: "section-body-fallback"
}, react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("div", {
className: `section-body${isAnimating ? " animating" : ""}`,
@@ -4495,14 +4592,14 @@ CollapsibleSection.defaultProps = {
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
/***/ }),
-/* 31 */
+/* 32 */
/***/ (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 content_src_components_A11yLinkButton_A11yLinkButton__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(32);
+/* harmony import */ var content_src_components_A11yLinkButton_A11yLinkButton__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(33);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -4582,7 +4679,7 @@ ErrorBoundary.defaultProps = {
};
/***/ }),
-/* 32 */
+/* 33 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -4612,7 +4709,7 @@ function A11yLinkButton(props) {
}
/***/ }),
-/* 33 */
+/* 34 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -4658,7 +4755,7 @@ class FluentOrText extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComp
}
/***/ }),
-/* 34 */
+/* 35 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -4669,7 +4766,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_2__);
-/* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(35);
+/* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(36);
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -4680,6 +4777,21 @@ __webpack_require__.r(__webpack_exports__);
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_2___default.a.PureComponent {
+ handleAddWhileCollapsed() {
+ const {
+ action,
+ userEvent
+ } = content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_3__["SectionMenuOptions"].ExpandSection(this.props);
+ this.props.dispatch(action);
+
+ if (userEvent) {
+ this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].UserEvent({
+ event: userEvent,
+ source: this.props.source
+ }));
+ }
+ }
+
getOptions() {
const {
props
@@ -4710,6 +4822,12 @@ class _SectionMenu extends react__WEBPACK_IMPORTED_MODULE_2___default.a.PureComp
if (!type && id) {
option.onClick = () => {
+ const hasAddEvent = userEvent === "MENU_ADD_TOPSITE" || userEvent === "MENU_ADD_SEARCH";
+
+ if (props.collapsed && hasAddEvent) {
+ this.handleAddWhileCollapsed();
+ }
+
props.dispatch(action);
if (userEvent) {
@@ -4734,7 +4852,8 @@ class _SectionMenu extends react__WEBPACK_IMPORTED_MODULE_2___default.a.PureComp
render() {
return react__WEBPACK_IMPORTED_MODULE_2___default.a.createElement(content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_1__["ContextMenu"], {
onUpdate: this.props.onUpdate,
- options: this.getOptions()
+ options: this.getOptions(),
+ keyboardAccess: this.props.keyboardAccess
});
}
@@ -4742,7 +4861,7 @@ class _SectionMenu extends react__WEBPACK_IMPORTED_MODULE_2___default.a.PureComp
const SectionMenu = _SectionMenu;
/***/ }),
-/* 35 */
+/* 36 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -4872,7 +4991,7 @@ const SectionMenuOptions = {
};
/***/ }),
-/* 36 */
+/* 37 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -4882,18 +5001,18 @@ __webpack_require__.r(__webpack_exports__);
/* 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__(54);
-/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(30);
-/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(38);
-/* harmony import */ var content_src_components_FluentOrText_FluentOrText__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(33);
+/* harmony import */ var content_src_components_Card_Card__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55);
+/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(31);
+/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(39);
+/* harmony import */ var content_src_components_FluentOrText_FluentOrText__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(34);
/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(24);
/* 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__(40);
-/* harmony import */ var content_src_components_PocketLoggedInCta_PocketLoggedInCta__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(41);
+/* harmony import */ var content_src_components_MoreRecommendations_MoreRecommendations__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(41);
+/* harmony import */ var content_src_components_PocketLoggedInCta_PocketLoggedInCta__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(42);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(9);
/* 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__(42);
-/* harmony import */ var content_src_components_TopSites_TopSites__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(43);
+/* harmony import */ var content_src_components_Topics_Topics__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(43);
+/* harmony import */ var content_src_components_TopSites_TopSites__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(44);
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); }
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -5243,7 +5362,7 @@ const Sections = Object(react_redux__WEBPACK_IMPORTED_MODULE_5__["connect"])(sta
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
/***/ }),
-/* 37 */
+/* 38 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -5312,14 +5431,14 @@ const ScreenshotUtils = {
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
/***/ }),
-/* 38 */
+/* 39 */
/***/ (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__(39);
+/* harmony import */ var common_PerfService_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(40);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_2__);
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -5492,7 +5611,7 @@ class ComponentPerfTimer extends react__WEBPACK_IMPORTED_MODULE_2___default.a.Co
}
/***/ }),
-/* 39 */
+/* 40 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -5624,7 +5743,7 @@ _PerfService.prototype = {
var perfService = new _PerfService();
/***/ }),
-/* 40 */
+/* 41 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -5656,7 +5775,7 @@ class MoreRecommendations extends react__WEBPACK_IMPORTED_MODULE_0___default.a.P
}
/***/ }),
-/* 41 */
+/* 42 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -5699,7 +5818,7 @@ const PocketLoggedInCta = Object(react_redux__WEBPACK_IMPORTED_MODULE_0__["conne
}))(_PocketLoggedInCta);
/***/ }),
-/* 42 */
+/* 43 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -5744,7 +5863,7 @@ class Topics extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent
}
/***/ }),
-/* 43 */
+/* 44 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -5752,18 +5871,18 @@ __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__(44);
-/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(30);
-/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(38);
+/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(45);
+/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(31);
+/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(39);
/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(24);
/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var _asrouter_components_ModalOverlay_ModalOverlay__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(13);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(9);
/* 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__(45);
-/* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(53);
-/* harmony import */ var _TopSiteForm__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(55);
-/* harmony import */ var _TopSite__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(46);
+/* harmony import */ var _SearchShortcutsForm__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(46);
+/* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(54);
+/* harmony import */ var _TopSiteForm__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(56);
+/* harmony import */ var _TopSite__WEBPACK_IMPORTED_MODULE_10__ = __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); }
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -5970,7 +6089,7 @@ const TopSites = Object(react_redux__WEBPACK_IMPORTED_MODULE_4__["connect"])(sta
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
/***/ }),
-/* 44 */
+/* 45 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -5993,7 +6112,7 @@ const MIN_RICH_FAVICON_SIZE = 96; // minimum size necessary to show any icon in
const MIN_CORNER_FAVICON_SIZE = 16;
/***/ }),
-/* 45 */
+/* 46 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -6003,7 +6122,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
-/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(44);
+/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(45);
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -6184,7 +6303,7 @@ class SearchShortcutsForm extends react__WEBPACK_IMPORTED_MODULE_1___default.a.P
}
/***/ }),
-/* 46 */
+/* 47 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -6194,12 +6313,13 @@ __webpack_require__.r(__webpack_exports__);
/* 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 import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(44);
-/* harmony import */ var content_src_components_LinkMenu_LinkMenu__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(56);
+/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(45);
+/* harmony import */ var content_src_components_LinkMenu_LinkMenu__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(57);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_3__);
-/* harmony import */ var content_src_lib_screenshot_utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(37);
-/* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(53);
+/* harmony import */ var content_src_lib_screenshot_utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(38);
+/* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(54);
+/* harmony import */ var content_src_components_ContextMenu_ContextMenuButton__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(29);
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); }
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -6211,6 +6331,7 @@ function _extends() { _extends = Object.assign || function (target) { for (var i
+
class TopSiteLink extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent {
constructor(props) {
super(props);
@@ -6468,7 +6589,6 @@ class TopSite extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent
showContextMenu: false
};
this.onLinkClick = this.onLinkClick.bind(this);
- this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
}
/**
@@ -6540,18 +6660,12 @@ class TopSite extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent
}
}
- onMenuButtonClick(event) {
- event.preventDefault();
- this.props.onActivate(this.props.index);
- this.setState({
- showContextMenu: true
- });
- }
-
- onMenuUpdate(showContextMenu) {
- this.setState({
- showContextMenu
- });
+ onMenuUpdate(isOpen) {
+ if (isOpen) {
+ this.props.onActivate(this.props.index);
+ } else {
+ this.props.onActivate();
+ }
}
render() {
@@ -6561,22 +6675,20 @@ class TopSite extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent
const {
link
} = props;
- const isContextMenuOpen = this.state.showContextMenu && props.activeIndex === props.index;
+ const isContextMenuOpen = props.activeIndex === props.index;
const title = link.label || link.hostname;
return react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(TopSiteLink, _extends({}, props, {
onClick: this.onLinkClick,
onDragEvent: this.props.onDragEvent,
className: `${props.className || ""}${isContextMenuOpen ? " active" : ""}`,
title: title
- }), react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("div", null, react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("button", {
- "aria-haspopup": "true",
- className: "context-menu-button icon",
- "data-l10n-id": "newtab-menu-content-tooltip",
- "data-l10n-args": JSON.stringify({
+ }), react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("div", null, react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(content_src_components_ContextMenu_ContextMenuButton__WEBPACK_IMPORTED_MODULE_6__["ContextMenuButton"], {
+ tooltip: "newtab-menu-content-tooltip",
+ tooltipArgs: {
title
- }),
- onClick: this.onMenuButtonClick
- }), isContextMenuOpen && react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(content_src_components_LinkMenu_LinkMenu__WEBPACK_IMPORTED_MODULE_2__["LinkMenu"], {
+ },
+ onUpdate: this.onMenuUpdate
+ }, react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(content_src_components_LinkMenu_LinkMenu__WEBPACK_IMPORTED_MODULE_2__["LinkMenu"], {
dispatch: props.dispatch,
index: props.index,
onUpdate: this.onMenuUpdate,
@@ -6584,7 +6696,7 @@ class TopSite extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent
site: link,
siteInfo: this._getTelemetryInfo(),
source: _TopSitesConstants__WEBPACK_IMPORTED_MODULE_1__["TOP_SITES_SOURCE"]
- })));
+ }))));
}
}
@@ -6834,7 +6946,7 @@ class TopSiteList extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureCompo
}
/***/ }),
-/* 47 */
+/* 48 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -7022,14 +7134,14 @@ class _Search extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent
const Search = Object(react_redux__WEBPACK_IMPORTED_MODULE_1__["connect"])()(_Search);
/***/ }),
-/* 48 */
+/* 49 */
/***/ (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__(39);
+/* harmony import */ var common_PerfService_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(40);
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -7104,7 +7216,7 @@ class DetectUserSessionStart {
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
/***/ }),
-/* 49 */
+/* 50 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -7247,7 +7359,10 @@ DSImage_DSImage.defaultProps = {
};
// EXTERNAL MODULE: ./content-src/components/LinkMenu/LinkMenu.jsx + 1 modules
-var LinkMenu = __webpack_require__(56);
+var LinkMenu = __webpack_require__(57);
+
+// EXTERNAL MODULE: ./content-src/components/ContextMenu/ContextMenuButton.jsx
+var ContextMenuButton = __webpack_require__(29);
// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -7255,38 +7370,22 @@ var LinkMenu = __webpack_require__(56);
* You can obtain one at http://mozilla.org/MPL/2.0/. */
+
class DSLinkMenu_DSLinkMenu extends external_React_default.a.PureComponent {
constructor(props) {
super(props);
- this.state = {
- activeCard: null,
- showContextMenu: false
- };
this.windowObj = this.props.windowObj || window; // Added to support unit tests
- this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
this.onMenuShow = this.onMenuShow.bind(this);
this.contextMenuButtonRef = external_React_default.a.createRef();
}
- onMenuButtonClick(event) {
- event.preventDefault();
- this.setState({
- activeCard: this.props.index,
- showContextMenu: true
- });
- }
-
onMenuUpdate(showContextMenu) {
if (!showContextMenu) {
const dsLinkMenuHostDiv = this.contextMenuButtonRef.current.parentElement;
dsLinkMenuHostDiv.parentElement.classList.remove("active", "last-item");
}
-
- this.setState({
- showContextMenu
- });
}
nextAnimationFrame() {
@@ -7310,24 +7409,20 @@ class DSLinkMenu_DSLinkMenu extends external_React_default.a.PureComponent {
index,
dispatch
} = this.props;
- const isContextMenuOpen = this.state.showContextMenu && this.state.activeCard === index;
const TOP_STORIES_CONTEXT_MENU_OPTIONS = ["CheckBookmarkOrArchive", "CheckSavedToPocket", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
const type = this.props.type || "DISCOVERY_STREAM";
const title = this.props.title || this.props.source;
- return external_React_default.a.createElement("div", null, external_React_default.a.createElement("button", {
- ref: this.contextMenuButtonRef,
- "aria-haspopup": "true",
- className: "context-menu-button icon",
- "data-l10n-id": "newtab-menu-content-tooltip",
- "data-l10n-args": JSON.stringify({
+ return external_React_default.a.createElement("div", null, external_React_default.a.createElement(ContextMenuButton["ContextMenuButton"], {
+ refFunction: this.contextMenuButtonRef,
+ tooltip: "newtab-menu-content-tooltip",
+ tooltipArgs: {
title
- }),
- onClick: this.onMenuButtonClick
- }), isContextMenuOpen && external_React_default.a.createElement(LinkMenu["LinkMenu"], {
+ },
+ onUpdate: this.onMenuUpdate
+ }, external_React_default.a.createElement(LinkMenu["LinkMenu"], {
dispatch: dispatch,
index: index,
source: type.toUpperCase(),
- onUpdate: this.onMenuUpdate,
onShow: this.onMenuShow,
options: TOP_STORIES_CONTEXT_MENU_OPTIONS,
shouldSendImpressionStats: true,
@@ -7341,12 +7436,12 @@ class DSLinkMenu_DSLinkMenu extends external_React_default.a.PureComponent {
shim: this.props.shim,
bookmarkGuid: this.props.bookmarkGuid
}
- }));
+ })));
}
}
// EXTERNAL MODULE: ./content-src/components/DiscoveryStreamImpressionStats/ImpressionStats.jsx
-var ImpressionStats = __webpack_require__(29);
+var ImpressionStats = __webpack_require__(30);
// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -7691,7 +7786,7 @@ CardGrid_CardGrid.defaultProps = {
};
// EXTERNAL MODULE: ./content-src/components/CollapsibleSection/CollapsibleSection.jsx
-var CollapsibleSection = __webpack_require__(30);
+var CollapsibleSection = __webpack_require__(31);
// EXTERNAL MODULE: external "ReactRedux"
var external_ReactRedux_ = __webpack_require__(24);
@@ -8064,7 +8159,7 @@ Hero_Hero.defaultProps = {
};
// EXTERNAL MODULE: ./content-src/components/Sections/Sections.jsx
-var Sections = __webpack_require__(36);
+var Sections = __webpack_require__(37);
// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/Highlights/Highlights.jsx
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); }
@@ -8209,8 +8304,11 @@ const selectLayoutRender = (state, prefs, rickRollCache) => {
if (rickRoll <= spocsConfig.probability) {
spocIndex++;
- recommendations.splice(position.index, 0, spoc);
- chosenSpocs.add(spoc);
+
+ if (!spocs.blocked.includes(spoc.url)) {
+ recommendations.splice(position.index, 0, spoc);
+ chosenSpocs.add(spoc);
+ }
} else {
unchosenSpocs.add(spoc);
}
@@ -8372,7 +8470,7 @@ const selectLayoutRender = (state, prefs, rickRollCache) => {
};
};
// EXTERNAL MODULE: ./content-src/components/TopSites/TopSites.jsx
-var TopSites = __webpack_require__(43);
+var TopSites = __webpack_require__(44);
// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/TopSites/TopSites.jsx
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -8701,7 +8799,7 @@ const DiscoveryStreamBase = Object(external_ReactRedux_["connect"])(state => ({
}))(DiscoveryStreamBase_DiscoveryStreamBase);
/***/ }),
-/* 50 */
+/* 51 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -9498,7 +9596,7 @@ localized_Localized.propTypes = {
/***/ }),
-/* 51 */
+/* 52 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -10586,7 +10684,7 @@ const SnippetsTemplates = {
};
/***/ }),
-/* 52 */
+/* 53 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -12006,7 +12104,7 @@ function generateBundles(content) {
}
/***/ }),
-/* 53 */
+/* 54 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -12131,7 +12229,8 @@ const INITIAL_STATE = {
data: {},
// {spocs: []}
loaded: false,
- frequency_caps: []
+ frequency_caps: [],
+ blocked: []
}
},
Search: {
@@ -12759,6 +12858,13 @@ function DiscoveryStream(prevState = INITIAL_STATE.DiscoveryStream, action) {
return prevState;
+ case Actions["actionTypes"].DISCOVERY_STREAM_SPOC_BLOCKED:
+ return { ...prevState,
+ spocs: { ...prevState.spocs,
+ blocked: [...prevState.spocs.blocked, action.data.url]
+ }
+ };
+
case Actions["actionTypes"].DISCOVERY_STREAM_LINK_BLOCKED:
return isNotReady() ? prevState : nextState(items => items.filter(item => item.url !== action.data.url));
@@ -12857,7 +12963,7 @@ var reducers = {
};
/***/ }),
-/* 54 */
+/* 55 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -12895,15 +13001,18 @@ const cardContextTypes = {
// EXTERNAL MODULE: external "ReactRedux"
var external_ReactRedux_ = __webpack_require__(24);
+// EXTERNAL MODULE: ./content-src/components/ContextMenu/ContextMenuButton.jsx
+var ContextMenuButton = __webpack_require__(29);
+
// EXTERNAL MODULE: ./content-src/components/LinkMenu/LinkMenu.jsx + 1 modules
-var LinkMenu = __webpack_require__(56);
+var LinkMenu = __webpack_require__(57);
// EXTERNAL MODULE: external "React"
var external_React_ = __webpack_require__(9);
var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
// EXTERNAL MODULE: ./content-src/lib/screenshot-utils.js
-var screenshot_utils = __webpack_require__(37);
+var screenshot_utils = __webpack_require__(38);
// CONCATENATED MODULE: ./content-src/components/Card/Card.jsx
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_Card", function() { return Card_Card; });
@@ -12917,6 +13026,7 @@ var screenshot_utils = __webpack_require__(37);
+
// Keep track of pending image loads to only request once
const gImageLoading = new Map();
@@ -12936,11 +13046,9 @@ class Card_Card extends external_React_default.a.PureComponent {
this.state = {
activeCard: null,
imageLoaded: false,
- showContextMenu: false,
cardImage: null
};
- this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
- this.onMenuUpdate = this.onMenuUpdate.bind(this);
+ this.onMenuButtonUpdate = this.onMenuButtonUpdate.bind(this);
this.onLinkClick = this.onLinkClick.bind(this);
}
/**
@@ -13021,12 +13129,16 @@ class Card_Card extends external_React_default.a.PureComponent {
return nextState;
}
- onMenuButtonClick(event) {
- event.preventDefault();
- this.setState({
- activeCard: this.props.index,
- showContextMenu: true
- });
+ onMenuButtonUpdate(isOpen) {
+ if (isOpen) {
+ this.setState({
+ activeCard: this.props.index
+ });
+ } else {
+ this.setState({
+ activeCard: null
+ });
+ }
}
/**
* Report to telemetry additional information about the item.
@@ -13102,12 +13214,6 @@ class Card_Card extends external_React_default.a.PureComponent {
}
}
- onMenuUpdate(showContextMenu) {
- this.setState({
- showContextMenu
- });
- }
-
componentDidMount() {
this.maybeLoadImage();
}
@@ -13156,7 +13262,7 @@ class Card_Card extends external_React_default.a.PureComponent {
props
} = this;
const title = link.title || link.hostname;
- const isContextMenuOpen = this.state.showContextMenu && this.state.activeCard === index; // Display "now" as "trending" until we have new strings #3402
+ const isContextMenuOpen = this.state.activeCard === index; // Display "now" as "trending" until we have new strings #3402
const {
icon,
@@ -13210,24 +13316,21 @@ class Card_Card extends external_React_default.a.PureComponent {
"data-l10n-id": fluentID
}), link.context && external_React_default.a.createElement("div", {
className: "card-context-label"
- }, link.context))))), !props.placeholder && external_React_default.a.createElement("button", {
- "aria-haspopup": "true",
- "data-l10n-id": "newtab-menu-content-tooltip",
- "data-l10n-args": JSON.stringify({
+ }, link.context))))), !props.placeholder && external_React_default.a.createElement(ContextMenuButton["ContextMenuButton"], {
+ tooltip: "newtab-menu-content-tooltip",
+ tooltipArgs: {
title
- }),
- className: "context-menu-button icon",
- onClick: this.onMenuButtonClick
- }), isContextMenuOpen && external_React_default.a.createElement(LinkMenu["LinkMenu"], {
+ },
+ onUpdate: this.onMenuButtonUpdate
+ }, external_React_default.a.createElement(LinkMenu["LinkMenu"], {
dispatch: dispatch,
index: index,
source: eventSource,
- onUpdate: this.onMenuUpdate,
options: link.contextMenuOptions || contextMenuOptions,
site: link,
siteInfo: this._getTelemetryInfo(),
shouldSendImpressionStats: shouldSendImpressionStats
- }));
+ })));
}
}
@@ -13243,7 +13346,7 @@ const PlaceholderCard = props => external_React_default.a.createElement(Card, {
});
/***/ }),
-/* 55 */
+/* 56 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -13253,14 +13356,14 @@ __webpack_require__.r(__webpack_exports__);
var Actions = __webpack_require__(2);
// EXTERNAL MODULE: ./content-src/components/A11yLinkButton/A11yLinkButton.jsx
-var A11yLinkButton = __webpack_require__(32);
+var A11yLinkButton = __webpack_require__(33);
// EXTERNAL MODULE: external "React"
var external_React_ = __webpack_require__(9);
var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
// EXTERNAL MODULE: ./content-src/components/TopSites/TopSitesConstants.js
-var TopSitesConstants = __webpack_require__(44);
+var TopSitesConstants = __webpack_require__(45);
// CONCATENATED MODULE: ./content-src/components/TopSites/TopSiteFormInput.jsx
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -13375,7 +13478,7 @@ TopSiteFormInput_TopSiteFormInput.defaultProps = {
validationError: false
};
// EXTERNAL MODULE: ./content-src/components/TopSites/TopSite.jsx
-var TopSite = __webpack_require__(46);
+var TopSite = __webpack_require__(47);
// CONCATENATED MODULE: ./content-src/components/TopSites/TopSiteForm.jsx
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSiteForm", function() { return TopSiteForm_TopSiteForm; });
@@ -13672,7 +13775,7 @@ TopSiteForm_TopSiteForm.defaultProps = {
};
/***/ }),
-/* 56 */
+/* 57 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
@@ -14038,7 +14141,8 @@ class LinkMenu_LinkMenu extends external_React_default.a.PureComponent {
return external_React_default.a.createElement(ContextMenu["ContextMenu"], {
onUpdate: this.props.onUpdate,
onShow: this.props.onShow,
- options: this.getOptions()
+ options: this.getOptions(),
+ keyboardAccess: this.props.keyboardAccess
});
}
diff --git a/browser/components/newtab/docs/v2-system-addon/data_events.md b/browser/components/newtab/docs/v2-system-addon/data_events.md
index 6132d7ca19f5..d98e389ead6a 100644
--- a/browser/components/newtab/docs/v2-system-addon/data_events.md
+++ b/browser/components/newtab/docs/v2-system-addon/data_events.md
@@ -1068,3 +1068,21 @@ This reports an enrollment ping when a user gets enrolled in a Trailhead experim
}
}
```
+
+## Feature Callouts interaction pings
+
+This reports when a user has seen or clicked a badge/notification in the browser toolbar in a non-PBM window
+
+```
+{
+ "locale": "en-US",
+ "client_id": "9da773d8-4356-f54f-b7cf-6134726bcf3d",
+ "version": "70.0a1",
+ "release_channel": "default",
+ "addon_version": "20190712095934",
+ "action": "cfr_user_event",
+ "source": "CFR",
+ "message_id": "FXA_ACCOUNTS_BADGE",
+ "event": ["CLICK" | "IMPRESSION"],
+}
+```
diff --git a/browser/components/newtab/lib/ASRouter.jsm b/browser/components/newtab/lib/ASRouter.jsm
index 6100b59ab55d..d0b510d47f84 100644
--- a/browser/components/newtab/lib/ASRouter.jsm
+++ b/browser/components/newtab/lib/ASRouter.jsm
@@ -728,8 +728,9 @@ class _ASRouter {
handleMessageRequest: this.handleMessageRequest,
addImpression: this.addImpression,
blockMessageById: this.blockMessageById,
+ dispatch: this.dispatch,
});
- ToolbarPanelHub.init({
+ ToolbarPanelHub.init(this.waitForInitialized, {
getMessages: this.handleMessageRequest,
});
@@ -1889,6 +1890,7 @@ class _ASRouter {
await this.addImpression(action.data);
break;
case "DOORHANGER_TELEMETRY":
+ case "TOOLBAR_BADGE_TELEMETRY":
if (this.dispatchToAS) {
this.dispatchToAS(ac.ASRouterUserEvent(action.data));
}
diff --git a/browser/components/newtab/lib/DiscoveryStreamFeed.jsm b/browser/components/newtab/lib/DiscoveryStreamFeed.jsm
index a2984d9b3cb4..78dc9b4ca162 100644
--- a/browser/components/newtab/lib/DiscoveryStreamFeed.jsm
+++ b/browser/components/newtab/lib/DiscoveryStreamFeed.jsm
@@ -1182,6 +1182,21 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
const filtered = spocsList.filter(s => s.url === action.data.url);
if (filtered.length) {
this._sendSpocsFill({ blocked_by_user: filtered }, false);
+
+ // If we're blocking a spoc, we want a slightly different treatment for open tabs.
+ this.store.dispatch(
+ ac.AlsoToPreloaded({
+ type: at.DISCOVERY_STREAM_LINK_BLOCKED,
+ data: action.data,
+ })
+ );
+ this.store.dispatch(
+ ac.BroadcastToContent({
+ type: at.DISCOVERY_STREAM_SPOC_BLOCKED,
+ data: action.data,
+ })
+ );
+ return;
}
}
this.store.dispatch(
diff --git a/browser/components/newtab/lib/ToolbarBadgeHub.jsm b/browser/components/newtab/lib/ToolbarBadgeHub.jsm
index f03ac590d59f..c682b181328b 100644
--- a/browser/components/newtab/lib/ToolbarBadgeHub.jsm
+++ b/browser/components/newtab/lib/ToolbarBadgeHub.jsm
@@ -13,6 +13,11 @@ ChromeUtils.defineModuleGetter(
"ToolbarPanelHub",
"resource://activity-stream/lib/ToolbarPanelHub.jsm"
);
+ChromeUtils.defineModuleGetter(
+ this,
+ "Services",
+ "resource://gre/modules/Services.jsm"
+);
ChromeUtils.defineModuleGetter(
this,
"setTimeout",
@@ -23,6 +28,16 @@ ChromeUtils.defineModuleGetter(
"clearTimeout",
"resource://gre/modules/Timer.jsm"
);
+ChromeUtils.defineModuleGetter(
+ this,
+ "Services",
+ "resource://gre/modules/Services.jsm"
+);
+ChromeUtils.defineModuleGetter(
+ this,
+ "PrivateBrowsingUtils",
+ "resource://gre/modules/PrivateBrowsingUtils.jsm"
+);
const notificationsByWindow = new WeakMap();
@@ -31,33 +46,51 @@ class _ToolbarBadgeHub {
this.id = "toolbar-badge-hub";
this.template = "toolbar_badge";
this.state = null;
+ this.prefs = {
+ WHATSNEW_TOOLBAR_PANEL: "browser.messaging-system.whatsNewPanel.enabled",
+ };
this.removeAllNotifications = this.removeAllNotifications.bind(this);
this.removeToolbarNotification = this.removeToolbarNotification.bind(this);
this.addToolbarNotification = this.addToolbarNotification.bind(this);
this.registerBadgeToAllWindows = this.registerBadgeToAllWindows.bind(this);
+ this._sendTelemetry = this._sendTelemetry.bind(this);
+ this.sendUserEventTelemetry = this.sendUserEventTelemetry.bind(this);
this._handleMessageRequest = null;
this._addImpression = null;
this._blockMessageById = null;
+ this._dispatch = null;
}
async init(
waitForInitialized,
- { handleMessageRequest, addImpression, blockMessageById }
+ { handleMessageRequest, addImpression, blockMessageById, dispatch }
) {
this._handleMessageRequest = handleMessageRequest;
this._blockMessageById = blockMessageById;
this._addImpression = addImpression;
+ this._dispatch = dispatch;
this.state = {};
// Need to wait for ASRouter to initialize before trying to fetch messages
await waitForInitialized;
this.messageRequest("toolbarBadgeUpdate");
+ // Listen for pref changes that could trigger new badges
+ Services.prefs.addObserver(this.prefs.WHATSNEW_TOOLBAR_PANEL, this);
+ }
+
+ observe(aSubject, aTopic, aPrefName) {
+ switch (aPrefName) {
+ case this.prefs.WHATSNEW_TOOLBAR_PANEL:
+ this.messageRequest("toolbarBadgeUpdate");
+ break;
+ }
}
executeAction({ id }) {
switch (id) {
case "show-whatsnew-button":
ToolbarPanelHub.enableToolbarButton();
+ ToolbarPanelHub.enableAppmenuButton();
break;
}
}
@@ -91,6 +124,11 @@ class _ToolbarBadgeHub {
this.removeAllNotifications
);
event.target.removeEventListener("click", this.removeAllNotifications);
+ // If we have an event it means the user interacted with the badge
+ // we should send telemetry
+ if (this.state.notification) {
+ this.sendUserEventTelemetry("CLICK", this.state.notification);
+ }
}
// Will call uninit on every window
EveryWindow.unregisterCallback(this.id);
@@ -146,6 +184,8 @@ class _ToolbarBadgeHub {
registerBadgeToAllWindows(message) {
// Impression should be added when the badge becomes visible
this._addImpression(message);
+ // Send a telemetry ping when adding the notification badge
+ this.sendUserEventTelemetry("IMPRESSION", message);
EveryWindow.registerCallback(
this.id,
@@ -191,9 +231,34 @@ class _ToolbarBadgeHub {
}
}
+ _sendTelemetry(ping) {
+ this._dispatch({
+ type: "TOOLBAR_BADGE_TELEMETRY",
+ data: { action: "cfr_user_event", source: "CFR", ...ping },
+ });
+ }
+
+ sendUserEventTelemetry(event, message) {
+ const win = Services.wm.getMostRecentWindow("navigator:browser");
+ // Only send pings for non private browsing windows
+ if (
+ win &&
+ !PrivateBrowsingUtils.isBrowserPrivate(
+ win.ownerGlobal.gBrowser.selectedBrowser
+ )
+ ) {
+ this._sendTelemetry({
+ message_id: message.id,
+ bucket_id: message.id,
+ event,
+ });
+ }
+ }
+
uninit() {
this._clearBadgeTimeout();
this.state = null;
+ Services.prefs.removeObserver(this.prefs.WHATSNEW_TOOLBAR_PANEL, this);
}
}
diff --git a/browser/components/newtab/lib/ToolbarPanelHub.jsm b/browser/components/newtab/lib/ToolbarPanelHub.jsm
index d599489a8839..3d3d7ae783a1 100644
--- a/browser/components/newtab/lib/ToolbarPanelHub.jsm
+++ b/browser/components/newtab/lib/ToolbarPanelHub.jsm
@@ -30,16 +30,42 @@ class _ToolbarPanelHub {
this._hideToolbarButton = this._hideToolbarButton.bind(this);
}
- init({ getMessages }) {
+ async init(waitForInitialized, { getMessages }) {
this._getMessages = getMessages;
+ // Wait for ASRouter messages to become available in order to know
+ // if we can show the What's New panel
+ await waitForInitialized;
if (this.whatsNewPanelEnabled) {
+ // Enable the application menu button so that the user can access
+ // the panel outside of the toolbar button
this.enableAppmenuButton();
}
+ // Listen for pref changes that could turn off the feature
+ Services.prefs.addObserver(WHATSNEW_ENABLED_PREF, this);
}
uninit() {
EveryWindow.unregisterCallback(TOOLBAR_BUTTON_ID);
EveryWindow.unregisterCallback(APPMENU_BUTTON_ID);
+ Services.prefs.removeObserver(WHATSNEW_ENABLED_PREF, this);
+ }
+
+ observe(aSubject, aTopic, aPrefName) {
+ switch (aPrefName) {
+ case WHATSNEW_ENABLED_PREF:
+ if (!this.whatsNewPanelEnabled) {
+ this.uninit();
+ }
+ break;
+ }
+ }
+
+ get messages() {
+ return this._getMessages({
+ template: "whatsnew_panel_message",
+ triggerId: "whatsNewPanelOpened",
+ returnAll: true,
+ });
}
get whatsNewPanelEnabled() {
@@ -51,21 +77,25 @@ class _ToolbarPanelHub {
}
// Turns on the Appmenu (hamburger menu) button for all open windows and future windows.
- enableAppmenuButton() {
- EveryWindow.registerCallback(
- APPMENU_BUTTON_ID,
- this._showAppmenuButton,
- this._hideAppmenuButton
- );
+ async enableAppmenuButton() {
+ if ((await this.messages).length) {
+ EveryWindow.registerCallback(
+ APPMENU_BUTTON_ID,
+ this._showAppmenuButton,
+ this._hideAppmenuButton
+ );
+ }
}
// Turns on the Toolbar button for all open windows and future windows.
- enableToolbarButton() {
- EveryWindow.registerCallback(
- TOOLBAR_BUTTON_ID,
- this._showToolbarButton,
- this._hideToolbarButton
- );
+ async enableToolbarButton() {
+ if ((await this.messages).length) {
+ EveryWindow.registerCallback(
+ TOOLBAR_BUTTON_ID,
+ this._showToolbarButton,
+ this._hideToolbarButton
+ );
+ }
}
// When the panel is hidden we want to run some cleanup
@@ -88,11 +118,7 @@ class _ToolbarPanelHub {
// Render what's new messages into the panel.
async renderMessages(win, doc, containerId) {
- const messages = (await this._getMessages({
- template: "whatsnew_panel_message",
- triggerId: "whatsNewPanelOpened",
- returnAll: true,
- })).sort((m1, m2) => {
+ const messages = (await this.messages).sort((m1, m2) => {
// Sort by published_date in descending order.
if (m1.content.published_date === m2.content.published_date) {
return 0;
diff --git a/browser/components/newtab/test/browser/browser.ini b/browser/components/newtab/test/browser/browser.ini
index efc2e7313dd6..1c27f064ea42 100644
--- a/browser/components/newtab/test/browser/browser.ini
+++ b/browser/components/newtab/test/browser/browser.ini
@@ -26,5 +26,4 @@ skip-if = (os == "linux") # Test setup only implemented for OSX and Windows
[browser_topsites_contextMenu_options.js]
[browser_topsites_section.js]
[browser_asrouter_cfr.js]
-skip-if = fission
[browser_asrouter_bookmarkpanel.js]
diff --git a/browser/components/newtab/test/unit/asrouter/ASRouter.test.js b/browser/components/newtab/test/unit/asrouter/ASRouter.test.js
index ff5b12de908f..08b114a1dd0f 100644
--- a/browser/components/newtab/test/unit/asrouter/ASRouter.test.js
+++ b/browser/components/newtab/test/unit/asrouter/ASRouter.test.js
@@ -206,12 +206,17 @@ describe("ASRouter", () => {
handleMessageRequest: Router.handleMessageRequest,
addImpression: Router.addImpression,
blockMessageById: Router.blockMessageById,
+ dispatch: Router.dispatch,
}
);
- assert.calledWithExactly(FakeToolbarPanelHub.init, {
- getMessages: Router.handleMessageRequest,
- });
+ assert.calledWithExactly(
+ FakeToolbarPanelHub.init,
+ Router.waitForInitialized,
+ {
+ getMessages: Router.handleMessageRequest,
+ }
+ );
assert.calledWithExactly(
FakeBookmarkPanelHub.init,
diff --git a/browser/components/newtab/test/unit/asrouter/ModalOverlay.test.jsx b/browser/components/newtab/test/unit/asrouter/ModalOverlay.test.jsx
index c801c3e75147..0e4a07cb4a53 100644
--- a/browser/components/newtab/test/unit/asrouter/ModalOverlay.test.jsx
+++ b/browser/components/newtab/test/unit/asrouter/ModalOverlay.test.jsx
@@ -5,12 +5,18 @@ import React from "react";
describe("ModalOverlayWrapper", () => {
let fakeDoc;
let sandbox;
+ let header;
beforeEach(() => {
sandbox = sinon.createSandbox();
+ header = document.createElement("div");
+
fakeDoc = {
addEventListener: sandbox.stub(),
removeEventListener: sandbox.stub(),
body: { classList: { add: sandbox.stub(), remove: sandbox.stub() } },
+ getElementById() {
+ return header;
+ },
};
});
afterEach(() => {
diff --git a/browser/components/newtab/test/unit/common/Reducers.test.js b/browser/components/newtab/test/unit/common/Reducers.test.js
index bdf8ac285f2e..622707f02ff0 100644
--- a/browser/components/newtab/test/unit/common/Reducers.test.js
+++ b/browser/components/newtab/test/unit/common/Reducers.test.js
@@ -970,6 +970,7 @@ describe("Reducers", () => {
lastUpdated: 123,
loaded: true,
frequency_caps: [],
+ blocked: [],
});
});
it("should handle no data from DISCOVERY_STREAM_SPOCS_UPDATE", () => {
@@ -980,6 +981,21 @@ describe("Reducers", () => {
});
assert.deepEqual(state.spocs, INITIAL_STATE.DiscoveryStream.spocs);
});
+ it("should add blocked spocs to blocked array with DISCOVERY_STREAM_SPOC_BLOCKED", () => {
+ const firstState = DiscoveryStream(undefined, {
+ type: at.DISCOVERY_STREAM_SPOC_BLOCKED,
+ data: { url: "https://foo.com" },
+ });
+ const secondState = DiscoveryStream(firstState, {
+ type: at.DISCOVERY_STREAM_SPOC_BLOCKED,
+ data: { url: "https://bar.com" },
+ });
+ assert.deepEqual(firstState.spocs.blocked, ["https://foo.com"]);
+ assert.deepEqual(secondState.spocs.blocked, [
+ "https://foo.com",
+ "https://bar.com",
+ ]);
+ });
it("should not update state for empty action.data on DISCOVERY_STREAM_LINK_BLOCKED", () => {
const newState = DiscoveryStream(undefined, {
type: at.DISCOVERY_STREAM_LINK_BLOCKED,
diff --git a/browser/components/newtab/test/unit/content-src/components/Card.test.jsx b/browser/components/newtab/test/unit/content-src/components/Card.test.jsx
index e1d35d6c5f53..dd8ba88a344a 100644
--- a/browser/components/newtab/test/unit/content-src/components/Card.test.jsx
+++ b/browser/components/newtab/test/unit/content-src/components/Card.test.jsx
@@ -7,6 +7,7 @@ import { combineReducers, createStore } from "redux";
import { GlobalOverrider } from "test/unit/utils";
import { INITIAL_STATE, reducers } from "common/Reducers.jsm";
import { cardContextTypes } from "content-src/components/Card/types";
+import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
import { Provider } from "react-redux";
import React from "react";
@@ -149,18 +150,22 @@ describe("", () => {
const title = '"fluent"';
const link = { ...DEFAULT_PROPS.link, title };
- wrapper = shallow();
- let button = wrapper.find(
- "button[data-l10n-id='newtab-menu-content-tooltip']"
- );
+ wrapper = mountCardWithProps({ ...DEFAULT_PROPS, link });
+ let button = wrapper.find(ContextMenuButton).find("button");
assert.equal(button.prop("data-l10n-args"), JSON.stringify({ title }));
});
it("should have .active class, on card-outer if context menu is open", () => {
- const button = wrapper.find(".context-menu-button");
- assert.isFalse(wrapper.find(".card-outer").hasClass("active"));
+ const button = wrapper.find(ContextMenuButton);
+ assert.isFalse(
+ wrapper.find(".card-outer").hasClass("active"),
+ "does not have active class"
+ );
button.simulate("click", { preventDefault: () => {} });
- assert.isTrue(wrapper.find(".card-outer").hasClass("active"));
+ assert.isTrue(
+ wrapper.find(".card-outer").hasClass("active"),
+ "has active class"
+ );
});
it("should send SHOW_DOWNLOAD_FILE if we clicked on a download", () => {
const downloadLink = {
@@ -382,7 +387,7 @@ describe("", () => {
});
it("should not have a context menu button or LinkMenu", () => {
assert.isFalse(
- wrapper.find(".context-menu-button").exists(),
+ wrapper.find(ContextMenuButton).exists(),
"context menu button"
);
assert.isFalse(wrapper.find(LinkMenu).exists(), "LinkMenu");
diff --git a/browser/components/newtab/test/unit/content-src/components/ContextMenu.test.jsx b/browser/components/newtab/test/unit/content-src/components/ContextMenu.test.jsx
index 31ba4468ceaa..2288f6c0c82f 100644
--- a/browser/components/newtab/test/unit/content-src/components/ContextMenu.test.jsx
+++ b/browser/components/newtab/test/unit/content-src/components/ContextMenu.test.jsx
@@ -2,6 +2,7 @@ import {
ContextMenu,
ContextMenuItem,
} from "content-src/components/ContextMenu/ContextMenu";
+import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
import { mount, shallow } from "enzyme";
import React from "react";
@@ -11,6 +12,88 @@ const DEFAULT_PROPS = {
tabbableOptionsLength: 0,
};
+const DEFAULT_MENU_OPTIONS = [
+ "MoveUp",
+ "MoveDown",
+ "Separator",
+ "RemoveSection",
+ "CheckCollapsed",
+ "Separator",
+ "ManageSection",
+];
+
+const FakeMenu = props => {
+ return {props.children}
;
+};
+
+describe("", () => {
+ let sandbox;
+ beforeEach(() => {
+ sandbox = sinon.createSandbox();
+ });
+ afterEach(() => {
+ sandbox.restore();
+ });
+ it("should call onUpdate when clicked", () => {
+ const onUpdate = sandbox.spy();
+ const wrapper = mount(
+
+
+
+ );
+ wrapper.find(".context-menu-button").simulate("click");
+ assert.calledOnce(onUpdate);
+ });
+ it("should call onUpdate when activated with Enter", () => {
+ const onUpdate = sandbox.spy();
+ const wrapper = mount(
+
+
+
+ );
+ wrapper.find(".context-menu-button").simulate("keydown", { key: "Enter" });
+ assert.calledOnce(onUpdate);
+ });
+ it("should call onClick", () => {
+ const onClick = sandbox.spy(ContextMenuButton.prototype, "onClick");
+ const wrapper = mount(
+
+
+
+ );
+ wrapper.find("button").simulate("click");
+ assert.calledOnce(onClick);
+ });
+ it("should have a default keyboardAccess prop of false", () => {
+ const wrapper = mount(
+
+
+
+ );
+ wrapper.setState({ showContextMenu: true });
+ assert.equal(wrapper.find(ContextMenu).prop("keyboardAccess"), false);
+ });
+ it("should pass the keyboardAccess prop down to ContextMenu", () => {
+ const wrapper = mount(
+
+
+
+ );
+ wrapper.setState({ showContextMenu: true, contextMenuKeyboard: true });
+ assert.equal(wrapper.find(ContextMenu).prop("keyboardAccess"), true);
+ });
+ it("should call focusFirst when keyboardAccess is true", () => {
+ const wrapper = mount(
+
+
+
+ );
+ const focusFirst = sandbox.spy(ContextMenuItem.prototype, "focusFirst");
+ wrapper.setState({ showContextMenu: true, contextMenuKeyboard: true });
+ assert.calledOnce(focusFirst);
+ });
+});
+
describe("", () => {
it("should render all the options provided", () => {
const options = [
@@ -79,7 +162,7 @@ describe("", () => {
const wrapper = mount();
assert.lengthOf(wrapper.find(".context-menu-item"), 1);
});
- it("should call onClick when onKeyDown is called", () => {
+ it("should call onClick when onKeyDown is called with Enter", () => {
const onClick = sinon.spy();
const wrapper = mount(
diff --git a/browser/components/newtab/test/unit/content-src/components/DiscoveryStreamComponents/DSLinkMenu.test.jsx b/browser/components/newtab/test/unit/content-src/components/DiscoveryStreamComponents/DSLinkMenu.test.jsx
index 748cd51a8cd4..c31e34bcc21a 100644
--- a/browser/components/newtab/test/unit/content-src/components/DiscoveryStreamComponents/DSLinkMenu.test.jsx
+++ b/browser/components/newtab/test/unit/content-src/components/DiscoveryStreamComponents/DSLinkMenu.test.jsx
@@ -1,5 +1,6 @@
import { mount, shallow } from "enzyme";
import { DSLinkMenu } from "content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu";
+import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
import React from "react";
@@ -18,7 +19,6 @@ describe("", () => {
});
it("Should remove active on Menu Update", () => {
- wrapper.setState({ showContextMenu: true });
// Add active class name to DSLinkMenu parent node
// to simulate menu open state
parentNode.classList.add("active");
@@ -28,7 +28,6 @@ describe("", () => {
wrapper.update();
assert.isEmpty(parentNode.className);
- assert.isFalse(wrapper.state(["showContextMenu"]));
});
it("Should add active on Menu Show", async () => {
@@ -76,7 +75,7 @@ describe("", () => {
it("should parse args for fluent correctly ", () => {
const title = '"fluent"';
- wrapper = shallow();
+ wrapper = mount();
const button = wrapper.find(
"button[data-l10n-id='newtab-menu-content-tooltip']"
@@ -96,23 +95,25 @@ describe("", () => {
it("should render a context menu button", () => {
assert.ok(wrapper.exists());
- assert.ok(wrapper.find(".context-menu-button").exists());
+ assert.ok(
+ wrapper.find(ContextMenuButton).exists(),
+ "context menu button exists"
+ );
});
it("should render LinkMenu when context menu button is clicked", () => {
- let button = wrapper.find(".context-menu-button");
+ let button = wrapper.find(ContextMenuButton);
button.simulate("click", { preventDefault: () => {} });
assert.equal(wrapper.find(LinkMenu).length, 1);
});
- it("should pass dispatch, onUpdate, onShow, site, options, shouldSendImpressionStats, source and index to LinkMenu", () => {
+ it("should pass dispatch, onShow, site, options, shouldSendImpressionStats, source and index to LinkMenu", () => {
wrapper
- .find(".context-menu-button")
+ .find(ContextMenuButton)
.simulate("click", { preventDefault: () => {} });
const linkMenuProps = wrapper.find(LinkMenu).props();
[
"dispatch",
- "onUpdate",
"onShow",
"site",
"index",
@@ -124,7 +125,7 @@ describe("", () => {
it("should pass through the correct menu options to LinkMenu", () => {
wrapper
- .find(".context-menu-button")
+ .find(ContextMenuButton)
.simulate("click", { preventDefault: () => {} });
const linkMenuProps = wrapper.find(LinkMenu).props();
assert.deepEqual(linkMenuProps.options, [
diff --git a/browser/components/newtab/test/unit/content-src/components/SectionMenu.test.jsx b/browser/components/newtab/test/unit/content-src/components/SectionMenu.test.jsx
index a12ebff62030..ca6ee2e907c0 100644
--- a/browser/components/newtab/test/unit/content-src/components/SectionMenu.test.jsx
+++ b/browser/components/newtab/test/unit/content-src/components/SectionMenu.test.jsx
@@ -164,17 +164,6 @@ describe("", () => {
});
describe(".onClick", () => {
const dispatch = sinon.stub();
- const propOptions = [
- "Separator",
- "MoveUp",
- "MoveDown",
- "RemoveSection",
- "CollapseSection",
- "ExpandSection",
- "ManageSection",
- "AddTopSite",
- "PrivacyNotice",
- ];
const expectedActionData = {
"newtab-section-menu-move-up": { id: "sectionId", direction: -1 },
"newtab-section-menu-move-down": { id: "sectionId", direction: +1 },
@@ -197,11 +186,7 @@ describe("", () => {
},
};
const { options } = shallow(
-
+
)
.find(ContextMenu)
.props();
@@ -238,4 +223,54 @@ describe("", () => {
});
});
});
+ describe("dispatch expand section if section is collapsed and adding top site", () => {
+ const dispatch = sinon.stub();
+ const expectedExpandData = {
+ id: DEFAULT_PROPS.id,
+ value: { collapsed: false },
+ };
+ const expectedAddData = { index: -1 };
+ const { options } = shallow(
+
+ )
+ .find(ContextMenu)
+ .props();
+ afterEach(() => dispatch.reset());
+
+ assert.equal(options[0].id, "newtab-section-menu-add-topsite");
+ options
+ .filter(o => o.id === "newtab-section-menu-add-topsite")
+ .forEach(option => {
+ it(`should dispatch an action to expand the section and to add a topsite after expanding`, () => {
+ option.onClick();
+
+ const [expandAction] = dispatch.firstCall.args;
+ assert.deepEqual(expandAction.data, expectedExpandData);
+
+ const [addAction] = dispatch.thirdCall.args;
+ assert.deepEqual(addAction.data, expectedAddData);
+ });
+ it(`should dispatch the expand userEvent and add topsite userEvent after expanding`, () => {
+ option.onClick();
+ assert.ok(dispatch.thirdCall.calledWith(option.action));
+
+ const [expandUserEvent] = dispatch.secondCall.args;
+ assert.isUserEventAction(expandUserEvent);
+ assert.propertyVal(
+ expandUserEvent.data,
+ "source",
+ DEFAULT_PROPS.source
+ );
+
+ const [addUserEvent] = dispatch.lastCall.args;
+ assert.isUserEventAction(addUserEvent);
+ assert.propertyVal(addUserEvent.data, "source", DEFAULT_PROPS.source);
+ });
+ });
+ });
});
diff --git a/browser/components/newtab/test/unit/content-src/components/TopSites.test.jsx b/browser/components/newtab/test/unit/content-src/components/TopSites.test.jsx
index 4b4118cfc08c..b2eb4721b746 100644
--- a/browser/components/newtab/test/unit/content-src/components/TopSites.test.jsx
+++ b/browser/components/newtab/test/unit/content-src/components/TopSites.test.jsx
@@ -22,6 +22,7 @@ import { mount, shallow } from "enzyme";
import { TopSiteForm } from "content-src/components/TopSites/TopSiteForm";
import { TopSiteFormInput } from "content-src/components/TopSites/TopSiteFormInput";
import { _TopSites as TopSites } from "content-src/components/TopSites/TopSites";
+import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
const perfSvc = {
mark() {},
@@ -724,24 +725,14 @@ describe("", () => {
});
it("should render a context menu button", () => {
const wrapper = shallow();
- assert.equal(wrapper.find(".context-menu-button").length, 1);
+ assert.equal(wrapper.find(ContextMenuButton).length, 1);
});
- it("should render a link menu when button is clicked", () => {
+ it("should render a link menu", () => {
const wrapper = shallow();
- let button = wrapper.find(".context-menu-button");
- assert.equal(wrapper.find(LinkMenu).length, 0);
- button.simulate("click", { preventDefault: () => {} });
assert.equal(wrapper.find(LinkMenu).length, 1);
});
- it("should not render a link menu by default", () => {
- const wrapper = shallow();
- assert.equal(wrapper.find(LinkMenu).length, 0);
- });
it("should pass onUpdate, site, options, and index to LinkMenu", () => {
const wrapper = shallow();
- wrapper
- .find(".context-menu-button")
- .simulate("click", { preventDefault: () => {} });
const linkMenuProps = wrapper.find(LinkMenu).props();
["onUpdate", "site", "index", "options"].forEach(prop =>
assert.property(linkMenuProps, prop)
@@ -749,9 +740,6 @@ describe("", () => {
});
it("should pass through the correct menu options to LinkMenu", () => {
const wrapper = shallow();
- wrapper
- .find(".context-menu-button")
- .simulate("click", { preventDefault: () => {} });
const linkMenuProps = wrapper.find(LinkMenu).props();
assert.deepEqual(linkMenuProps.options, [
"CheckPinTopSite",
diff --git a/browser/components/newtab/test/unit/content-src/lib/selectLayoutRender.test.js b/browser/components/newtab/test/unit/content-src/lib/selectLayoutRender.test.js
index 7e5c07dbde55..6a2914ba2b9e 100644
--- a/browser/components/newtab/test/unit/content-src/lib/selectLayoutRender.test.js
+++ b/browser/components/newtab/test/unit/content-src/lib/selectLayoutRender.test.js
@@ -702,7 +702,7 @@ describe("selectLayoutRender", () => {
type: "foo3",
properties: { items: 3 },
feed: { url: "foo3.com" },
- spocs: { positions: [{ index: 0, probability: 1 }] },
+ spocs: { positions: [{ index: 0 }], probability: 1 },
},
{ type: "foo4", properties: { items: 3 }, feed: { url: "foo4.com" } },
{ type: "foo5" },
@@ -751,7 +751,7 @@ describe("selectLayoutRender", () => {
type: "foo3",
properties: { items: 3 },
feed: { url: "foo3.com" },
- spocs: { positions: [{ index: 0, probability: 1 }] },
+ spocs: { positions: [{ index: 0 }], probability: 1 },
},
{ type: "foo4", properties: { items: 3 }, feed: { url: "foo4.com" } },
{ type: "foo5" },
@@ -840,4 +840,67 @@ describe("selectLayoutRender", () => {
assert.equal(layoutRender[0].components[0].type, "TopSites");
assert.equal(layoutRender[0].components[1], undefined);
});
+ it("should skip rendering a spoc in position if that spoc is blocked for that session", () => {
+ const fakeLayout = [
+ {
+ width: 3,
+ components: [
+ {
+ type: "foo1",
+ properties: { items: 3 },
+ feed: { url: "foo1.com" },
+ spocs: { positions: [{ index: 0 }], probability: 1 },
+ },
+ ],
+ },
+ ];
+ const fakeSpocsData = {
+ lastUpdated: 0,
+ spocs: {
+ spocs: [{ name: "spoc", url: "https://foo.com" }],
+ },
+ };
+ store.dispatch({
+ type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
+ data: { layout: fakeLayout },
+ });
+ store.dispatch({
+ type: at.DISCOVERY_STREAM_FEED_UPDATE,
+ data: {
+ feed: { data: { recommendations: [{ name: "rec" }] } },
+ url: "foo1.com",
+ },
+ });
+ store.dispatch({
+ type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
+ data: fakeSpocsData,
+ });
+
+ const { layoutRender: layout1 } = selectLayoutRender(
+ store.getState().DiscoveryStream,
+ {},
+ []
+ );
+
+ store.dispatch({
+ type: at.DISCOVERY_STREAM_SPOC_BLOCKED,
+ data: { url: "https://foo.com" },
+ });
+
+ const { layoutRender: layout2 } = selectLayoutRender(
+ store.getState().DiscoveryStream,
+ {},
+ []
+ );
+
+ assert.deepEqual(layout1[0].components[0].data.recommendations[0], {
+ name: "spoc",
+ url: "https://foo.com",
+ pos: 0,
+ });
+ assert.deepEqual(layout2[0].components[0].data.recommendations[0], {
+ name: "rec",
+ pos: 0,
+ });
+ });
});
diff --git a/browser/components/newtab/test/unit/lib/DiscoveryStreamFeed.test.js b/browser/components/newtab/test/unit/lib/DiscoveryStreamFeed.test.js
index c495addc7c3a..f3499b1444e4 100644
--- a/browser/components/newtab/test/unit/lib/DiscoveryStreamFeed.test.js
+++ b/browser/components/newtab/test/unit/lib/DiscoveryStreamFeed.test.js
@@ -1429,6 +1429,20 @@ describe("DiscoveryStreamFeed", () => {
"not_a_spoc.com"
);
});
+ it("should dispatch a DISCOVERY_STREAM_SPOC_BLOCKED for a blocked spoc", async () => {
+ Object.defineProperty(feed, "showSpocs", { get: () => true });
+ sandbox.spy(feed.store, "dispatch");
+
+ await feed.onAction({
+ type: at.PLACES_LINK_BLOCKED,
+ data: { url: "foo.com" },
+ });
+
+ assert.equal(
+ feed.store.dispatch.thirdCall.args[0].type,
+ "DISCOVERY_STREAM_SPOC_BLOCKED"
+ );
+ });
});
describe("#onAction: INIT", () => {
diff --git a/browser/components/newtab/test/unit/lib/ToolbarBadgeHub.test.js b/browser/components/newtab/test/unit/lib/ToolbarBadgeHub.test.js
index 515460e04033..f119e3c7352a 100644
--- a/browser/components/newtab/test/unit/lib/ToolbarBadgeHub.test.js
+++ b/browser/components/newtab/test/unit/lib/ToolbarBadgeHub.test.js
@@ -7,6 +7,8 @@ describe("ToolbarBadgeHub", () => {
let sandbox;
let instance;
let fakeAddImpression;
+ let fakeDispatch;
+ let isBrowserPrivateStub;
let fxaMessage;
let whatsnewMessage;
let fakeElement;
@@ -14,11 +16,15 @@ describe("ToolbarBadgeHub", () => {
let everyWindowStub;
let clearTimeoutStub;
let setTimeoutStub;
+ let addObserverStub;
+ let removeObserverStub;
beforeEach(async () => {
globals = new GlobalOverrider();
sandbox = sinon.createSandbox();
instance = new _ToolbarBadgeHub();
fakeAddImpression = sandbox.stub();
+ fakeDispatch = sandbox.stub();
+ isBrowserPrivateStub = sandbox.stub();
const msgs = await PanelTestProvider.getMessages();
fxaMessage = msgs.find(({ id }) => id === "FXA_ACCOUNTS_BADGE");
whatsnewMessage = msgs.find(({ id }) => id.includes("WHATS_NEW_BADGE_"));
@@ -40,9 +46,30 @@ describe("ToolbarBadgeHub", () => {
};
clearTimeoutStub = sandbox.stub();
setTimeoutStub = sandbox.stub();
- globals.set("EveryWindow", everyWindowStub);
- globals.set("setTimeout", setTimeoutStub);
- globals.set("clearTimeout", clearTimeoutStub);
+ const fakeWindow = {
+ ownerGlobal: {
+ gBrowser: {
+ selectedBrowser: "browser",
+ },
+ },
+ };
+ addObserverStub = sandbox.stub();
+ removeObserverStub = sandbox.stub();
+ globals.set({
+ EveryWindow: everyWindowStub,
+ PrivateBrowsingUtils: { isBrowserPrivate: isBrowserPrivateStub },
+ setTimeout: setTimeoutStub,
+ clearTimeout: clearTimeoutStub,
+ Services: {
+ wm: {
+ getMostRecentWindow: () => fakeWindow,
+ },
+ prefs: {
+ addObserver: addObserverStub,
+ removeObserver: removeObserverStub,
+ },
+ },
+ });
});
afterEach(() => {
sandbox.restore();
@@ -60,8 +87,21 @@ describe("ToolbarBadgeHub", () => {
assert.calledOnce(instance.messageRequest);
assert.calledWithExactly(instance.messageRequest, "toolbarBadgeUpdate");
});
+ it("should add a pref observer", async () => {
+ await instance.init(sandbox.stub().resolves(), {});
+
+ assert.calledOnce(addObserverStub);
+ assert.calledWithExactly(
+ addObserverStub,
+ instance.prefs.WHATSNEW_TOOLBAR_PANEL,
+ instance
+ );
+ });
});
describe("#uninit", () => {
+ beforeEach(async () => {
+ instance.init(sandbox.stub().resolves(), {});
+ });
it("should clear any setTimeout cbs", () => {
instance.init(sandbox.stub().resolves(), {});
@@ -72,6 +112,16 @@ describe("ToolbarBadgeHub", () => {
assert.calledOnce(clearTimeoutStub);
assert.calledWithExactly(clearTimeoutStub, 2);
});
+ it("should remove the pref observer", () => {
+ instance.uninit();
+
+ assert.calledOnce(removeObserverStub);
+ assert.calledWithExactly(
+ removeObserverStub,
+ instance.prefs.WHATSNEW_TOOLBAR_PANEL,
+ instance
+ );
+ });
});
describe("messageRequest", () => {
let handleMessageRequestStub;
@@ -166,6 +216,7 @@ describe("ToolbarBadgeHub", () => {
beforeEach(() => {
instance.init(sandbox.stub().resolves(), {
addImpression: fakeAddImpression,
+ dispatch: fakeDispatch,
});
sandbox.stub(instance, "addToolbarNotification").returns(fakeElement);
sandbox.stub(instance, "removeToolbarNotification");
@@ -212,6 +263,18 @@ describe("ToolbarBadgeHub", () => {
assert.calledOnce(instance.removeToolbarNotification);
assert.calledWithExactly(instance.removeToolbarNotification, fakeElement);
});
+ it("should send an impression", async () => {
+ sandbox.stub(instance, "sendUserEventTelemetry");
+
+ instance.registerBadgeNotificationListener(fxaMessage);
+
+ assert.calledOnce(instance.sendUserEventTelemetry);
+ assert.calledWithExactly(
+ instance.sendUserEventTelemetry,
+ "IMPRESSION",
+ fxaMessage
+ );
+ });
it("should unregister notifications when forcing a badge via devtools", () => {
instance.registerBadgeNotificationListener(fxaMessage, { force: true });
@@ -228,6 +291,16 @@ describe("ToolbarBadgeHub", () => {
instance.executeAction({ id: "show-whatsnew-button" });
+ assert.calledOnce(stub);
+ });
+ it("should call ToolbarPanelHub.enableAppmenuButton", () => {
+ const stub = sandbox.stub(
+ _ToolbarPanelHub.prototype,
+ "enableAppmenuButton"
+ );
+
+ instance.executeAction({ id: "show-whatsnew-button" });
+
assert.calledOnce(stub);
});
});
@@ -245,6 +318,7 @@ describe("ToolbarBadgeHub", () => {
let blockMessageByIdStub;
let fakeEvent;
beforeEach(() => {
+ instance.init(sandbox.stub().resolves(), { dispatch: fakeDispatch });
blockMessageByIdStub = sandbox.stub();
sandbox.stub(instance, "_blockMessageById").value(blockMessageByIdStub);
instance.state = { notification: { id: fxaMessage.id } };
@@ -307,6 +381,18 @@ describe("ToolbarBadgeHub", () => {
instance.removeAllNotifications
);
});
+ it("should send telemetry", () => {
+ fakeEvent.type = "click";
+ fakeEvent.button = 0;
+ sandbox.stub(instance, "sendUserEventTelemetry");
+
+ instance.removeAllNotifications(fakeEvent);
+
+ assert.calledOnce(instance.sendUserEventTelemetry);
+ assert.calledWithExactly(instance.sendUserEventTelemetry, "CLICK", {
+ id: "FXA_ACCOUNTS_BADGE",
+ });
+ });
it("should remove the event listeners after succesfully focusing the element", () => {
fakeEvent.type = "keypress";
fakeEvent.key = "Enter";
@@ -367,4 +453,43 @@ describe("ToolbarBadgeHub", () => {
);
});
});
+ describe("#sendUserEventTelemetry", () => {
+ beforeEach(() => {
+ instance.init(sandbox.stub().resolves(), { dispatch: fakeDispatch });
+ });
+ it("should check for private window and not send", () => {
+ isBrowserPrivateStub.returns(true);
+
+ instance.sendUserEventTelemetry("CLICK", { id: fxaMessage });
+
+ assert.notCalled(instance._dispatch);
+ });
+ it("should check for private window and send", () => {
+ isBrowserPrivateStub.returns(false);
+
+ instance.sendUserEventTelemetry("CLICK", { id: fxaMessage });
+
+ assert.calledOnce(fakeDispatch);
+ const [ping] = instance._dispatch.firstCall.args;
+ assert.propertyVal(ping, "type", "TOOLBAR_BADGE_TELEMETRY");
+ assert.propertyVal(ping.data, "event", "CLICK");
+ });
+ });
+ describe("#observe", () => {
+ it("should make a message request when the whats new pref is changed", () => {
+ sandbox.stub(instance, "messageRequest");
+
+ instance.observe("", "", instance.prefs.WHATSNEW_TOOLBAR_PANEL);
+
+ assert.calledOnce(instance.messageRequest);
+ assert.calledWithExactly(instance.messageRequest, "toolbarBadgeUpdate");
+ });
+ it("should not react to other pref changes", () => {
+ sandbox.stub(instance, "messageRequest");
+
+ instance.observe("", "", "foo");
+
+ assert.notCalled(instance.messageRequest);
+ });
+ });
});
diff --git a/browser/components/newtab/test/unit/lib/ToolbarPanelHub.test.js b/browser/components/newtab/test/unit/lib/ToolbarPanelHub.test.js
index 6bcee6f329e5..01ab3a8c1af0 100644
--- a/browser/components/newtab/test/unit/lib/ToolbarPanelHub.test.js
+++ b/browser/components/newtab/test/unit/lib/ToolbarPanelHub.test.js
@@ -12,11 +12,16 @@ describe("ToolbarPanelHub", () => {
let fakeElementById;
let createdElements = [];
let eventListeners = {};
+ let addObserverStub;
+ let removeObserverStub;
+ let getBoolPrefStub;
+ let waitForInitializedStub;
beforeEach(async () => {
sandbox = sinon.createSandbox();
globals = new GlobalOverrider();
instance = new _ToolbarPanelHub();
+ waitForInitializedStub = sandbox.stub().resolves();
fakeElementById = {
setAttribute: sandbox.stub(),
removeAttribute: sandbox.stub(),
@@ -59,38 +64,140 @@ describe("ToolbarPanelHub", () => {
registerCallback: sandbox.stub(),
unregisterCallback: sandbox.stub(),
};
+ addObserverStub = sandbox.stub();
+ removeObserverStub = sandbox.stub();
+ getBoolPrefStub = sandbox.stub();
globals.set("EveryWindow", everyWindowStub);
+ globals.set("Services", {
+ ...Services,
+ prefs: {
+ addObserver: addObserverStub,
+ removeObserver: removeObserverStub,
+ getBoolPref: getBoolPrefStub,
+ },
+ });
});
afterEach(() => {
instance.uninit();
sandbox.restore();
+ globals.restore();
});
it("should create an instance", () => {
assert.ok(instance);
});
it("should not enableAppmenuButton() on init() if pref is not enabled", () => {
- sandbox.stub(global.Services.prefs, "getBoolPref").returns(false);
+ getBoolPrefStub.returns(false);
instance.enableAppmenuButton = sandbox.stub();
- instance.init({ getMessages: () => {} });
+ instance.init(waitForInitializedStub, { getMessages: () => {} });
assert.notCalled(instance.enableAppmenuButton);
});
- it("should enableAppmenuButton() on init() if pref is enabled", () => {
- sandbox.stub(global.Services.prefs, "getBoolPref").returns(true);
+ it("should enableAppmenuButton() on init() if pref is enabled", async () => {
+ getBoolPrefStub.returns(true);
instance.enableAppmenuButton = sandbox.stub();
- instance.init({ getMessages: () => {} });
+
+ await instance.init(waitForInitializedStub, { getMessages: () => {} });
+
assert.calledOnce(instance.enableAppmenuButton);
});
it("should unregisterCallback on uninit()", () => {
instance.uninit();
assert.calledTwice(everyWindowStub.unregisterCallback);
});
- it("should registerCallback on enableAppmenuButton()", () => {
- instance.enableAppmenuButton();
- assert.calledOnce(everyWindowStub.registerCallback);
+ it("should observe pref changes on init", async () => {
+ await instance.init(waitForInitializedStub, {});
+
+ assert.calledOnce(addObserverStub);
+ assert.calledWithExactly(
+ addObserverStub,
+ "browser.messaging-system.whatsNewPanel.enabled",
+ instance
+ );
});
- it("should registerCallback on enableToolbarButton()", () => {
- instance.enableToolbarButton();
- assert.calledOnce(everyWindowStub.registerCallback);
+ it("should remove the observer on uninit", () => {
+ instance.uninit();
+
+ assert.calledOnce(removeObserverStub);
+ assert.calledWithExactly(
+ removeObserverStub,
+ "browser.messaging-system.whatsNewPanel.enabled",
+ instance
+ );
+ });
+ describe("#observe", () => {
+ it("should uninit if the pref is turned off", () => {
+ sandbox.stub(instance, "uninit");
+ getBoolPrefStub.returns(false);
+
+ instance.observe(
+ "",
+ "",
+ "browser.messaging-system.whatsNewPanel.enabled"
+ );
+
+ assert.calledOnce(instance.uninit);
+ });
+ it("shouldn't do anything if the pref is true", () => {
+ sandbox.stub(instance, "uninit");
+ getBoolPrefStub.returns(true);
+
+ instance.observe(
+ "",
+ "",
+ "browser.messaging-system.whatsNewPanel.enabled"
+ );
+
+ assert.notCalled(instance.uninit);
+ });
+ });
+ describe("#enableAppmenuButton", () => {
+ it("should registerCallback on enableAppmenuButton() if there are messages", async () => {
+ instance.init(waitForInitializedStub, {
+ getMessages: sandbox.stub().resolves([{}, {}]),
+ });
+ // init calls `enableAppmenuButton`
+ everyWindowStub.registerCallback.resetHistory();
+
+ await instance.enableAppmenuButton();
+
+ assert.calledOnce(everyWindowStub.registerCallback);
+ assert.calledWithExactly(
+ everyWindowStub.registerCallback,
+ "appMenu-whatsnew-button",
+ sinon.match.func,
+ sinon.match.func
+ );
+ });
+ it("should not registerCallback on enableAppmenuButton() if there are no messages", async () => {
+ instance.init(waitForInitializedStub, {
+ getMessages: sandbox.stub().resolves([]),
+ });
+ // init calls `enableAppmenuButton`
+ everyWindowStub.registerCallback.resetHistory();
+
+ await instance.enableAppmenuButton();
+
+ assert.notCalled(everyWindowStub.registerCallback);
+ });
+ });
+ describe("#enableToolbarButton", () => {
+ it("should registerCallback on enableToolbarButton if messages.length", async () => {
+ instance.init(waitForInitializedStub, {
+ getMessages: sandbox.stub().resolves([{}, {}]),
+ });
+
+ await instance.enableToolbarButton();
+
+ assert.calledOnce(everyWindowStub.registerCallback);
+ });
+ it("should not registerCallback on enableToolbarButton if no messages", async () => {
+ instance.init(waitForInitializedStub, {
+ getMessages: sandbox.stub().resolves([]),
+ });
+
+ await instance.enableToolbarButton();
+
+ assert.notCalled(everyWindowStub.registerCallback);
+ });
});
it("should unhide appmenu button on _showAppmenuButton()", () => {
instance._showAppmenuButton(fakeWindow);
@@ -113,7 +220,7 @@ describe("ToolbarPanelHub", () => {
m => m.template === "whatsnew_panel_message"
);
messages[0].content.link_text = { string_id: "link_text_id" };
- instance.init({
+ instance.init(waitForInitializedStub, {
getMessages: sandbox
.stub()
.returns([messages[0], messages[2], messages[1]]),
@@ -143,14 +250,14 @@ describe("ToolbarPanelHub", () => {
const uniqueDates = [
...new Set(messages.map(m => m.content.published_date)),
];
- instance.init({
+ instance.init(waitForInitializedStub, {
getMessages: sandbox.stub().returns(messages),
});
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
assert.callCount(instance._createDateElement, uniqueDates.length);
});
it("should listen for panelhidden and remove the toolbar button", async () => {
- instance.init({
+ instance.init(waitForInitializedStub, {
getMessages: sandbox.stub().returns([]),
});
fakeDocument.getElementById
@@ -162,7 +269,7 @@ describe("ToolbarPanelHub", () => {
assert.notCalled(fakeElementById.addEventListener);
});
it("should listen for panelhidden and remove the toolbar button", async () => {
- instance.init({
+ instance.init(waitForInitializedStub, {
getMessages: sandbox.stub().returns([]),
});
diff --git a/browser/config/mozconfigs/macosx64/beta b/browser/config/mozconfigs/macosx64/beta
index 41454b28092d..a4d89d8bf4aa 100644
--- a/browser/config/mozconfigs/macosx64/beta
+++ b/browser/config/mozconfigs/macosx64/beta
@@ -2,6 +2,6 @@
ac_add_options --enable-official-branding
-export MOZ_LTO=1
+export MOZ_LTO=cross
. "$topsrcdir/build/mozconfig.common.override"
diff --git a/browser/config/mozconfigs/macosx64/devedition b/browser/config/mozconfigs/macosx64/devedition
index b19e57b0c566..715c9a6275aa 100644
--- a/browser/config/mozconfigs/macosx64/devedition
+++ b/browser/config/mozconfigs/macosx64/devedition
@@ -5,7 +5,7 @@ MOZ_REQUIRE_SIGNING=0
ac_add_options --disable-install-strip
-export MOZ_LTO=1
+export MOZ_LTO=cross
ac_add_options --enable-instruments
diff --git a/browser/config/mozconfigs/macosx64/nightly b/browser/config/mozconfigs/macosx64/nightly
index e4e3bdc5f550..0c5b2adbc7df 100644
--- a/browser/config/mozconfigs/macosx64/nightly
+++ b/browser/config/mozconfigs/macosx64/nightly
@@ -8,7 +8,7 @@ if test `uname -s` != Linux; then
ac_add_options --enable-dtrace
fi
-export MOZ_LTO=1
+export MOZ_LTO=cross
ac_add_options --with-branding=browser/branding/nightly
diff --git a/browser/config/mozconfigs/macosx64/release b/browser/config/mozconfigs/macosx64/release
index 69c5ec2075aa..2439d7131cfd 100644
--- a/browser/config/mozconfigs/macosx64/release
+++ b/browser/config/mozconfigs/macosx64/release
@@ -5,7 +5,7 @@
ac_add_options --enable-official-branding
-export MOZ_LTO=1
+export MOZ_LTO=cross
# safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
# defines.sh during the beta cycle
diff --git a/browser/config/mozconfigs/win64-aarch64/beta b/browser/config/mozconfigs/win64-aarch64/beta
index 1890757b16c4..7dbd0935b557 100644
--- a/browser/config/mozconfigs/win64-aarch64/beta
+++ b/browser/config/mozconfigs/win64-aarch64/beta
@@ -4,7 +4,7 @@
ac_add_options --enable-official-branding
-export MOZ_LTO=1
+export MOZ_LTO=cross
unset ENABLE_CLANG_PLUGIN
diff --git a/browser/config/mozconfigs/win64-aarch64/devedition b/browser/config/mozconfigs/win64-aarch64/devedition
index d24111ffe2fb..48a8ee35ca74 100644
--- a/browser/config/mozconfigs/win64-aarch64/devedition
+++ b/browser/config/mozconfigs/win64-aarch64/devedition
@@ -5,7 +5,7 @@
# Add-on signing is not required for DevEdition
MOZ_REQUIRE_SIGNING=0
-export MOZ_LTO=1
+export MOZ_LTO=cross
ac_add_options --with-branding=browser/branding/aurora
diff --git a/browser/config/mozconfigs/win64-aarch64/nightly b/browser/config/mozconfigs/win64-aarch64/nightly
index e0a29120f75b..63887716d2af 100644
--- a/browser/config/mozconfigs/win64-aarch64/nightly
+++ b/browser/config/mozconfigs/win64-aarch64/nightly
@@ -2,7 +2,7 @@
. "$topsrcdir/browser/config/mozconfigs/win64-aarch64/common-win64"
. "$topsrcdir/browser/config/mozconfigs/win64-aarch64/common-opt"
-export MOZ_LTO=1
+export MOZ_LTO=cross
ac_add_options --with-branding=browser/branding/nightly
diff --git a/browser/config/mozconfigs/win64-aarch64/release b/browser/config/mozconfigs/win64-aarch64/release
index 8ed349513589..acd20d9374ec 100644
--- a/browser/config/mozconfigs/win64-aarch64/release
+++ b/browser/config/mozconfigs/win64-aarch64/release
@@ -7,7 +7,7 @@
ac_add_options --enable-official-branding
-export MOZ_LTO=1
+export MOZ_LTO=cross
unset ENABLE_CLANG_PLUGIN
diff --git a/browser/config/mozconfigs/win64/beta b/browser/config/mozconfigs/win64/beta
index b31df287736d..46441cdf3370 100644
--- a/browser/config/mozconfigs/win64/beta
+++ b/browser/config/mozconfigs/win64/beta
@@ -6,4 +6,6 @@ export MOZ_PGO=1
ac_add_options --enable-official-branding
+export MOZ_LTO=cross
+
. "$topsrcdir/build/mozconfig.common.override"
diff --git a/browser/config/mozconfigs/win64/common-opt b/browser/config/mozconfigs/win64/common-opt
index cca521eb920e..60de2fcabc18 100644
--- a/browser/config/mozconfigs/win64/common-opt
+++ b/browser/config/mozconfigs/win64/common-opt
@@ -18,7 +18,3 @@ export MOZILLA_OFFICIAL=1
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1
-
-if [ -n "$MOZ_PGO" ]; then
- RUSTFLAGS="-Clinker-plugin-lto"
-fi
diff --git a/browser/config/mozconfigs/win64/devedition b/browser/config/mozconfigs/win64/devedition
index 8181567ab399..defb7a0dfd9b 100644
--- a/browser/config/mozconfigs/win64/devedition
+++ b/browser/config/mozconfigs/win64/devedition
@@ -12,4 +12,6 @@ ac_add_options --with-branding=browser/branding/aurora
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
+export MOZ_LTO=cross
+
. "$topsrcdir/build/mozconfig.common.override"
diff --git a/browser/config/mozconfigs/win64/nightly b/browser/config/mozconfigs/win64/nightly
index a4cf517613e3..c0ca32897458 100644
--- a/browser/config/mozconfigs/win64/nightly
+++ b/browser/config/mozconfigs/win64/nightly
@@ -4,4 +4,6 @@
ac_add_options --with-branding=browser/branding/nightly
+export MOZ_LTO=cross
+
. "$topsrcdir/build/mozconfig.common.override"
diff --git a/browser/config/mozconfigs/win64/release b/browser/config/mozconfigs/win64/release
index 952fc7b55f87..5914d9c4743a 100644
--- a/browser/config/mozconfigs/win64/release
+++ b/browser/config/mozconfigs/win64/release
@@ -9,6 +9,8 @@ export MOZ_PGO=1
ac_add_options --enable-official-branding
+export MOZ_LTO=cross
+
# safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
# defines.sh during the beta cycle
export BUILDING_RELEASE=1
diff --git a/build/debian-packages/valgrind-wheezy.diff b/build/debian-packages/valgrind-wheezy.diff
index 0227aa710126..7ddc3bbfd91f 100644
--- a/build/debian-packages/valgrind-wheezy.diff
+++ b/build/debian-packages/valgrind-wheezy.diff
@@ -1,15 +1,8 @@
-diff -Nru valgrind-3.14.0/debian/changelog valgrind-3.14.0/debian/changelog
---- valgrind-3.14.0/debian/changelog 2018-11-15 09:21:43.000000000 +0900
-+++ valgrind-3.14.0/debian/changelog 2018-11-30 15:20:10.000000000 +0900
-@@ -1,3 +1,21 @@
-+valgrind (1:3.14.0-1.deb7moz2) wheezy; urgency=medium
-+
-+ * debian/patches/vg401112: Cherry-pick upstream revision
-+ f2c03ce3babe51eecbf03735f726c4028a162857.
-+
-+ -- Mike Hommey Fri, 30 Nov 2018 15:20:10 +0900
-+
-+valgrind (1:3.14.0-1.deb7moz1) wheezy; urgency=medium
+diff -Nru valgrind-3.15.0/debian/changelog valgrind-3.15.0/debian/changelog
+--- valgrind-3.15.0/debian/changelog 2019-07-14 19:23:29.000000000 +0900
++++ valgrind-3.15.0/debian/changelog 2019-07-17 18:19:13.000000000 +0900
+@@ -1,3 +1,14 @@
++valgrind (1:3.15.0-1.deb7moz1) wheezy; urgency=medium
+
+ * Mozilla backport for wheezy.
+ * debian/control, debian/compat: Drop debhelper compat back to 7, which
@@ -18,192 +11,33 @@ diff -Nru valgrind-3.14.0/debian/changelog valgrind-3.14.0/debian/changelog
+ add --parallel back.
+ * debian/valgrind-mpi.install: Use non-multiarch path.
+
-+ -- Mike Hommey Thu, 15 Nov 2018 11:45:25 +0900
++ -- Mike Hommey Wed, 17 Jul 2019 18:19:13 +0900
+
- valgrind (1:3.14.0-1) unstable; urgency=medium
+ valgrind (1:3.15.0-1) unstable; urgency=medium
- * New upstream release (Closes: #913208)
-diff -Nru valgrind-3.14.0/debian/compat valgrind-3.14.0/debian/compat
---- valgrind-3.14.0/debian/compat 2018-11-15 09:21:43.000000000 +0900
-+++ valgrind-3.14.0/debian/compat 2018-11-15 11:45:25.000000000 +0900
+ * New upstream release
+diff -Nru valgrind-3.15.0/debian/compat valgrind-3.15.0/debian/compat
+--- valgrind-3.15.0/debian/compat 2019-07-14 19:23:29.000000000 +0900
++++ valgrind-3.15.0/debian/compat 2019-07-17 18:19:13.000000000 +0900
@@ -1 +1 @@
--11
+-12
+7
-diff -Nru valgrind-3.14.0/debian/control valgrind-3.14.0/debian/control
---- valgrind-3.14.0/debian/control 2018-11-15 09:21:43.000000000 +0900
-+++ valgrind-3.14.0/debian/control 2018-11-15 11:45:25.000000000 +0900
+diff -Nru valgrind-3.15.0/debian/control valgrind-3.15.0/debian/control
+--- valgrind-3.15.0/debian/control 2019-07-14 19:23:29.000000000 +0900
++++ valgrind-3.15.0/debian/control 2019-07-17 18:19:13.000000000 +0900
@@ -2,7 +2,8 @@
Section: devel
Priority: optional
Maintainer: Alessandro Ghedini
--Build-Depends: debhelper (>= 11),
+-Build-Depends: debhelper (>= 12),
+Build-Depends: debhelper (>= 7.0.50~),
+ dh-autoreconf,
gdb,
gcc-multilib [amd64],
libc6-dev-i386 [amd64],
-diff -Nru valgrind-3.14.0/debian/patches/series valgrind-3.14.0/debian/patches/series
---- valgrind-3.14.0/debian/patches/series 2018-11-15 09:21:43.000000000 +0900
-+++ valgrind-3.14.0/debian/patches/series 2018-11-30 15:19:14.000000000 +0900
-@@ -5,3 +5,4 @@
- 08_fix-spelling-in-manpage.patch
- 09_fix-armhf-detect.patch
- 10_mpi-pkg-config.patch
-+vg401112
-diff -Nru valgrind-3.14.0/debian/patches/vg401112 valgrind-3.14.0/debian/patches/vg401112
---- valgrind-3.14.0/debian/patches/vg401112 1970-01-01 09:00:00.000000000 +0900
-+++ valgrind-3.14.0/debian/patches/vg401112 2018-11-30 15:19:46.000000000 +0900
-@@ -0,0 +1,147 @@
-+From f2c03ce3babe51eecbf03735f726c4028a162857 Mon Sep 17 00:00:00 2001
-+From: Julian Seward
-+Date: Wed, 28 Nov 2018 14:15:06 +0100
-+Subject: [PATCH] Bug 401112 - LLVM 5.0 generates comparison against partially
-+ initialized data.
-+
-+This generalises the existing spec rules for W of 32 bits:
-+
-+ W Iex.Const.con->Ico.U64 == n;
-+ }
-+
-+-/* Returns N if E is an immediate of the form 1 << N for N in 1 to 31,
-++/* Returns N if W64 is a value of the form 1 << N for N in 1 to 31,
-+ and zero in any other case. */
-+-static Int isU64_1_shl_N ( IRExpr* e )
-++static Int isU64_1_shl_N_literal ( ULong w64 )
-+ {
-+- if (e->tag != Iex_Const || e->Iex.Const.con->tag != Ico_U64)
-+- return 0;
-+- ULong w64 = e->Iex.Const.con->Ico.U64;
-+ if (w64 < (1ULL << 1) || w64 > (1ULL << 31))
-+ return 0;
-+ if ((w64 & (w64 - 1)) != 0)
-+@@ -1036,6 +1033,30 @@ static Int isU64_1_shl_N ( IRExpr* e )
-+ return 0;
-+ }
-+
-++/* Returns N if E is an immediate of the form 1 << N for N in 1 to 31,
-++ and zero in any other case. */
-++static Int isU64_1_shl_N ( IRExpr* e )
-++{
-++ if (e->tag != Iex_Const || e->Iex.Const.con->tag != Ico_U64)
-++ return 0;
-++ ULong w64 = e->Iex.Const.con->Ico.U64;
-++ return isU64_1_shl_N_literal(w64);
-++}
-++
-++/* Returns N if E is an immediate of the form (1 << N) - 1 for N in 1 to 31,
-++ and zero in any other case. */
-++static Int isU64_1_shl_N_minus_1 ( IRExpr* e )
-++{
-++ if (e->tag != Iex_Const || e->Iex.Const.con->tag != Ico_U64)
-++ return 0;
-++ ULong w64 = e->Iex.Const.con->Ico.U64;
-++ // This isn't actually necessary since isU64_1_shl_N_literal will return
-++ // zero given a zero argument, but still ..
-++ if (w64 == 0xFFFFFFFFFFFFFFFFULL)
-++ return 0;
-++ return isU64_1_shl_N_literal(w64 + 1);
-++}
-++
-+ IRExpr* guest_amd64_spechelper ( const HChar* function_name,
-+ IRExpr** args,
-+ IRStmt** precedingStmts,
-+@@ -1258,32 +1279,51 @@ IRExpr* guest_amd64_spechelper ( const HChar* function_name,
-+ /* It appears that LLVM 5.0 and later have a new way to find out
-+ whether the top N bits of a word W are all zero, by computing
-+
-+- W 0) {
-+- /* long sub/cmp, then B (unsigned less than),
-+- where dep2 is a power of 2:
-+- -> CmpLT32(dep1, 1 << N)
-+- -> CmpEQ32(dep1 >>u N, 0)
-+- and
-+- long sub/cmp, then NB (unsigned greater than or equal),
-+- where dep2 is a power of 2:
-+- -> CmpGE32(dep1, 1 << N)
-+- -> CmpNE32(dep1 >>u N, 0)
-+- This avoids CmpLT32U/CmpGE32U being applied to potentially
-+- uninitialised bits in the area being shifted out. */
-++ Bool is_NB_or_NBE = False;
-++ if (isU64(cc_op, AMD64G_CC_OP_SUBL)) {
-++ if (isU64(cond, AMD64CondB) || isU64(cond, AMD64CondNB)) {
-++ /* long sub/cmp, then B (unsigned less than),
-++ where dep2 is a power of 2:
-++ -> CmpLT32U(dep1, 1 << N)
-++ -> CmpEQ32(dep1 >>u N, 0)
-++ and
-++ long sub/cmp, then NB (unsigned greater than or equal),
-++ where dep2 is a power of 2:
-++ -> CmpGE32U(dep1, 1 << N)
-++ -> CmpNE32(dep1 >>u N, 0)
-++ This avoids CmpLT32U/CmpGE32U being applied to potentially
-++ uninitialised bits in the area being shifted out. */
-++ n = isU64_1_shl_N(cc_dep2);
-++ is_NB_or_NBE = isU64(cond, AMD64CondNB);
-++ } else if (isU64(cond, AMD64CondBE) || isU64(cond, AMD64CondNBE)) {
-++ /* long sub/cmp, then BE (unsigned less than or equal),
-++ where dep2 is a power of 2 minus 1:
-++ -> CmpLE32U(dep1, (1 << N) - 1)
-++ -> CmpEQ32(dep1 >>u N, 0)
-++ and
-++ long sub/cmp, then NBE (unsigned greater than),
-++ where dep2 is a power of 2 minus 1:
-++ -> CmpGT32U(dep1, (1 << N) - 1)
-++ -> CmpNE32(dep1 >>u N, 0)
-++ This avoids CmpLE32U/CmpGT32U being applied to potentially
-++ uninitialised bits in the area being shifted out. */
-++ n = isU64_1_shl_N_minus_1(cc_dep2);
-++ is_NB_or_NBE = isU64(cond, AMD64CondNBE);
-++ }
-++ }
-++ if (n > 0) {
-+ vassert(n >= 1 && n <= 31);
-+- Bool isNB = isU64(cond, AMD64CondNB);
-+ return unop(Iop_1Uto64,
-+- binop(isNB ? Iop_CmpNE32 : Iop_CmpEQ32,
-++ binop(is_NB_or_NBE ? Iop_CmpNE32 : Iop_CmpEQ32,
-+ binop(Iop_Shr32, unop(Iop_64to32, cc_dep1),
-+ mkU8(n)),
-+ mkU32(0)));
-+--
-+2.9.3
-+
-diff -Nru valgrind-3.14.0/debian/rules valgrind-3.14.0/debian/rules
---- valgrind-3.14.0/debian/rules 2018-11-15 09:21:43.000000000 +0900
-+++ valgrind-3.14.0/debian/rules 2018-11-15 11:45:25.000000000 +0900
+diff -Nru valgrind-3.15.0/debian/rules valgrind-3.15.0/debian/rules
+--- valgrind-3.15.0/debian/rules 2019-07-14 19:23:29.000000000 +0900
++++ valgrind-3.15.0/debian/rules 2019-07-17 18:19:13.000000000 +0900
@@ -11,7 +11,7 @@
LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
@@ -222,9 +56,9 @@ diff -Nru valgrind-3.14.0/debian/rules valgrind-3.14.0/debian/rules
$(MAKE) -C docs FAQ.txt
$(MAKE) -C docs html-docs
$(MAKE) -C docs man-pages
-diff -Nru valgrind-3.14.0/debian/valgrind-mpi.install valgrind-3.14.0/debian/valgrind-mpi.install
---- valgrind-3.14.0/debian/valgrind-mpi.install 2018-11-15 09:21:43.000000000 +0900
-+++ valgrind-3.14.0/debian/valgrind-mpi.install 2018-11-15 11:45:25.000000000 +0900
+diff -Nru valgrind-3.15.0/debian/valgrind-mpi.install valgrind-3.15.0/debian/valgrind-mpi.install
+--- valgrind-3.15.0/debian/valgrind-mpi.install 2019-07-14 19:23:29.000000000 +0900
++++ valgrind-3.15.0/debian/valgrind-mpi.install 2019-07-17 18:19:13.000000000 +0900
@@ -1 +1 @@
-usr/lib/*/valgrind/libmpiwrap*
+usr/lib/valgrind/libmpiwrap*
diff --git a/build/moz.configure/toolchain.configure b/build/moz.configure/toolchain.configure
index 3ba77f41a4a1..72efd1425dd9 100755
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -1547,7 +1547,7 @@ def lto(value, pgo, profile_generate, c_compiler):
# MSVC's implementation of PGO implies LTO. Make clang-cl match this.
if c_compiler.type == 'clang-cl' and pgo and not profile_generate and value.origin == 'default':
- value = ['thin']
+ value = ['cross']
if value:
enabled = True
diff --git a/build/unix/mozconfig.unix b/build/unix/mozconfig.unix
index 2704ac06cf9e..952c7602a23b 100644
--- a/build/unix/mozconfig.unix
+++ b/build/unix/mozconfig.unix
@@ -26,7 +26,7 @@ else
if [ -n "$MOZ_PGO" ]; then
if [ -z "$USE_ARTIFACT" ]; then
- export MOZ_LTO=1
+ export MOZ_LTO=cross
if [ -n "$MOZ_PGO_PROFILE_USE" ]; then
ac_add_options --enable-profile-use
ac_add_options --with-pgo-jarlog=/builds/worker/fetches/en-US.log
diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h
index 7eb2b21ff8d5..d1673c9bf2f3 100644
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1839,7 +1839,7 @@ class GFX2D_API Factory {
static already_AddRefed CreateScaledFontForDWriteFont(
IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
const RefPtr& aUnscaledFont, Float aSize,
- bool aUseEmbeddedBitmap, bool aForceGDIMode,
+ bool aUseEmbeddedBitmap, int aRenderingMode,
IDWriteRenderingParams* aParams, Float aGamma, Float aContrast);
static void SetSystemTextQuality(uint8_t aQuality);
diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp
index 2a18270ce664..822537d3f658 100644
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -959,10 +959,11 @@ void Factory::D2DCleanup() {
already_AddRefed Factory::CreateScaledFontForDWriteFont(
IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
const RefPtr& aUnscaledFont, float aSize,
- bool aUseEmbeddedBitmap, bool aForceGDIMode,
+ bool aUseEmbeddedBitmap, int aRenderingMode,
IDWriteRenderingParams* aParams, Float aGamma, Float aContrast) {
return MakeAndAddRef(aFontFace, aUnscaledFont, aSize,
- aUseEmbeddedBitmap, aForceGDIMode,
+ aUseEmbeddedBitmap,
+ (DWRITE_RENDERING_MODE)aRenderingMode,
aParams, aGamma, aContrast, aStyle);
}
diff --git a/gfx/2d/ScaledFontDWrite.cpp b/gfx/2d/ScaledFontDWrite.cpp
index fe68b108bbf8..6a8e383bf7c8 100644
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -126,14 +126,14 @@ static inline DWRITE_FONT_STRETCH DWriteFontStretchFromStretch(
ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace* aFontFace,
const RefPtr& aUnscaledFont,
Float aSize, bool aUseEmbeddedBitmap,
- bool aForceGDIMode,
+ DWRITE_RENDERING_MODE aRenderingMode,
IDWriteRenderingParams* aParams,
Float aGamma, Float aContrast,
const gfxFontStyle* aStyle)
: ScaledFontBase(aUnscaledFont, aSize),
mFontFace(aFontFace),
mUseEmbeddedBitmap(aUseEmbeddedBitmap),
- mForceGDIMode(aForceGDIMode),
+ mRenderingMode(aRenderingMode),
mParams(aParams),
mGamma(aGamma),
mContrast(aContrast) {
@@ -184,7 +184,7 @@ SkTypeface* ScaledFontDWrite::CreateSkTypeface() {
}
return SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle,
- mForceGDIMode, gamma, contrast);
+ mRenderingMode, gamma, contrast);
}
#endif
@@ -416,7 +416,7 @@ ScaledFontDWrite::InstanceData::InstanceData(
const wr::FontInstanceOptions* aOptions,
const wr::FontInstancePlatformOptions* aPlatformOptions)
: mUseEmbeddedBitmap(false),
- mForceGDIMode(false),
+ mRenderingMode(DWRITE_RENDERING_MODE_DEFAULT),
mGamma(2.2f),
mContrast(1.0f) {
if (aOptions) {
@@ -424,7 +424,11 @@ ScaledFontDWrite::InstanceData::InstanceData(
mUseEmbeddedBitmap = true;
}
if (aOptions->flags & wr::FontInstanceFlags_FORCE_GDI) {
- mForceGDIMode = true;
+ mRenderingMode = DWRITE_RENDERING_MODE_GDI_CLASSIC;
+ } else if (aOptions->flags & wr::FontInstanceFlags_FORCE_SYMMETRIC) {
+ mRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+ } else if (aOptions->flags & wr::FontInstanceFlags_NO_SYMMETRIC) {
+ mRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
}
}
if (aPlatformOptions) {
@@ -505,6 +509,16 @@ bool ScaledFontDWrite::GetWRFontInstanceOptions(
} else {
options.flags |= wr::FontInstanceFlags_SUBPIXEL_POSITION;
}
+ switch (GetRenderingMode()) {
+ case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC:
+ options.flags |= wr::FontInstanceFlags_FORCE_SYMMETRIC;
+ break;
+ case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL:
+ options.flags |= wr::FontInstanceFlags_NO_SYMMETRIC;
+ break;
+ default:
+ break;
+ }
if (Factory::GetBGRSubpixelOrder()) {
options.flags |= wr::FontInstanceFlags_SUBPIXEL_BGR;
}
@@ -594,7 +608,7 @@ already_AddRefed UnscaledFontDWrite::CreateScaledFont(
RefPtr scaledFont = new ScaledFontDWrite(
face, this, aGlyphSize, instanceData.mUseEmbeddedBitmap,
- instanceData.mForceGDIMode, nullptr, instanceData.mGamma,
+ instanceData.mRenderingMode, nullptr, instanceData.mGamma,
instanceData.mContrast);
if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
diff --git a/gfx/2d/ScaledFontDWrite.h b/gfx/2d/ScaledFontDWrite.h
index fa8fca163b3f..1d38fb67070b 100644
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -27,15 +27,17 @@ class ScaledFontDWrite final : public ScaledFontBase {
: ScaledFontBase(aUnscaledFont, aSize),
mFontFace(aFont),
mUseEmbeddedBitmap(false),
- mForceGDIMode(false),
+ mRenderingMode(DWRITE_RENDERING_MODE_DEFAULT),
mGamma(2.2f),
mContrast(1.0f) {}
ScaledFontDWrite(IDWriteFontFace* aFontFace,
const RefPtr& aUnscaledFont, Float aSize,
- bool aUseEmbeddedBitmap, bool aForceGDIMode,
- IDWriteRenderingParams* aParams, Float aGamma,
- Float aContrast, const gfxFontStyle* aStyle = nullptr);
+ bool aUseEmbeddedBitmap,
+ DWRITE_RENDERING_MODE aRenderingMode,
+ IDWriteRenderingParams* aParams,
+ Float aGamma, Float aContrast,
+ const gfxFontStyle* aStyle = nullptr);
FontType GetType() const override { return FontType::DWRITE; }
@@ -61,8 +63,9 @@ class ScaledFontDWrite final : public ScaledFontBase {
AntialiasMode GetDefaultAAMode() override;
- bool UseEmbeddedBitmaps() { return mUseEmbeddedBitmap; }
- bool ForceGDIMode() { return mForceGDIMode; }
+ bool UseEmbeddedBitmaps() const { return mUseEmbeddedBitmap; }
+ bool ForceGDIMode() const { return mRenderingMode == DWRITE_RENDERING_MODE_GDI_CLASSIC; }
+ DWRITE_RENDERING_MODE GetRenderingMode() const { return mRenderingMode; }
#ifdef USE_SKIA
SkTypeface* CreateSkTypeface() override;
@@ -71,7 +74,7 @@ class ScaledFontDWrite final : public ScaledFontBase {
RefPtr mFontFace;
bool mUseEmbeddedBitmap;
- bool mForceGDIMode;
+ DWRITE_RENDERING_MODE mRenderingMode;
// DrawTargetD2D1 requires the IDWriteRenderingParams,
// but we also separately need to store the gamma and contrast
// since Skia needs to be able to access these without having
@@ -94,7 +97,7 @@ class ScaledFontDWrite final : public ScaledFontBase {
struct InstanceData {
explicit InstanceData(ScaledFontDWrite* aScaledFont)
: mUseEmbeddedBitmap(aScaledFont->mUseEmbeddedBitmap),
- mForceGDIMode(aScaledFont->mForceGDIMode),
+ mRenderingMode(aScaledFont->mRenderingMode),
mGamma(aScaledFont->mGamma),
mContrast(aScaledFont->mContrast) {}
@@ -102,7 +105,7 @@ class ScaledFontDWrite final : public ScaledFontBase {
const wr::FontInstancePlatformOptions* aPlatformOptions);
bool mUseEmbeddedBitmap;
- bool mForceGDIMode;
+ DWRITE_RENDERING_MODE mRenderingMode;
Float mGamma;
Float mContrast;
};
diff --git a/gfx/skia/skia/include/ports/SkTypeface_win.h b/gfx/skia/skia/include/ports/SkTypeface_win.h
index d3b5ffae4496..562c4b1beab4 100644
--- a/gfx/skia/skia/include/ports/SkTypeface_win.h
+++ b/gfx/skia/skia/include/ports/SkTypeface_win.h
@@ -53,7 +53,7 @@ struct IDWriteFontFallback;
SK_API SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
IDWriteFontFace* aFontFace,
SkFontStyle aStyle,
- bool aForceGDI,
+ DWRITE_RENDERING_MODE aRenderingMode,
float aGamma,
float aContrast);
diff --git a/gfx/skia/skia/src/ports/SkFontHost_win.cpp b/gfx/skia/skia/src/ports/SkFontHost_win.cpp
index b628b657e512..4a2e039b877d 100644
--- a/gfx/skia/skia/src/ports/SkFontHost_win.cpp
+++ b/gfx/skia/skia/src/ports/SkFontHost_win.cpp
@@ -351,11 +351,11 @@ SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
IDWriteFontFace* aFontFace,
SkFontStyle aStyle,
- bool aForceGDI,
+ DWRITE_RENDERING_MODE aRenderingMode,
float aGamma,
float aContrast)
{
- return DWriteFontTypeface::Create(aFactory, aFontFace, aStyle, aForceGDI, aGamma, aContrast);
+ return DWriteFontTypeface::Create(aFactory, aFontFace, aStyle, aRenderingMode, aGamma, aContrast);
}
/**
diff --git a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp
index 8515a9cb63cf..3ad19804ea2a 100644
--- a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp
+++ b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp
@@ -335,20 +335,23 @@ SkScalerContext_DW::SkScalerContext_DW(sk_sp typefaceRef,
range.fVersion >= 1) ||
realTextSize > SkIntToScalar(20) || !is_hinted(this, typeface)) {
fTextSizeRender = realTextSize;
+ fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
fTextSizeMeasure = realTextSize;
fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
- IDWriteRenderingParams* params = sk_get_dwrite_default_rendering_params();
DWriteFontTypeface* typeface = static_cast(getTypeface());
- if (params &&
- !SUCCEEDED(typeface->fDWriteFontFace->GetRecommendedRenderingMode(
- fTextSizeRender,
- 1.0f,
- fMeasuringMode,
- params,
- &fRenderingMode))) {
- fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+ switch (typeface->GetRenderingMode()) {
+ case DWRITE_RENDERING_MODE_NATURAL:
+ case DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC:
+ fRenderingMode = typeface->GetRenderingMode();
+ break;
+ default:
+ if (IDWriteRenderingParams* params = sk_get_dwrite_default_rendering_params()) {
+ typeface->fDWriteFontFace->GetRecommendedRenderingMode(
+ fTextSizeRender, 1.0f, fMeasuringMode, params, &fRenderingMode);
+ }
+ break;
}
// We don't support outline mode right now.
diff --git a/gfx/skia/skia/src/ports/SkTypeface_win_dw.h b/gfx/skia/skia/src/ports/SkTypeface_win_dw.h
index 54dbddad726b..53e9af6ffd16 100644
--- a/gfx/skia/skia/src/ports/SkTypeface_win_dw.h
+++ b/gfx/skia/skia/src/ports/SkTypeface_win_dw.h
@@ -57,7 +57,7 @@ private:
, fDWriteFontFamily(SkSafeRefComPtr(fontFamily))
, fDWriteFont(SkSafeRefComPtr(font))
, fDWriteFontFace(SkRefComPtr(fontFace))
- , fForceGDI(false)
+ , fRenderingMode(DWRITE_RENDERING_MODE_DEFAULT)
, fGamma(2.2f)
, fContrast(1.0f)
{
@@ -105,20 +105,21 @@ public:
static DWriteFontTypeface* Create(IDWriteFactory* factory,
IDWriteFontFace* fontFace,
SkFontStyle aStyle,
- bool aForceGDI,
+ DWRITE_RENDERING_MODE aRenderingMode,
float aGamma,
float aContrast) {
DWriteFontTypeface* typeface =
new DWriteFontTypeface(aStyle, factory, fontFace,
nullptr, nullptr,
nullptr, nullptr);
- typeface->fForceGDI = aForceGDI;
+ typeface->fRenderingMode = aRenderingMode;
typeface->fGamma = aGamma;
typeface->fContrast = aContrast;
return typeface;
}
- bool ForceGDI() const { return fForceGDI; }
+ bool ForceGDI() const { return fRenderingMode == DWRITE_RENDERING_MODE_GDI_CLASSIC; }
+ DWRITE_RENDERING_MODE GetRenderingMode() const { return fRenderingMode; }
protected:
void weak_dispose() const override {
@@ -156,7 +157,7 @@ protected:
private:
typedef SkTypeface INHERITED;
- bool fForceGDI;
+ DWRITE_RENDERING_MODE fRenderingMode;
float fGamma;
float fContrast;
};
diff --git a/gfx/thebes/gfxDWriteFonts.cpp b/gfx/thebes/gfxDWriteFonts.cpp
index 503d5216d070..a85230f3fd60 100644
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -646,11 +646,14 @@ already_AddRefed gfxDWriteFont::GetScaledFont(
: gfxWindowsPlatform::TEXT_RENDERING_NORMAL)
: gfxWindowsPlatform::TEXT_RENDERING_NO_CLEARTYPE);
+ DWRITE_RENDERING_MODE renderingMode =
+ forceGDI ? DWRITE_RENDERING_MODE_GDI_CLASSIC : params->GetRenderingMode();
+
const gfxFontStyle* fontStyle = GetStyle();
mAzureScaledFont = Factory::CreateScaledFontForDWriteFont(
mFontFace, fontStyle, GetUnscaledFont(), GetAdjustedSize(),
- useEmbeddedBitmap, forceGDI, params, params->GetGamma(),
- params->GetEnhancedContrast());
+ useEmbeddedBitmap, (int)renderingMode,
+ params, params->GetGamma(), params->GetEnhancedContrast());
if (!mAzureScaledFont) {
return nullptr;
}
diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp
index 67bc207ce873..7e21dbe03f58 100644
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1574,20 +1574,26 @@ class GlyphBufferAzure {
}
}
- // Ensure the buffer has enough space for aGlyphCount glyphs to be added.
+ // Ensure the buffer has enough space for aGlyphCount glyphs to be added,
+ // considering the supplied strike multipler aStrikeCount.
// This MUST be called before OutputGlyph is used to actually store glyph
// records in the buffer. It may be called repeated to add further capacity
// in case we don't know up-front exactly what will be needed.
- void AddCapacity(uint32_t aGlyphCount) {
+ void AddCapacity(uint32_t aGlyphCount, uint32_t aStrikeCount) {
+ // Calculate the new capacity and ensure it will fit within the maximum
+ // allowed capacity.
+ static const uint64_t kMaxCapacity = 64 * 1024;
+ mCapacity = uint32_t(std::min(
+ kMaxCapacity,
+ uint64_t(mCapacity) + uint64_t(aGlyphCount) * uint64_t(aStrikeCount)));
// See if the required capacity fits within the already-allocated space
- if (mCapacity + aGlyphCount <= mBufSize) {
- mCapacity += aGlyphCount;
+ if (mCapacity <= mBufSize) {
return;
}
// We need to grow the buffer: determine a new size, allocate, and
// copy the existing data over if we didn't use realloc (which would
// do it automatically).
- mBufSize = std::max(mCapacity + aGlyphCount, mBufSize * 2);
+ mBufSize = std::max(mCapacity, mBufSize * 2);
if (mBuffer == *mAutoBuffer.addr()) {
// switching from autobuffer to malloc, so we need to copy
mBuffer = reinterpret_cast(moz_xmalloc(mBufSize * sizeof(Glyph)));
@@ -1596,12 +1602,15 @@ class GlyphBufferAzure {
mBuffer = reinterpret_cast(
moz_xrealloc(mBuffer, mBufSize * sizeof(Glyph)));
}
- mCapacity += aGlyphCount;
}
void OutputGlyph(uint32_t aGlyphID, const gfx::Point& aPt) {
- // Check that AddCapacity has been used appropriately!
- MOZ_ASSERT(mNumGlyphs < mCapacity);
+ // If the buffer is full, flush to make room for the new glyph.
+ if (mNumGlyphs >= mCapacity) {
+ // Check that AddCapacity has been used appropriately!
+ MOZ_ASSERT(mCapacity > 0 && mNumGlyphs == mCapacity);
+ Flush();
+ }
Glyph* glyph = mBuffer + mNumGlyphs++;
glyph->mIndex = aGlyphID;
glyph->mPosition = aPt;
@@ -1805,7 +1814,7 @@ bool gfxFont::DrawGlyphs(const gfxShapedText* aShapedText,
// Allocate buffer space for the run, assuming all simple glyphs.
uint32_t capacityMult = 1 + aBuffer.mFontParams.extraStrikes;
- aBuffer.AddCapacity(capacityMult * aCount);
+ aBuffer.AddCapacity(aCount, capacityMult);
bool emittedGlyphs = false;
@@ -1825,7 +1834,7 @@ bool gfxFont::DrawGlyphs(const gfxShapedText* aShapedText,
uint32_t glyphCount = glyphData->GetGlyphCount();
if (glyphCount > 0) {
// Add extra buffer capacity to allow for multiple-glyph entry.
- aBuffer.AddCapacity(capacityMult * (glyphCount - 1));
+ aBuffer.AddCapacity(glyphCount - 1, capacityMult);
const gfxShapedText::DetailedGlyph* details =
aShapedText->GetDetailedGlyphs(aOffset + i);
MOZ_ASSERT(details, "missing DetailedGlyph!");
diff --git a/gfx/wr/webrender/res/brush_image.glsl b/gfx/wr/webrender/res/brush_image.glsl
index 32d65e0f76d6..ced1c17ca422 100644
--- a/gfx/wr/webrender/res/brush_image.glsl
+++ b/gfx/wr/webrender/res/brush_image.glsl
@@ -122,7 +122,15 @@ void brush_vs(
vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
+#ifdef WR_FEATURE_ALPHA_PASS
+ int color_mode = prim_user_data.x & 0xffff;
+ int blend_mode = prim_user_data.x >> 16;
int raster_space = prim_user_data.y;
+
+ if (color_mode == COLOR_MODE_FROM_PASS) {
+ color_mode = uMode;
+ }
+
// Derive the texture coordinates for this image, based on
// whether the source image is a local-space or screen-space
// image.
@@ -137,14 +145,6 @@ void brush_vs(
default:
break;
}
-
-#ifdef WR_FEATURE_ALPHA_PASS
- int color_mode = prim_user_data.x & 0xffff;
- int blend_mode = prim_user_data.x >> 16;
-
- if (color_mode == COLOR_MODE_FROM_PASS) {
- color_mode = uMode;
- }
#endif
// Offset and scale vUv here to avoid doing it in the fragment shader.
diff --git a/gfx/wr/webrender/src/batch.rs b/gfx/wr/webrender/src/batch.rs
index 0bb09c2cc311..cc48aba883c0 100644
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -1226,7 +1226,7 @@ impl BatchBuilder {
textures,
[
ShaderColorMode::Image as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
- RasterizationSpace::Screen as i32,
+ RasterizationSpace::Local as i32,
get_shader_opacity(1.0),
0,
],
diff --git a/gfx/wr/webrender/src/frame_builder.rs b/gfx/wr/webrender/src/frame_builder.rs
index a6e6a3cc4b8b..98a82bd074b7 100644
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -240,7 +240,7 @@ impl FrameBuilder {
retained_tiles: RetainedTiles,
globals: FrameGlobalResources,
) {
- debug_assert!(self.pending_retained_tiles.tiles.is_empty());
+ assert!(self.pending_retained_tiles.caches.is_empty());
self.pending_retained_tiles = retained_tiles;
self.globals = globals;
}
diff --git a/gfx/wr/webrender/src/picture.rs b/gfx/wr/webrender/src/picture.rs
index d54dd2958693..692caefe7756 100644
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -111,26 +111,35 @@ struct PictureInfo {
_spatial_node_index: SpatialNodeIndex,
}
+pub struct PictureCacheState {
+ /// The tiles retained by this picture cache.
+ pub tiles: FastHashMap,
+ /// The current fractional offset of the cache transform root.
+ fract_offset: PictureVector2D,
+}
+
/// Stores a list of cached picture tiles that are retained
/// between new scenes.
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct RetainedTiles {
/// The tiles retained between display lists.
#[cfg_attr(feature = "capture", serde(skip))] //TODO
- pub tiles: FastHashMap,
+ pub caches: FastHashMap,
}
impl RetainedTiles {
pub fn new() -> Self {
RetainedTiles {
- tiles: FastHashMap::default(),
+ caches: FastHashMap::default(),
}
}
/// Merge items from one retained tiles into another.
pub fn merge(&mut self, other: RetainedTiles) {
- assert!(self.tiles.is_empty() || other.tiles.is_empty());
- self.tiles.extend(other.tiles);
+ assert!(self.caches.is_empty() || other.caches.is_empty());
+ if self.caches.is_empty() {
+ self.caches = other.caches;
+ }
}
}
@@ -198,14 +207,6 @@ impl From> for OpacityBinding {
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct TileId(usize);
-#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq)]
-pub struct TileKey {
- /// The picture cache slice that this belongs to.
- slice: usize,
- /// Offset (in tile coords) of the tile within this slice.
- offset: TileOffset,
-}
-
/// Information about a cached tile.
#[derive(Debug)]
pub struct Tile {
@@ -504,7 +505,7 @@ pub struct TileCacheInstance {
/// The positioning node for this tile cache.
pub spatial_node_index: SpatialNodeIndex,
/// Hash of tiles present in this picture.
- pub tiles: FastHashMap,
+ pub tiles: FastHashMap,
/// A helper struct to map local rects into surface coords.
map_local_to_surface: SpaceMapper,
/// List of opacity bindings, with some extra information
@@ -525,7 +526,7 @@ pub struct TileCacheInstance {
/// Local clip rect for this tile cache.
pub local_clip_rect: PictureRect,
/// A list of tiles that are valid and visible, which should be drawn to the main scene.
- pub tiles_to_draw: Vec,
+ pub tiles_to_draw: Vec,
/// The world space viewport that this tile cache draws into.
/// Any clips outside this viewport can be ignored (and must be removed so that
/// we can draw outside the bounds of the viewport).
@@ -542,6 +543,10 @@ pub struct TileCacheInstance {
/// The allowed subpixel mode for this surface, which depends on the detected
/// opacity of the background.
pub subpixel_mode: SubpixelMode,
+ /// The current fractional offset of the cache transform root. If this changes,
+ /// all tiles need to be invalidated and redrawn, since snapping differences are
+ /// likely to occur.
+ fract_offset: PictureVector2D,
}
impl TileCacheInstance {
@@ -572,6 +577,7 @@ impl TileCacheInstance {
background_color,
opaque_rect: PictureRect::zero(),
subpixel_mode: SubpixelMode::Allow,
+ fract_offset: PictureVector2D::zero(),
}
}
@@ -639,6 +645,53 @@ impl TileCacheInstance {
frame_context.clip_scroll_tree,
);
+ // If there are pending retained state, retrieve it.
+ if let Some(prev_state) = frame_state.retained_tiles.caches.remove(&self.slice) {
+ self.tiles.extend(prev_state.tiles);
+ self.fract_offset = prev_state.fract_offset;
+ }
+
+ // Map an arbitrary point in picture space to world space, to work out
+ // what the fractional translation is that's applied by this scroll root.
+ // TODO(gw): I'm not 100% sure this is right. At least, in future, we should
+ // make a specific API for this, and/or enforce that the picture
+ // cache transform only includes scale and/or translation (we
+ // already ensure it doesn't have perspective).
+ let world_origin = pic_to_world_mapper
+ .map(&PictureRect::new(PicturePoint::zero(), PictureSize::new(1.0, 1.0)))
+ .expect("bug: unable to map origin to world space")
+ .origin;
+
+ // Get the desired integer device coordinate
+ let device_origin = world_origin * frame_context.global_device_pixel_scale;
+ let desired_device_origin = device_origin.round();
+
+ // Unmap from device space to world space rect
+ let ref_world_rect = WorldRect::new(
+ desired_device_origin / frame_context.global_device_pixel_scale,
+ WorldSize::new(1.0, 1.0),
+ );
+
+ // Unmap from world space to picture space
+ let ref_point = pic_to_world_mapper
+ .unmap(&ref_world_rect)
+ .expect("bug: unable to unmap ref world rect")
+ .origin;
+
+ // Extract the fractional offset required in picture space to align in device space
+ let fract_offset = PictureVector2D::new(
+ ref_point.x.fract(),
+ ref_point.y.fract(),
+ );
+
+ // Determine if the fractional offset of the transform is different this frame
+ // from the currently cached tile set.
+ let fract_changed = (self.fract_offset.x - fract_offset.x).abs() > 0.001 ||
+ (self.fract_offset.y - fract_offset.y).abs() > 0.001;
+ if fract_changed {
+ self.fract_offset = fract_offset;
+ }
+
let spatial_node = &frame_context
.clip_scroll_tree
.spatial_nodes[self.spatial_node_index.0 as usize];
@@ -751,31 +804,16 @@ impl TileCacheInstance {
self.tile_bounds_p0 = TileOffset::new(x0, y0);
self.tile_bounds_p1 = TileOffset::new(x1, y1);
- // TODO(gw): Tidy this up as we add better support for retaining
- // slices and sub-grid dirty areas.
- let mut keys = Vec::new();
- for key in frame_state.retained_tiles.tiles.keys() {
- if key.slice == self.slice {
- keys.push(*key);
- }
- }
- for key in keys {
- self.tiles.insert(key, frame_state.retained_tiles.tiles.remove(&key).unwrap());
- }
+ let mut world_culling_rect = WorldRect::zero();
let mut old_tiles = mem::replace(
&mut self.tiles,
FastHashMap::default(),
);
- let mut world_culling_rect = WorldRect::zero();
-
for y in y0 .. y1 {
for x in x0 .. x1 {
- let key = TileKey {
- offset: TileOffset::new(x, y),
- slice: self.slice,
- };
+ let key = TileOffset::new(x, y);
let mut tile = old_tiles
.remove(&key)
@@ -784,10 +822,13 @@ impl TileCacheInstance {
Tile::new(next_id)
});
+ // Ensure each tile is offset by the appropriate amount from the
+ // origin, such that the content origin will be a whole number and
+ // the snapping will be consistent.
tile.rect = PictureRect::new(
PicturePoint::new(
- x as f32 * self.tile_size.width,
- y as f32 * self.tile_size.height,
+ x as f32 * self.tile_size.width + fract_offset.x,
+ y as f32 * self.tile_size.height + fract_offset.y,
),
self.tile_size,
);
@@ -808,8 +849,9 @@ impl TileCacheInstance {
// Do tile invalidation for any dependencies that we know now.
for (_, tile) in &mut self.tiles {
- // Start frame assuming that the tile has the same content.
- tile.is_same_content = true;
+ // Start frame assuming that the tile has the same content,
+ // unless the fractional offset of the transform root changed.
+ tile.is_same_content = !fract_changed;
// Content has changed if any opacity bindings changed.
for binding in tile.descriptor.opacity_bindings.items() {
@@ -1043,10 +1085,7 @@ impl TileCacheInstance {
for y in p0.y .. p1.y {
for x in p0.x .. p1.x {
// TODO(gw): Convert to 2d array temporarily to avoid hash lookups per-tile?
- let key = TileKey {
- slice: self.slice,
- offset: TileOffset::new(x, y),
- };
+ let key = TileOffset::new(x, y);
let tile = self.tiles.get_mut(&key).expect("bug: no tile");
// Mark if the tile is cacheable at all.
@@ -1239,29 +1278,9 @@ impl TileCacheInstance {
TILE_SIZE_WIDTH,
TILE_SIZE_HEIGHT,
);
-
- let content_origin_f = tile.world_rect.origin * frame_context.global_device_pixel_scale;
- let content_origin_i = content_origin_f.floor();
-
- // Calculate the UV coords for this tile. These are generally 0-1, but if the
- // local rect of the cache has fractional coordinates, then the content origin
- // of the tile is floor'ed, and so we need to adjust the UV rect in order to
- // ensure a correct 1:1 texel:pixel mapping and correct snapping.
- let s0 = (content_origin_f.x - content_origin_i.x) / tile.world_rect.size.width;
- let t0 = (content_origin_f.y - content_origin_i.y) / tile.world_rect.size.height;
- let s1 = 1.0;
- let t1 = 1.0;
-
- let uv_rect_kind = UvRectKind::Quad {
- top_left: DeviceHomogeneousVector::new(s0, t0, 0.0, 1.0),
- top_right: DeviceHomogeneousVector::new(s1, t0, 0.0, 1.0),
- bottom_left: DeviceHomogeneousVector::new(s0, t1, 0.0, 1.0),
- bottom_right: DeviceHomogeneousVector::new(s1, t1, 0.0, 1.0),
- };
resource_cache.texture_cache.update_picture_cache(
tile_size,
&mut tile.handle,
- uv_rect_kind,
gpu_cache,
);
}
@@ -2048,7 +2067,15 @@ impl PicturePrimitive {
retained_tiles: &mut RetainedTiles,
) {
if let Some(tile_cache) = self.tile_cache.take() {
- retained_tiles.tiles.extend(tile_cache.tiles);
+ if !tile_cache.tiles.is_empty() {
+ retained_tiles.caches.insert(
+ tile_cache.slice,
+ PictureCacheState {
+ tiles: tile_cache.tiles,
+ fract_offset: tile_cache.fract_offset,
+ },
+ );
+ }
}
}
@@ -2434,10 +2461,10 @@ impl PicturePrimitive {
continue;
}
- // The content origin for surfaces is always an integer value (this preserves
- // the same snapping on a surface as would occur if drawn directly to parent).
let content_origin_f = tile.world_rect.origin * device_pixel_scale;
- let content_origin = content_origin_f.floor().to_i32();
+ let content_origin = content_origin_f.round();
+ debug_assert!((content_origin_f.x - content_origin.x).abs() < 0.01);
+ debug_assert!((content_origin_f.y - content_origin.y).abs() < 0.01);
let cache_item = frame_state.resource_cache.texture_cache.get(&tile.handle);
@@ -2449,7 +2476,7 @@ impl PicturePrimitive {
},
tile_size,
pic_index,
- content_origin,
+ content_origin.to_i32(),
UvRectKind::Rect,
surface_spatial_node_index,
device_pixel_scale,
diff --git a/gfx/wr/webrender/src/platform/windows/font.rs b/gfx/wr/webrender/src/platform/windows/font.rs
index 5702a8fa79dc..def44ee97674 100644
--- a/gfx/wr/webrender/src/platform/windows/font.rs
+++ b/gfx/wr/webrender/src/platform/windows/font.rs
@@ -113,6 +113,10 @@ fn dwrite_render_mode(
FontRenderMode::Alpha | FontRenderMode::Subpixel => {
if bitmaps || font.flags.contains(FontInstanceFlags::FORCE_GDI) {
dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC
+ } else if font.flags.contains(FontInstanceFlags::FORCE_SYMMETRIC) {
+ dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC
+ } else if font.flags.contains(FontInstanceFlags::NO_SYMMETRIC) {
+ dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL
} else {
font_face.get_recommended_rendering_mode_default_params(em_size, 1.0, measure_mode)
}
diff --git a/gfx/wr/webrender/src/texture_cache.rs b/gfx/wr/webrender/src/texture_cache.rs
index d448cabae1e9..de96445f3bf4 100644
--- a/gfx/wr/webrender/src/texture_cache.rs
+++ b/gfx/wr/webrender/src/texture_cache.rs
@@ -1316,7 +1316,6 @@ impl TextureCache {
&mut self,
tile_size: DeviceIntSize,
handle: &mut TextureCacheHandle,
- uv_rect_kind: UvRectKind,
gpu_cache: &mut GpuCache,
) {
debug_assert!(self.now.is_valid());
@@ -1348,11 +1347,10 @@ impl TextureCache {
}
// Upload the resource rect and texture array layer.
- let entry = self.entries
+ self.entries
.get_opt_mut(handle)
- .expect("BUG: handle must be valid now");
- entry.uv_rect_kind = uv_rect_kind;
- entry.update_gpu_cache(gpu_cache);
+ .expect("BUG: handle must be valid now")
+ .update_gpu_cache(gpu_cache);
}
}
diff --git a/gfx/wr/webrender_api/src/font.rs b/gfx/wr/webrender_api/src/font.rs
index fa099baa71c1..528e1d327741 100644
--- a/gfx/wr/webrender_api/src/font.rs
+++ b/gfx/wr/webrender_api/src/font.rs
@@ -182,6 +182,8 @@ bitflags! {
// Windows flags
const FORCE_GDI = 1 << 16;
+ const FORCE_SYMMETRIC = 1 << 17;
+ const NO_SYMMETRIC = 1 << 18;
// Mac flags
const FONT_SMOOTHING = 1 << 16;
diff --git a/gfx/wr/wrench/src/rawtest.rs b/gfx/wr/wrench/src/rawtest.rs
index 44a82d9622c5..cda68fee5aa6 100644
--- a/gfx/wr/wrench/src/rawtest.rs
+++ b/gfx/wr/wrench/src/rawtest.rs
@@ -2,14 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use crate::{WindowWrapper, NotifierEvent};
-use crate::blob;
use euclid::{point2, size2, rect};
use std::sync::Arc;
use std::sync::atomic::{AtomicIsize, Ordering};
use std::sync::mpsc::Receiver;
use webrender::api::*;
use webrender::api::units::*;
+use crate::{WindowWrapper, NotifierEvent};
+use crate::blob;
+use crate::reftest::{ReftestImage, ReftestImageComparison};
use crate::wrench::Wrench;
pub struct RawtestHarness<'a> {
@@ -53,6 +54,41 @@ impl<'a> RawtestHarness<'a> {
self.wrench.renderer.read_pixels_rgba8(window_rect)
}
+ fn compare_pixels(&self, reference: Vec, test: Vec, size: FramebufferIntSize) {
+ let size = DeviceIntSize::new(size.width, size.height);
+ let reference = ReftestImage {
+ data: reference,
+ size,
+ };
+ let test = ReftestImage {
+ data: test,
+ size,
+ };
+
+ match reference.compare(&test) {
+ ReftestImageComparison::Equal => {}
+ ReftestImageComparison::NotEqual { max_difference, count_different } => {
+ let t = "rawtest";
+ println!(
+ "{} | {} | {}: {}, {}: {}",
+ "REFTEST TEST-UNEXPECTED-FAIL",
+ t,
+ "image comparison, max difference",
+ max_difference,
+ "number of differing pixels",
+ count_different
+ );
+ println!("REFTEST IMAGE 1 (TEST): {}", test.create_data_uri());
+ println!(
+ "REFTEST IMAGE 2 (REFERENCE): {}",
+ reference.create_data_uri()
+ );
+ println!("REFTEST TEST-END | {}", t);
+ panic!();
+ }
+ }
+ }
+
fn submit_dl(
&mut self,
epoch: &mut Epoch,
@@ -574,7 +610,7 @@ impl<'a> RawtestHarness<'a> {
let pixels = self.render_and_get_pixels(window_rect);
- assert!(pixels == original_pixels);
+ self.compare_pixels(original_pixels, pixels, window_rect.size);
// Leaving a tiled blob image in the resource cache
// confuses the `test_capture`. TODO: remove this
@@ -667,7 +703,6 @@ impl<'a> RawtestHarness<'a> {
// use png;
// png::save_flipped("out1.png", &pixels_first, window_rect.size);
// png::save_flipped("out2.png", &pixels_second, window_rect.size);
-
assert!(pixels_first != pixels_second);
// cleanup
@@ -905,8 +940,8 @@ impl<'a> RawtestHarness<'a> {
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let pixels_third = self.render_and_get_pixels(window_rect);
- assert!(pixels_first == pixels_second);
assert!(pixels_first != pixels_third);
+ self.compare_pixels(pixels_first, pixels_second, window_rect.size);
}
// Ensures that content doing a save-restore produces the same results as not
@@ -1012,7 +1047,7 @@ impl<'a> RawtestHarness<'a> {
let first = do_test(false);
let second = do_test(true);
- assert_eq!(first, second);
+ self.compare_pixels(first, second, window_rect.size);
}
// regression test for #2769
@@ -1141,7 +1176,7 @@ impl<'a> RawtestHarness<'a> {
// 5. render the built frame and compare
let pixels1 = self.render_and_get_pixels(window_rect);
- assert!(pixels0 == pixels1);
+ self.compare_pixels(pixels0.clone(), pixels1, window_rect.size);
// 6. rebuild the scene and compare again
let mut txn = Transaction::new();
@@ -1149,7 +1184,7 @@ impl<'a> RawtestHarness<'a> {
txn.generate_frame();
self.wrench.api.send_transaction(captured.document_id, txn);
let pixels2 = self.render_and_get_pixels(window_rect);
- assert!(pixels0 == pixels2);
+ self.compare_pixels(pixels0, pixels2, window_rect.size);
}
fn test_zero_height_window(&mut self) {
diff --git a/gfx/wr/wrench/src/reftest.rs b/gfx/wr/wrench/src/reftest.rs
index aa1b1ccbaf55..c9c81f1f1744 100644
--- a/gfx/wr/wrench/src/reftest.rs
+++ b/gfx/wr/wrench/src/reftest.rs
@@ -114,11 +114,11 @@ impl Display for Reftest {
}
}
-struct ReftestImage {
- data: Vec,
- size: DeviceIntSize,
+pub struct ReftestImage {
+ pub data: Vec,
+ pub size: DeviceIntSize,
}
-enum ReftestImageComparison {
+pub enum ReftestImageComparison {
Equal,
NotEqual {
max_difference: usize,
@@ -127,7 +127,7 @@ enum ReftestImageComparison {
}
impl ReftestImage {
- fn compare(&self, other: &ReftestImage) -> ReftestImageComparison {
+ pub fn compare(&self, other: &ReftestImage) -> ReftestImageComparison {
assert_eq!(self.size, other.size);
assert_eq!(self.data.len(), other.data.len());
assert_eq!(self.data.len() % 4, 0);
@@ -158,7 +158,7 @@ impl ReftestImage {
}
}
- fn create_data_uri(mut self) -> String {
+ pub fn create_data_uri(mut self) -> String {
let width = self.size.width;
let height = self.size.height;
diff --git a/js/src/frontend/BinASTParserPerTokenizer.cpp b/js/src/frontend/BinASTParserPerTokenizer.cpp
index 88ed8d01c79a..d56db12d0154 100644
--- a/js/src/frontend/BinASTParserPerTokenizer.cpp
+++ b/js/src/frontend/BinASTParserPerTokenizer.cpp
@@ -395,7 +395,7 @@ JS::Result BinASTParserPerTokenizer::finishLazyFunction(
if (funbox->strict()) {
lazy->setStrict();
}
- lazy->setIsBinAST();
+ MOZ_ASSERT(lazy->isBinAST());
fun->initLazyScript(lazy);
return Ok();
diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h
index 257e360221c4..eba2789c4348 100644
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -2070,9 +2070,7 @@ class JSScript : public js::gc::TenuredCell {
// Whether the Parser declared 'arguments'.
ShouldDeclareArguments = 1 << 25,
- // Whether source is BinAST.
- // FIXME: JSScript should also set this flag correctly.
- IsBinAST = 1 << 26,
+ // (1 << 26) is unused.
// Whether this script contains a direct eval statement.
HasDirectEval = 1 << 27,
@@ -3522,8 +3520,7 @@ class LazyScript : public gc::TenuredCell {
return frontend::ParseGoal::Script;
}
- bool isBinAST() const { return hasFlag(ImmutableFlags::IsBinAST); }
- void setIsBinAST() { setFlag(ImmutableFlags::IsBinAST); }
+ bool isBinAST() const { return scriptSource()->hasBinASTSource(); }
bool strict() const { return hasFlag(ImmutableFlags::Strict); }
void setStrict() { setFlag(ImmutableFlags::Strict); }
diff --git a/mobile/android/config/mozconfigs/android-aarch64/beta b/mobile/android/config/mozconfigs/android-aarch64/beta
index eb101ce532eb..e0b3820990f0 100644
--- a/mobile/android/config/mozconfigs/android-aarch64/beta
+++ b/mobile/android/config/mozconfigs/android-aarch64/beta
@@ -10,7 +10,7 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
-export MOZ_LTO=1
+export MOZ_LTO=cross
export MOZILLA_OFFICIAL=1
export MOZ_ANDROID_POCKET=1
diff --git a/mobile/android/config/mozconfigs/android-aarch64/nightly b/mobile/android/config/mozconfigs/android-aarch64/nightly
index b8480fca91b0..c27a787bcc65 100644
--- a/mobile/android/config/mozconfigs/android-aarch64/nightly
+++ b/mobile/android/config/mozconfigs/android-aarch64/nightly
@@ -12,7 +12,7 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
-export MOZ_LTO=1
+export MOZ_LTO=cross
export MOZILLA_OFFICIAL=1
export MOZ_ANDROID_POCKET=1
diff --git a/mobile/android/config/mozconfigs/android-api-16/beta b/mobile/android/config/mozconfigs/android-api-16/beta
index 8a9e79561d07..333cabb95705 100644
--- a/mobile/android/config/mozconfigs/android-api-16/beta
+++ b/mobile/android/config/mozconfigs/android-api-16/beta
@@ -19,6 +19,6 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
-export MOZ_LTO=1
+export MOZ_LTO=cross
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
diff --git a/mobile/android/config/mozconfigs/android-api-16/nightly b/mobile/android/config/mozconfigs/android-api-16/nightly
index fa159af0dcff..39fe467ff7d5 100644
--- a/mobile/android/config/mozconfigs/android-api-16/nightly
+++ b/mobile/android/config/mozconfigs/android-api-16/nightly
@@ -21,6 +21,6 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
-export MOZ_LTO=1
+export MOZ_LTO=cross
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
diff --git a/mobile/android/config/mozconfigs/android-x86/beta b/mobile/android/config/mozconfigs/android-x86/beta
index 7850da2e7089..74a7f1c75e9d 100644
--- a/mobile/android/config/mozconfigs/android-x86/beta
+++ b/mobile/android/config/mozconfigs/android-x86/beta
@@ -17,6 +17,6 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
-export MOZ_LTO=1
+export MOZ_LTO=cross
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
diff --git a/mobile/android/config/mozconfigs/android-x86/nightly b/mobile/android/config/mozconfigs/android-x86/nightly
index 1eb75923f1c5..7c99617b9862 100644
--- a/mobile/android/config/mozconfigs/android-x86/nightly
+++ b/mobile/android/config/mozconfigs/android-x86/nightly
@@ -19,6 +19,6 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
-export MOZ_LTO=1
+export MOZ_LTO=cross
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
diff --git a/mobile/android/config/mozconfigs/android-x86_64/beta b/mobile/android/config/mozconfigs/android-x86_64/beta
index 684d80888c6e..48dce09118e7 100644
--- a/mobile/android/config/mozconfigs/android-x86_64/beta
+++ b/mobile/android/config/mozconfigs/android-x86_64/beta
@@ -10,7 +10,7 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
-export MOZ_LTO=1
+export MOZ_LTO=cross
export MOZILLA_OFFICIAL=1
export MOZ_ANDROID_POCKET=1
diff --git a/mobile/android/config/mozconfigs/android-x86_64/nightly b/mobile/android/config/mozconfigs/android-x86_64/nightly
index 0d023983f587..a8a26c57b575 100644
--- a/mobile/android/config/mozconfigs/android-x86_64/nightly
+++ b/mobile/android/config/mozconfigs/android-x86_64/nightly
@@ -12,7 +12,7 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
-export MOZ_LTO=1
+export MOZ_LTO=cross
export MOZILLA_OFFICIAL=1
export MOZ_ANDROID_POCKET=1
diff --git a/taskcluster/ci/docker-image/kind.yml b/taskcluster/ci/docker-image/kind.yml
index 15a486176f2b..07b13b6119b9 100644
--- a/taskcluster/ci/docker-image/kind.yml
+++ b/taskcluster/ci/docker-image/kind.yml
@@ -75,8 +75,6 @@ jobs:
symbol: I(deb7-bb)
parent: debian7-base
definition: debian-build
- packages:
- - deb7-valgrind
args:
ARCH: amd64
debian7-amd64-build:
diff --git a/taskcluster/ci/packages/kind.yml b/taskcluster/ci/packages/kind.yml
index 8c032ccbba0d..94a424300a10 100644
--- a/taskcluster/ci/packages/kind.yml
+++ b/taskcluster/ci/packages/kind.yml
@@ -192,8 +192,8 @@ jobs:
run:
using: debian-package
dsc:
- url: http://snapshot.debian.org/archive/debian/20181115T045552Z/pool/main/v/valgrind/valgrind_3.14.0-1.dsc
- sha256: 6709e2fe4e8251ee32f3cfbf2c6ee106a5cfa3e8dc672cf1dd5f2b26e72b64ee
+ url: http://snapshot.debian.org/archive/debian/20190714T155055Z/pool/main/v/valgrind/valgrind_3.15.0-1.dsc
+ sha256: 2bd467ec486302060ed4e91452231b2c0a09afcd51c25d4ba8d3028218328681
patch: valgrind-wheezy.diff
deb7-dh-python:
diff --git a/testing/web-platform/meta/css/css-backgrounds/parsing/background-position-x-valid.html.ini b/testing/web-platform/meta/css/css-backgrounds/parsing/background-position-x-valid.html.ini
index 00376a56fce8..566b507fd81d 100644
--- a/testing/web-platform/meta/css/css-backgrounds/parsing/background-position-x-valid.html.ini
+++ b/testing/web-platform/meta/css/css-backgrounds/parsing/background-position-x-valid.html.ini
@@ -1,10 +1,4 @@
[background-position-x-valid.html]
- [e.style['background-position-x'\] = "calc(10px - 0.5em)" should set the property value]
- expected: FAIL
-
- [e.style['background-position-x'\] = "calc(10px - 0.5em), left -20%, right 10px" should set the property value]
- expected: FAIL
-
[e.style['background-position-x'\] = "x-end" should set the property value]
expected: FAIL
diff --git a/testing/web-platform/meta/css/css-backgrounds/parsing/background-position-y-valid.html.ini b/testing/web-platform/meta/css/css-backgrounds/parsing/background-position-y-valid.html.ini
index 429c6adbac5e..f2a01f43d9a8 100644
--- a/testing/web-platform/meta/css/css-backgrounds/parsing/background-position-y-valid.html.ini
+++ b/testing/web-platform/meta/css/css-backgrounds/parsing/background-position-y-valid.html.ini
@@ -11,6 +11,3 @@
[e.style['background-position-y'\] = "y-end" should set the property value]
expected: FAIL
- [e.style['background-position-y'\] = "calc(10px - 0.5em)" should set the property value]
- expected: FAIL
-
diff --git a/testing/web-platform/meta/css/css-logical/parsing/border-block-valid.html.ini b/testing/web-platform/meta/css/css-logical/parsing/border-block-valid.html.ini
deleted file mode 100644
index b6145021de72..000000000000
--- a/testing/web-platform/meta/css/css-logical/parsing/border-block-valid.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[border-block-valid.html]
- [e.style['border-block-end'\] = "calc(10px - 0.5em) dotted red" should set the property value]
- expected: FAIL
-
diff --git a/testing/web-platform/meta/css/css-logical/parsing/border-block-width-valid.html.ini b/testing/web-platform/meta/css/css-logical/parsing/border-block-width-valid.html.ini
deleted file mode 100644
index 3eac634e4b73..000000000000
--- a/testing/web-platform/meta/css/css-logical/parsing/border-block-width-valid.html.ini
+++ /dev/null
@@ -1,10 +0,0 @@
-[border-block-width-valid.html]
- [e.style['border-block-end-width'\] = "calc(10px - 0.5em)" should set the property value]
- expected: FAIL
-
- [e.style['border-block-start-width'\] = "calc(10px + 0.5em)" should set the property value]
- expected: FAIL
-
- [e.style['border-block-width'\] = "medium calc(10px + 0.5em)" should set the property value]
- expected: FAIL
-
diff --git a/testing/web-platform/meta/css/css-logical/parsing/border-inline-valid.html.ini b/testing/web-platform/meta/css/css-logical/parsing/border-inline-valid.html.ini
deleted file mode 100644
index 02fa57ffba16..000000000000
--- a/testing/web-platform/meta/css/css-logical/parsing/border-inline-valid.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[border-inline-valid.html]
- [e.style['border-inline-end'\] = "calc(10px - 0.5em) dotted red" should set the property value]
- expected: FAIL
-
diff --git a/testing/web-platform/meta/css/css-logical/parsing/border-inline-width-valid.html.ini b/testing/web-platform/meta/css/css-logical/parsing/border-inline-width-valid.html.ini
deleted file mode 100644
index e6781a16a557..000000000000
--- a/testing/web-platform/meta/css/css-logical/parsing/border-inline-width-valid.html.ini
+++ /dev/null
@@ -1,10 +0,0 @@
-[border-inline-width-valid.html]
- [e.style['border-inline-width'\] = "medium calc(10px + 0.5em)" should set the property value]
- expected: FAIL
-
- [e.style['border-inline-end-width'\] = "calc(10px - 0.5em)" should set the property value]
- expected: FAIL
-
- [e.style['border-inline-start-width'\] = "calc(10px + 0.5em)" should set the property value]
- expected: FAIL
-
diff --git a/testing/web-platform/meta/css/css-logical/parsing/inset-block-inline-valid.html.ini b/testing/web-platform/meta/css/css-logical/parsing/inset-block-inline-valid.html.ini
deleted file mode 100644
index 18b6e10bea3a..000000000000
--- a/testing/web-platform/meta/css/css-logical/parsing/inset-block-inline-valid.html.ini
+++ /dev/null
@@ -1,13 +0,0 @@
-[inset-block-inline-valid.html]
- [e.style['inset-block'\] = "calc(10px - 0.5em) -20%" should set the property value]
- expected: FAIL
-
- [e.style['inset-inline'\] = "calc(10px - 0.5em)" should set the property value]
- expected: FAIL
-
- [e.style['inset-inline-end'\] = "calc(10px - 0.5em)" should set the property value]
- expected: FAIL
-
- [e.style['inset-inline'\] = "auto calc(10px + 0.5em)" should set the property value]
- expected: FAIL
-
diff --git a/testing/web-platform/meta/css/css-tables/parsing/border-spacing-valid.html.ini b/testing/web-platform/meta/css/css-tables/parsing/border-spacing-valid.html.ini
deleted file mode 100644
index 9dfc328541da..000000000000
--- a/testing/web-platform/meta/css/css-tables/parsing/border-spacing-valid.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[border-spacing-valid.html]
- [e.style['border-spacing'\] = "calc(10px + 0.5em) calc(10px - 0.5em)" should set the property value]
- expected: FAIL
-
diff --git a/testing/web-platform/tests/css/css-backgrounds/parsing/background-position-x-valid.html b/testing/web-platform/tests/css/css-backgrounds/parsing/background-position-x-valid.html
index 282d7d27bf55..ca9c229db4a8 100644
--- a/testing/web-platform/tests/css/css-backgrounds/parsing/background-position-x-valid.html
+++ b/testing/web-platform/tests/css/css-backgrounds/parsing/background-position-x-valid.html
@@ -19,13 +19,13 @@ test_valid_value("background-position-x", "x-end");
test_valid_value("background-position-x", "-20%");
test_valid_value("background-position-x", "10px");
test_valid_value("background-position-x", "0.5em");
-test_valid_value("background-position-x", "calc(10px - 0.5em)");
+test_valid_value("background-position-x", "calc(10px - 0.5em)", "calc(-0.5em + 10px)");
test_valid_value("background-position-x", "left -20%");
test_valid_value("background-position-x", "right 10px");
test_valid_value("background-position-x", "-20%, 10px");
test_valid_value("background-position-x", "center, left, right");
test_valid_value("background-position-x", "0.5em, x-start, x-end");
-test_valid_value("background-position-x", "calc(10px - 0.5em), left -20%, right 10px");
+test_valid_value("background-position-x", "calc(10px - 0.5em), left -20%, right 10px", "calc(-0.5em + 10px), left -20%, right 10px");