зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound. CLOSED TREE
This commit is contained in:
Коммит
b11f89739a
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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%;
|
||||
|
|
|
@ -401,7 +401,7 @@
|
|||
|
||||
.inline-onboarding {
|
||||
&.activity-stream.welcome {
|
||||
overflow-y: scroll;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.modalOverlayInner {
|
||||
|
|
|
@ -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 {
|
|||
</div>
|
||||
</a>
|
||||
{!props.placeholder && (
|
||||
<button
|
||||
aria-haspopup="true"
|
||||
data-l10n-id="newtab-menu-content-tooltip"
|
||||
data-l10n-args={JSON.stringify({ title })}
|
||||
className="context-menu-button icon"
|
||||
onClick={this.onMenuButtonClick}
|
||||
/>
|
||||
)}
|
||||
{isContextMenuOpen && (
|
||||
<ContextMenuButton
|
||||
tooltip="newtab-menu-content-tooltip"
|
||||
tooltipArgs={{ title }}
|
||||
onUpdate={this.onMenuButtonUpdate}
|
||||
>
|
||||
<LinkMenu
|
||||
dispatch={dispatch}
|
||||
index={index}
|
||||
source={eventSource}
|
||||
onUpdate={this.onMenuUpdate}
|
||||
options={link.contextMenuOptions || contextMenuOptions}
|
||||
site={link}
|
||||
siteInfo={this._getTelemetryInfo()}
|
||||
shouldSendImpressionStats={shouldSendImpressionStats}
|
||||
/>
|
||||
</ContextMenuButton>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
|
|
|
@ -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 {
|
|||
</span>
|
||||
</h3>
|
||||
<div>
|
||||
<button
|
||||
aria-haspopup="true"
|
||||
className="context-menu-button icon"
|
||||
data-l10n-id="newtab-menu-section-tooltip"
|
||||
onClick={this.onMenuButtonClick}
|
||||
ref={this.setContextMenuButtonRef}
|
||||
/>
|
||||
{showContextMenu && (
|
||||
<ContextMenuButton
|
||||
tooltip="newtab-menu-section-tooltip"
|
||||
onUpdate={this.onMenuUpdate}
|
||||
refFunction={this.setContextMenuButtonRef}
|
||||
>
|
||||
<SectionMenu
|
||||
id={id}
|
||||
extraOptions={extraMenuOptions}
|
||||
|
@ -273,14 +265,13 @@ export class CollapsibleSection extends React.PureComponent {
|
|||
showPrefName={showPrefName}
|
||||
privacyNoticeURL={privacyNoticeURL}
|
||||
collapsed={collapsed}
|
||||
onUpdate={this.onMenuUpdate}
|
||||
isFixed={isFixed}
|
||||
isFirst={isFirst}
|
||||
isLast={isLast}
|
||||
dispatch={dispatch}
|
||||
isWebExtension={isWebExtension}
|
||||
/>
|
||||
)}
|
||||
</ContextMenuButton>
|
||||
</div>
|
||||
</div>
|
||||
<ErrorBoundary className="section-body-fallback">
|
||||
|
|
|
@ -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 && (
|
||||
<span className={`icon icon-spacer icon-${option.icon}`} />
|
||||
|
|
|
@ -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 (
|
||||
<React.Fragment>
|
||||
<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.cloneElement(children, {
|
||||
keyboardAccess: contextMenuKeyboard,
|
||||
onUpdate: this.onUpdate,
|
||||
})
|
||||
: null}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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 (
|
||||
<div>
|
||||
<button
|
||||
ref={this.contextMenuButtonRef}
|
||||
aria-haspopup="true"
|
||||
className="context-menu-button icon"
|
||||
data-l10n-id="newtab-menu-content-tooltip"
|
||||
data-l10n-args={JSON.stringify({ title })}
|
||||
onClick={this.onMenuButtonClick}
|
||||
/>
|
||||
{isContextMenuOpen && (
|
||||
<ContextMenuButton
|
||||
refFunction={this.contextMenuButtonRef}
|
||||
tooltip={"newtab-menu-content-tooltip"}
|
||||
tooltipArgs={{ title }}
|
||||
onUpdate={this.onMenuUpdate}
|
||||
>
|
||||
<LinkMenu
|
||||
dispatch={dispatch}
|
||||
index={index}
|
||||
source={type.toUpperCase()}
|
||||
onUpdate={this.onMenuUpdate}
|
||||
onShow={this.onMenuShow}
|
||||
options={TOP_STORIES_CONTEXT_MENU_OPTIONS}
|
||||
shouldSendImpressionStats={true}
|
||||
|
@ -95,7 +76,7 @@ export class DSLinkMenu extends React.PureComponent {
|
|||
bookmarkGuid: this.props.bookmarkGuid,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ContextMenuButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
<ContextMenu onUpdate={this.props.onUpdate} options={this.getOptions()} />
|
||||
<ContextMenu
|
||||
onUpdate={this.props.onUpdate}
|
||||
options={this.getOptions()}
|
||||
keyboardAccess={this.props.keyboardAccess}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
onMenuUpdate(isOpen) {
|
||||
if (isOpen) {
|
||||
this.props.onActivate(this.props.index);
|
||||
this.setState({ showContextMenu: true });
|
||||
} else {
|
||||
this.props.onActivate();
|
||||
}
|
||||
|
||||
onMenuUpdate(showContextMenu) {
|
||||
this.setState({ showContextMenu });
|
||||
}
|
||||
|
||||
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 (
|
||||
<TopSiteLink
|
||||
|
@ -362,14 +359,11 @@ export class TopSite extends React.PureComponent {
|
|||
title={title}
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
aria-haspopup="true"
|
||||
className="context-menu-button icon"
|
||||
data-l10n-id="newtab-menu-content-tooltip"
|
||||
data-l10n-args={JSON.stringify({ title })}
|
||||
onClick={this.onMenuButtonClick}
|
||||
/>
|
||||
{isContextMenuOpen && (
|
||||
<ContextMenuButton
|
||||
tooltip="newtab-menu-content-tooltip"
|
||||
tooltipArgs={{ title }}
|
||||
onUpdate={this.onMenuUpdate}
|
||||
>
|
||||
<LinkMenu
|
||||
dispatch={props.dispatch}
|
||||
index={props.index}
|
||||
|
@ -383,7 +377,7 @@ export class TopSite extends React.PureComponent {
|
|||
siteInfo={this._getTelemetryInfo()}
|
||||
source={TOP_SITES_SOURCE}
|
||||
/>
|
||||
)}
|
||||
</ContextMenuButton>
|
||||
</div>
|
||||
</TopSiteLink>
|
||||
);
|
||||
|
|
|
@ -30,8 +30,10 @@ export const selectLayoutRender = (state, prefs, rickRollCache) => {
|
|||
|
||||
if (rickRoll <= spocsConfig.probability) {
|
||||
spocIndex++;
|
||||
if (!spocs.blocked.includes(spoc.url)) {
|
||||
recommendations.splice(position.index, 0, spoc);
|
||||
chosenSpocs.add(spoc);
|
||||
}
|
||||
} else {
|
||||
unchosenSpocs.add(spoc);
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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"],
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,22 +77,26 @@ class _ToolbarPanelHub {
|
|||
}
|
||||
|
||||
// Turns on the Appmenu (hamburger menu) button for all open windows and future windows.
|
||||
enableAppmenuButton() {
|
||||
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() {
|
||||
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
|
||||
_onPanelHidden(win) {
|
||||
|
@ -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;
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -206,12 +206,17 @@ describe("ASRouter", () => {
|
|||
handleMessageRequest: Router.handleMessageRequest,
|
||||
addImpression: Router.addImpression,
|
||||
blockMessageById: Router.blockMessageById,
|
||||
dispatch: Router.dispatch,
|
||||
}
|
||||
);
|
||||
|
||||
assert.calledWithExactly(FakeToolbarPanelHub.init, {
|
||||
assert.calledWithExactly(
|
||||
FakeToolbarPanelHub.init,
|
||||
Router.waitForInitialized,
|
||||
{
|
||||
getMessages: Router.handleMessageRequest,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
assert.calledWithExactly(
|
||||
FakeBookmarkPanelHub.init,
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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("<Card>", () => {
|
|||
const title = '"fluent"';
|
||||
const link = { ...DEFAULT_PROPS.link, title };
|
||||
|
||||
wrapper = shallow(<Card {...DEFAULT_PROPS} link={link} />);
|
||||
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("<Card>", () => {
|
|||
});
|
||||
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");
|
||||
|
|
|
@ -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 <div>{props.children}</div>;
|
||||
};
|
||||
|
||||
describe("<ContextMenuButton>", () => {
|
||||
let sandbox;
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
});
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
it("should call onUpdate when clicked", () => {
|
||||
const onUpdate = sandbox.spy();
|
||||
const wrapper = mount(
|
||||
<ContextMenuButton onUpdate={onUpdate}>
|
||||
<FakeMenu />
|
||||
</ContextMenuButton>
|
||||
);
|
||||
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(
|
||||
<ContextMenuButton onUpdate={onUpdate}>
|
||||
<FakeMenu />
|
||||
</ContextMenuButton>
|
||||
);
|
||||
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(
|
||||
<ContextMenuButton>
|
||||
<FakeMenu />
|
||||
</ContextMenuButton>
|
||||
);
|
||||
wrapper.find("button").simulate("click");
|
||||
assert.calledOnce(onClick);
|
||||
});
|
||||
it("should have a default keyboardAccess prop of false", () => {
|
||||
const wrapper = mount(
|
||||
<ContextMenuButton>
|
||||
<ContextMenu options={DEFAULT_MENU_OPTIONS} />
|
||||
</ContextMenuButton>
|
||||
);
|
||||
wrapper.setState({ showContextMenu: true });
|
||||
assert.equal(wrapper.find(ContextMenu).prop("keyboardAccess"), false);
|
||||
});
|
||||
it("should pass the keyboardAccess prop down to ContextMenu", () => {
|
||||
const wrapper = mount(
|
||||
<ContextMenuButton>
|
||||
<ContextMenu options={DEFAULT_MENU_OPTIONS} />
|
||||
</ContextMenuButton>
|
||||
);
|
||||
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(
|
||||
<ContextMenuButton>
|
||||
<ContextMenu options={[{ label: "item1", first: true }]} />
|
||||
</ContextMenuButton>
|
||||
);
|
||||
const focusFirst = sandbox.spy(ContextMenuItem.prototype, "focusFirst");
|
||||
wrapper.setState({ showContextMenu: true, contextMenuKeyboard: true });
|
||||
assert.calledOnce(focusFirst);
|
||||
});
|
||||
});
|
||||
|
||||
describe("<ContextMenu>", () => {
|
||||
it("should render all the options provided", () => {
|
||||
const options = [
|
||||
|
@ -79,7 +162,7 @@ describe("<ContextMenu>", () => {
|
|||
const wrapper = mount(<ContextMenu {...DEFAULT_PROPS} options={options} />);
|
||||
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(
|
||||
<ContextMenu {...DEFAULT_PROPS} options={[{ label: "item1", onClick }]} />
|
||||
|
|
|
@ -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("<DSLinkMenu>", () => {
|
|||
});
|
||||
|
||||
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("<DSLinkMenu>", () => {
|
|||
wrapper.update();
|
||||
|
||||
assert.isEmpty(parentNode.className);
|
||||
assert.isFalse(wrapper.state(["showContextMenu"]));
|
||||
});
|
||||
|
||||
it("Should add active on Menu Show", async () => {
|
||||
|
@ -76,7 +75,7 @@ describe("<DSLinkMenu>", () => {
|
|||
|
||||
it("should parse args for fluent correctly ", () => {
|
||||
const title = '"fluent"';
|
||||
wrapper = shallow(<DSLinkMenu title={title} />);
|
||||
wrapper = mount(<DSLinkMenu title={title} />);
|
||||
|
||||
const button = wrapper.find(
|
||||
"button[data-l10n-id='newtab-menu-content-tooltip']"
|
||||
|
@ -96,23 +95,25 @@ describe("<DSLinkMenu>", () => {
|
|||
|
||||
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("<DSLinkMenu>", () => {
|
|||
|
||||
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, [
|
||||
|
|
|
@ -164,17 +164,6 @@ describe("<SectionMenu>", () => {
|
|||
});
|
||||
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("<SectionMenu>", () => {
|
|||
},
|
||||
};
|
||||
const { options } = shallow(
|
||||
<SectionMenu
|
||||
{...DEFAULT_PROPS}
|
||||
dispatch={dispatch}
|
||||
options={propOptions}
|
||||
/>
|
||||
<SectionMenu {...DEFAULT_PROPS} dispatch={dispatch} />
|
||||
)
|
||||
.find(ContextMenu)
|
||||
.props();
|
||||
|
@ -238,4 +223,54 @@ describe("<SectionMenu>", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
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(
|
||||
<SectionMenu
|
||||
{...DEFAULT_PROPS}
|
||||
collapsed={true}
|
||||
dispatch={dispatch}
|
||||
extraOptions={["AddTopSite"]}
|
||||
/>
|
||||
)
|
||||
.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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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("<TopSite>", () => {
|
|||
});
|
||||
it("should render a context menu button", () => {
|
||||
const wrapper = shallow(<TopSite link={link} />);
|
||||
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(<TopSite link={link} />);
|
||||
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(<TopSite link={link} />);
|
||||
assert.equal(wrapper.find(LinkMenu).length, 0);
|
||||
});
|
||||
it("should pass onUpdate, site, options, and index to LinkMenu", () => {
|
||||
const wrapper = shallow(<TopSite link={link} />);
|
||||
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("<TopSite>", () => {
|
|||
});
|
||||
it("should pass through the correct menu options to LinkMenu", () => {
|
||||
const wrapper = shallow(<TopSite link={link} />);
|
||||
wrapper
|
||||
.find(".context-menu-button")
|
||||
.simulate("click", { preventDefault: () => {} });
|
||||
const linkMenuProps = wrapper.find(LinkMenu).props();
|
||||
assert.deepEqual(linkMenuProps.options, [
|
||||
"CheckPinTopSite",
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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", () => {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
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 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 registerCallback on enableToolbarButton()", () => {
|
||||
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([]),
|
||||
});
|
||||
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
ac_add_options --enable-official-branding
|
||||
|
||||
export MOZ_LTO=1
|
||||
export MOZ_LTO=cross
|
||||
|
||||
. "$topsrcdir/build/mozconfig.common.override"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
ac_add_options --enable-official-branding
|
||||
|
||||
export MOZ_LTO=1
|
||||
export MOZ_LTO=cross
|
||||
|
||||
unset ENABLE_CLANG_PLUGIN
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
ac_add_options --enable-official-branding
|
||||
|
||||
export MOZ_LTO=1
|
||||
export MOZ_LTO=cross
|
||||
|
||||
unset ENABLE_CLANG_PLUGIN
|
||||
|
||||
|
|
|
@ -6,4 +6,6 @@ export MOZ_PGO=1
|
|||
|
||||
ac_add_options --enable-official-branding
|
||||
|
||||
export MOZ_LTO=cross
|
||||
|
||||
. "$topsrcdir/build/mozconfig.common.override"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -4,4 +4,6 @@
|
|||
|
||||
ac_add_options --with-branding=browser/branding/nightly
|
||||
|
||||
export MOZ_LTO=cross
|
||||
|
||||
. "$topsrcdir/build/mozconfig.common.override"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <glandium@debian.org> 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 <glandium@mozilla.com> Thu, 15 Nov 2018 11:45:25 +0900
|
||||
+ -- Mike Hommey <glandium@mozilla.com> 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 <ghedo@debian.org>
|
||||
-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 <jseward@acm.org>
|
||||
+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 <u 0---(N-1)---0 1 0---0 or
|
||||
+
|
||||
+(that is, B/NB after SUBL, where dep2 has the above form), to also cover
|
||||
+
|
||||
+ W <=u 0---(N-1)---0 0 1---1
|
||||
+
|
||||
+(that is, BE/NBE after SUBL, where dept2 has the specified form).
|
||||
+
|
||||
+Patch from Nicolas B. Pierron (nicolas.b.pierron@nbp.name).
|
||||
+---
|
||||
+ VEX/priv/guest_amd64_helpers.c | 90 ++++++++++++++++++++++++++++++------------
|
||||
+ 2 files changed, 66 insertions(+), 25 deletions(-)
|
||||
+
|
||||
+diff --git a/VEX/priv/guest_amd64_helpers.c b/VEX/priv/guest_amd64_helpers.c
|
||||
+index a2b0789..30e82db 100644
|
||||
+--- a/VEX/priv/guest_amd64_helpers.c
|
||||
++++ b/VEX/priv/guest_amd64_helpers.c
|
||||
+@@ -1013,13 +1013,10 @@ static inline Bool isU64 ( IRExpr* e, ULong n )
|
||||
+ && e->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 <u 0---(N-1)---0 1 0---0
|
||||
++ W <u 0---(N-1)---0 1 0---0 or
|
||||
++ W <=u 0---(N-1)---0 0 1---1
|
||||
+
|
||||
+ In particular, the result will be defined if the top N bits of W
|
||||
+ are defined, even if the trailing bits -- those corresponding to
|
||||
+- the 0---0 section -- are undefined. Rather than make Memcheck
|
||||
+- more complex, we detect this case where we can and shift out the
|
||||
+- irrelevant and potentially undefined bits. */
|
||||
++ the rightmost 0---0 / 1---1 section -- are undefined. Rather than
|
||||
++ make Memcheck more complex, we detect this case where we can and
|
||||
++ shift out the irrelevant and potentially undefined bits. */
|
||||
+ Int n = 0;
|
||||
+- if (isU64(cc_op, AMD64G_CC_OP_SUBL)
|
||||
+- && (isU64(cond, AMD64CondB) || isU64(cond, AMD64CondNB))
|
||||
+- && (n = isU64_1_shl_N(cc_dep2)) > 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*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1839,7 +1839,7 @@ class GFX2D_API Factory {
|
|||
static already_AddRefed<ScaledFont> CreateScaledFontForDWriteFont(
|
||||
IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
|
||||
const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
|
||||
bool aUseEmbeddedBitmap, bool aForceGDIMode,
|
||||
bool aUseEmbeddedBitmap, int aRenderingMode,
|
||||
IDWriteRenderingParams* aParams, Float aGamma, Float aContrast);
|
||||
|
||||
static void SetSystemTextQuality(uint8_t aQuality);
|
||||
|
|
|
@ -959,10 +959,11 @@ void Factory::D2DCleanup() {
|
|||
already_AddRefed<ScaledFont> Factory::CreateScaledFontForDWriteFont(
|
||||
IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
|
||||
const RefPtr<UnscaledFont>& aUnscaledFont, float aSize,
|
||||
bool aUseEmbeddedBitmap, bool aForceGDIMode,
|
||||
bool aUseEmbeddedBitmap, int aRenderingMode,
|
||||
IDWriteRenderingParams* aParams, Float aGamma, Float aContrast) {
|
||||
return MakeAndAddRef<ScaledFontDWrite>(aFontFace, aUnscaledFont, aSize,
|
||||
aUseEmbeddedBitmap, aForceGDIMode,
|
||||
aUseEmbeddedBitmap,
|
||||
(DWRITE_RENDERING_MODE)aRenderingMode,
|
||||
aParams, aGamma, aContrast, aStyle);
|
||||
}
|
||||
|
||||
|
|
|
@ -126,14 +126,14 @@ static inline DWRITE_FONT_STRETCH DWriteFontStretchFromStretch(
|
|||
ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace* aFontFace,
|
||||
const RefPtr<UnscaledFont>& 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<ScaledFont> UnscaledFontDWrite::CreateScaledFont(
|
|||
|
||||
RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(
|
||||
face, this, aGlyphSize, instanceData.mUseEmbeddedBitmap,
|
||||
instanceData.mForceGDIMode, nullptr, instanceData.mGamma,
|
||||
instanceData.mRenderingMode, nullptr, instanceData.mGamma,
|
||||
instanceData.mContrast);
|
||||
|
||||
if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
|
||||
|
|
|
@ -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<UnscaledFont>& 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<IDWriteFontFace> 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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -335,20 +335,23 @@ SkScalerContext_DW::SkScalerContext_DW(sk_sp<DWriteFontTypeface> 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<DWriteFontTypeface*>(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.
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -646,11 +646,14 @@ already_AddRefed<ScaledFont> 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;
|
||||
}
|
||||
|
|
|
@ -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<Glyph*>(moz_xmalloc(mBufSize * sizeof(Glyph)));
|
||||
|
@ -1596,12 +1602,15 @@ class GlyphBufferAzure {
|
|||
mBuffer = reinterpret_cast<Glyph*>(
|
||||
moz_xrealloc(mBuffer, mBufSize * sizeof(Glyph)));
|
||||
}
|
||||
mCapacity += aGlyphCount;
|
||||
}
|
||||
|
||||
void OutputGlyph(uint32_t aGlyphID, const gfx::Point& aPt) {
|
||||
// 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(mNumGlyphs < mCapacity);
|
||||
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!");
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
],
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -111,26 +111,35 @@ struct PictureInfo {
|
|||
_spatial_node_index: SpatialNodeIndex,
|
||||
}
|
||||
|
||||
pub struct PictureCacheState {
|
||||
/// The tiles retained by this picture cache.
|
||||
pub tiles: FastHashMap<TileOffset, Tile>,
|
||||
/// 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<TileKey, Tile>,
|
||||
pub caches: FastHashMap<usize, PictureCacheState>,
|
||||
}
|
||||
|
||||
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<PropertyBinding<f32>> 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<TileKey, Tile>,
|
||||
pub tiles: FastHashMap<TileOffset, Tile>,
|
||||
/// A helper struct to map local rects into surface coords.
|
||||
map_local_to_surface: SpaceMapper<LayoutPixel, PicturePixel>,
|
||||
/// 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<TileKey>,
|
||||
pub tiles_to_draw: Vec<TileOffset>,
|
||||
/// 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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<u8>, test: Vec<u8>, 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) {
|
||||
|
|
|
@ -114,11 +114,11 @@ impl Display for Reftest {
|
|||
}
|
||||
}
|
||||
|
||||
struct ReftestImage {
|
||||
data: Vec<u8>,
|
||||
size: DeviceIntSize,
|
||||
pub struct ReftestImage {
|
||||
pub data: Vec<u8>,
|
||||
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;
|
||||
|
||||
|
|
|
@ -395,7 +395,7 @@ JS::Result<Ok> BinASTParserPerTokenizer<Tok>::finishLazyFunction(
|
|||
if (funbox->strict()) {
|
||||
lazy->setStrict();
|
||||
}
|
||||
lazy->setIsBinAST();
|
||||
MOZ_ASSERT(lazy->isBinAST());
|
||||
fun->initLazyScript(lazy);
|
||||
|
||||
return Ok();
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -75,8 +75,6 @@ jobs:
|
|||
symbol: I(deb7-bb)
|
||||
parent: debian7-base
|
||||
definition: debian-build
|
||||
packages:
|
||||
- deb7-valgrind
|
||||
args:
|
||||
ARCH: amd64
|
||||
debian7-amd64-build:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -19,13 +19,13 @@ test_valid_value("background-position-y", "y-end");
|
|||
test_valid_value("background-position-y", "-20%");
|
||||
test_valid_value("background-position-y", "10px");
|
||||
test_valid_value("background-position-y", "0.5em");
|
||||
test_valid_value("background-position-y", "calc(10px - 0.5em)");
|
||||
test_valid_value("background-position-y", "calc(10px - 0.5em)", "calc(-0.5em + 10px)");
|
||||
test_valid_value("background-position-y", "top -20%");
|
||||
test_valid_value("background-position-y", "bottom 10px");
|
||||
test_valid_value("background-position-y", "-20%, 10px");
|
||||
test_valid_value("background-position-y", "center, top, bottom");
|
||||
test_valid_value("background-position-y", "0.5em, y-start, y-end");
|
||||
test_valid_value("background-position-y", "calc(10px - 0.5em), top -20%, bottom 10px");
|
||||
test_valid_value("background-position-y", "calc(10px - 0.5em), top -20%, bottom 10px", "calc(-0.5em + 10px), top -20%, top 10px");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -19,7 +19,7 @@ test_valid_value("border-block", "double", ["double", "medium double"]);
|
|||
test_valid_value("border-block-start", "green double thin", "thin double green");
|
||||
test_valid_value("border-block-start", "green", ["green", "medium none green"]);
|
||||
test_valid_value("border-block-end", "thin", ["thin", "thin none"]);
|
||||
test_valid_value("border-block-end", "calc(10px - 0.5em) dotted red");
|
||||
test_valid_value("border-block-end", "calc(10px - 0.5em) dotted red", "calc(-0.5em + 10px) dotted red");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
<script>
|
||||
// <length> | thin | medium | thick
|
||||
test_valid_value("border-block-start-width", "10px");
|
||||
test_valid_value("border-block-start-width", "calc(10px + 0.5em)");
|
||||
test_valid_value("border-block-start-width", "calc(10px + 0.5em)", "calc(0.5em + 10px)");
|
||||
test_valid_value("border-block-start-width", "thick");
|
||||
test_valid_value("border-block-start-width", "thin");
|
||||
test_valid_value("border-block-end-width", "0", "0px");
|
||||
test_valid_value("border-block-end-width", "calc(10px - 0.5em)");
|
||||
test_valid_value("border-block-end-width", "calc(10px - 0.5em)", "calc(-0.5em + 10px)");
|
||||
test_valid_value("border-block-end-width", "medium");
|
||||
test_valid_value("border-block-width", "10px");
|
||||
test_valid_value("border-block-width", "medium calc(10px + 0.5em)");
|
||||
test_valid_value("border-block-width", "medium calc(10px + 0.5em)", "medium calc(0.5em + 10px)");
|
||||
test_valid_value("border-block-width", "10px 10px", "10px");
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -19,7 +19,7 @@ test_valid_value("border-inline", "double", ["double", "medium double"]);
|
|||
test_valid_value("border-inline-start", "green double thin", "thin double green");
|
||||
test_valid_value("border-inline-start", "green", ["green", "medium none green"]);
|
||||
test_valid_value("border-inline-end", "thin", ["thin", "thin none"]);
|
||||
test_valid_value("border-inline-end", "calc(10px - 0.5em) dotted red");
|
||||
test_valid_value("border-inline-end", "calc(10px - 0.5em) dotted red", "calc(-0.5em + 10px) dotted red");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
<script>
|
||||
// <length> | thin | medium | thick
|
||||
test_valid_value("border-inline-start-width", "10px");
|
||||
test_valid_value("border-inline-start-width", "calc(10px + 0.5em)");
|
||||
test_valid_value("border-inline-start-width", "calc(10px + 0.5em)", "calc(0.5em + 10px)");
|
||||
test_valid_value("border-inline-start-width", "thick");
|
||||
test_valid_value("border-inline-start-width", "thin");
|
||||
test_valid_value("border-inline-end-width", "0", "0px");
|
||||
test_valid_value("border-inline-end-width", "calc(10px - 0.5em)");
|
||||
test_valid_value("border-inline-end-width", "calc(10px - 0.5em)", "calc(-0.5em + 10px)");
|
||||
test_valid_value("border-inline-end-width", "medium");
|
||||
test_valid_value("border-inline-width", "10px");
|
||||
test_valid_value("border-inline-width", "medium calc(10px + 0.5em)");
|
||||
test_valid_value("border-inline-width", "medium calc(10px + 0.5em)", "medium calc(0.5em + 10px)");
|
||||
test_valid_value("border-inline-width", "10px 10px", "10px");
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -15,16 +15,16 @@
|
|||
test_valid_value("inset-block-start", "auto");
|
||||
test_valid_value("inset-block-end", "-10px");
|
||||
test_valid_value("inset-inline-start", "-20%");
|
||||
test_valid_value("inset-inline-end", "calc(10px - 0.5em)");
|
||||
test_valid_value("inset-inline-end", "calc(10px - 0.5em)", "calc(-0.5em + 10px)");
|
||||
|
||||
test_valid_value("inset-block", "auto");
|
||||
test_valid_value("inset-block", "-10px");
|
||||
test_valid_value("inset-block", "calc(10px - 0.5em) -20%");
|
||||
test_valid_value("inset-block", "calc(10px - 0.5em) -20%", "calc(-0.5em + 10px) -20%");
|
||||
test_valid_value("inset-block", "auto auto", "auto");
|
||||
test_valid_value("inset-inline", "-20%");
|
||||
test_valid_value("inset-inline", "calc(10px - 0.5em)");
|
||||
test_valid_value("inset-inline", "calc(10px - 0.5em)", "calc(-0.5em + 10px)");
|
||||
test_valid_value("inset-inline", "-10px auto");
|
||||
test_valid_value("inset-inline", "auto calc(10px + 0.5em)");
|
||||
test_valid_value("inset-inline", "auto calc(10px + 0.5em)", "auto calc(0.5em + 10px)");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<script>
|
||||
test_valid_value("border-spacing", "0px");
|
||||
test_valid_value("border-spacing", "10px 20px");
|
||||
test_valid_value("border-spacing", "calc(10px + 0.5em) calc(10px - 0.5em)");
|
||||
test_valid_value("border-spacing", "calc(10px + 0.5em) calc(10px - 0.5em)", "calc(0.5em + 10px) calc(-0.5em + 10px)");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Загрузка…
Ссылка в новой задаче