зеркало из 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_ENDPOINT",
|
||||||
"DISCOVERY_STREAM_SPOCS_FILL",
|
"DISCOVERY_STREAM_SPOCS_FILL",
|
||||||
"DISCOVERY_STREAM_SPOCS_UPDATE",
|
"DISCOVERY_STREAM_SPOCS_UPDATE",
|
||||||
|
"DISCOVERY_STREAM_SPOC_BLOCKED",
|
||||||
"DISCOVERY_STREAM_SPOC_IMPRESSION",
|
"DISCOVERY_STREAM_SPOC_IMPRESSION",
|
||||||
"DOWNLOAD_CHANGED",
|
"DOWNLOAD_CHANGED",
|
||||||
"FAKE_FOCUS_SEARCH",
|
"FAKE_FOCUS_SEARCH",
|
||||||
|
|
|
@ -66,6 +66,7 @@ const INITIAL_STATE = {
|
||||||
data: {}, // {spocs: []}
|
data: {}, // {spocs: []}
|
||||||
loaded: false,
|
loaded: false,
|
||||||
frequency_caps: [],
|
frequency_caps: [],
|
||||||
|
blocked: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Search: {
|
Search: {
|
||||||
|
@ -612,6 +613,14 @@ function DiscoveryStream(prevState = INITIAL_STATE.DiscoveryStream, action) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return prevState;
|
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:
|
case at.DISCOVERY_STREAM_LINK_BLOCKED:
|
||||||
return isNotReady()
|
return isNotReady()
|
||||||
? prevState
|
? prevState
|
||||||
|
|
|
@ -19,11 +19,26 @@ export class ModalOverlayWrapper extends React.PureComponent {
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.props.document.addEventListener("keydown", this.onKeyDown);
|
this.props.document.addEventListener("keydown", this.onKeyDown);
|
||||||
this.props.document.body.classList.add("modal-open");
|
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() {
|
componentWillUnmount() {
|
||||||
this.props.document.removeEventListener("keydown", this.onKeyDown);
|
this.props.document.removeEventListener("keydown", this.onKeyDown);
|
||||||
this.props.document.body.classList.remove("modal-open");
|
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() {
|
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 {
|
.modalOverlayInner {
|
||||||
width: 960px;
|
width: 960px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -30,7 +43,6 @@
|
||||||
display: none;
|
display: none;
|
||||||
z-index: 1101;
|
z-index: 1101;
|
||||||
|
|
||||||
|
|
||||||
// modal takes over entire screen
|
// modal takes over entire screen
|
||||||
@media(max-width: 960px) {
|
@media(max-width: 960px) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -401,7 +401,7 @@
|
||||||
|
|
||||||
.inline-onboarding {
|
.inline-onboarding {
|
||||||
&.activity-stream.welcome {
|
&.activity-stream.welcome {
|
||||||
overflow-y: scroll;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modalOverlayInner {
|
.modalOverlayInner {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import { actionCreators as ac, actionTypes as at } from "common/Actions.jsm";
|
import { actionCreators as ac, actionTypes as at } from "common/Actions.jsm";
|
||||||
import { cardContextTypes } from "./types";
|
import { cardContextTypes } from "./types";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
|
||||||
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
|
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { ScreenshotUtils } from "content-src/lib/screenshot-utils";
|
import { ScreenshotUtils } from "content-src/lib/screenshot-utils";
|
||||||
|
@ -27,11 +28,9 @@ export class _Card extends React.PureComponent {
|
||||||
this.state = {
|
this.state = {
|
||||||
activeCard: null,
|
activeCard: null,
|
||||||
imageLoaded: false,
|
imageLoaded: false,
|
||||||
showContextMenu: false,
|
|
||||||
cardImage: null,
|
cardImage: null,
|
||||||
};
|
};
|
||||||
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
|
this.onMenuButtonUpdate = this.onMenuButtonUpdate.bind(this);
|
||||||
this.onMenuUpdate = this.onMenuUpdate.bind(this);
|
|
||||||
this.onLinkClick = this.onLinkClick.bind(this);
|
this.onLinkClick = this.onLinkClick.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,12 +116,12 @@ export class _Card extends React.PureComponent {
|
||||||
return nextState;
|
return nextState;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMenuButtonClick(event) {
|
onMenuButtonUpdate(isOpen) {
|
||||||
event.preventDefault();
|
if (isOpen) {
|
||||||
this.setState({
|
this.setState({ activeCard: this.props.index });
|
||||||
activeCard: this.props.index,
|
} else {
|
||||||
showContextMenu: true,
|
this.setState({ activeCard: null });
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -191,10 +190,6 @@ export class _Card extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMenuUpdate(showContextMenu) {
|
|
||||||
this.setState({ showContextMenu });
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.maybeLoadImage();
|
this.maybeLoadImage();
|
||||||
}
|
}
|
||||||
|
@ -239,8 +234,7 @@ export class _Card extends React.PureComponent {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { props } = this;
|
const { props } = this;
|
||||||
const title = link.title || link.hostname;
|
const title = link.title || link.hostname;
|
||||||
const isContextMenuOpen =
|
const isContextMenuOpen = this.state.activeCard === index;
|
||||||
this.state.showContextMenu && this.state.activeCard === index;
|
|
||||||
// Display "now" as "trending" until we have new strings #3402
|
// Display "now" as "trending" until we have new strings #3402
|
||||||
const { icon, fluentID } =
|
const { icon, fluentID } =
|
||||||
cardContextTypes[link.type === "now" ? "trending" : link.type] || {};
|
cardContextTypes[link.type === "now" ? "trending" : link.type] || {};
|
||||||
|
@ -329,25 +323,21 @@ export class _Card extends React.PureComponent {
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
{!props.placeholder && (
|
{!props.placeholder && (
|
||||||
<button
|
<ContextMenuButton
|
||||||
aria-haspopup="true"
|
tooltip="newtab-menu-content-tooltip"
|
||||||
data-l10n-id="newtab-menu-content-tooltip"
|
tooltipArgs={{ title }}
|
||||||
data-l10n-args={JSON.stringify({ title })}
|
onUpdate={this.onMenuButtonUpdate}
|
||||||
className="context-menu-button icon"
|
>
|
||||||
onClick={this.onMenuButtonClick}
|
<LinkMenu
|
||||||
/>
|
dispatch={dispatch}
|
||||||
)}
|
index={index}
|
||||||
{isContextMenuOpen && (
|
source={eventSource}
|
||||||
<LinkMenu
|
options={link.contextMenuOptions || contextMenuOptions}
|
||||||
dispatch={dispatch}
|
site={link}
|
||||||
index={index}
|
siteInfo={this._getTelemetryInfo()}
|
||||||
source={eventSource}
|
shouldSendImpressionStats={shouldSendImpressionStats}
|
||||||
onUpdate={this.onMenuUpdate}
|
/>
|
||||||
options={link.contextMenuOptions || contextMenuOptions}
|
</ContextMenuButton>
|
||||||
site={link}
|
|
||||||
siteInfo={this._getTelemetryInfo()}
|
|
||||||
shouldSendImpressionStats={shouldSendImpressionStats}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { FluentOrText } from "content-src/components/FluentOrText/FluentOrText";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { SectionMenu } from "content-src/components/SectionMenu/SectionMenu";
|
import { SectionMenu } from "content-src/components/SectionMenu/SectionMenu";
|
||||||
import { SectionMenuOptions } from "content-src/lib/section-menu-options";
|
import { SectionMenuOptions } from "content-src/lib/section-menu-options";
|
||||||
|
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
|
||||||
|
|
||||||
const VISIBLE = "visible";
|
const VISIBLE = "visible";
|
||||||
const VISIBILITY_CHANGE_EVENT = "visibilitychange";
|
const VISIBILITY_CHANGE_EVENT = "visibilitychange";
|
||||||
|
@ -20,7 +21,6 @@ export class CollapsibleSection extends React.PureComponent {
|
||||||
this.onKeyPress = this.onKeyPress.bind(this);
|
this.onKeyPress = this.onKeyPress.bind(this);
|
||||||
this.onTransitionEnd = this.onTransitionEnd.bind(this);
|
this.onTransitionEnd = this.onTransitionEnd.bind(this);
|
||||||
this.enableOrDisableAnimation = this.enableOrDisableAnimation.bind(this);
|
this.enableOrDisableAnimation = this.enableOrDisableAnimation.bind(this);
|
||||||
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
|
|
||||||
this.onMenuButtonMouseEnter = this.onMenuButtonMouseEnter.bind(this);
|
this.onMenuButtonMouseEnter = this.onMenuButtonMouseEnter.bind(this);
|
||||||
this.onMenuButtonMouseLeave = this.onMenuButtonMouseLeave.bind(this);
|
this.onMenuButtonMouseLeave = this.onMenuButtonMouseLeave.bind(this);
|
||||||
this.onMenuUpdate = this.onMenuUpdate.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() {
|
onMenuButtonMouseEnter() {
|
||||||
this.setState({ menuButtonHover: true });
|
this.setState({ menuButtonHover: true });
|
||||||
}
|
}
|
||||||
|
@ -258,14 +253,11 @@ export class CollapsibleSection extends React.PureComponent {
|
||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<ContextMenuButton
|
||||||
aria-haspopup="true"
|
tooltip="newtab-menu-section-tooltip"
|
||||||
className="context-menu-button icon"
|
onUpdate={this.onMenuUpdate}
|
||||||
data-l10n-id="newtab-menu-section-tooltip"
|
refFunction={this.setContextMenuButtonRef}
|
||||||
onClick={this.onMenuButtonClick}
|
>
|
||||||
ref={this.setContextMenuButtonRef}
|
|
||||||
/>
|
|
||||||
{showContextMenu && (
|
|
||||||
<SectionMenu
|
<SectionMenu
|
||||||
id={id}
|
id={id}
|
||||||
extraOptions={extraMenuOptions}
|
extraOptions={extraMenuOptions}
|
||||||
|
@ -273,14 +265,13 @@ export class CollapsibleSection extends React.PureComponent {
|
||||||
showPrefName={showPrefName}
|
showPrefName={showPrefName}
|
||||||
privacyNoticeURL={privacyNoticeURL}
|
privacyNoticeURL={privacyNoticeURL}
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
onUpdate={this.onMenuUpdate}
|
|
||||||
isFixed={isFixed}
|
isFixed={isFixed}
|
||||||
isFirst={isFirst}
|
isFirst={isFirst}
|
||||||
isLast={isLast}
|
isLast={isLast}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
isWebExtension={isWebExtension}
|
isWebExtension={isWebExtension}
|
||||||
/>
|
/>
|
||||||
)}
|
</ContextMenuButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ErrorBoundary className="section-body-fallback">
|
<ErrorBoundary className="section-body-fallback">
|
||||||
|
|
|
@ -60,6 +60,7 @@ export class ContextMenu extends React.PureComponent {
|
||||||
key={i}
|
key={i}
|
||||||
option={option}
|
option={option}
|
||||||
hideContext={this.hideContext}
|
hideContext={this.hideContext}
|
||||||
|
keyboardAccess={this.props.keyboardAccess}
|
||||||
tabIndex="0"
|
tabIndex="0"
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -76,6 +77,7 @@ export class ContextMenuItem extends React.PureComponent {
|
||||||
super(props);
|
super(props);
|
||||||
this.onClick = this.onClick.bind(this);
|
this.onClick = this.onClick.bind(this);
|
||||||
this.onKeyDown = this.onKeyDown.bind(this);
|
this.onKeyDown = this.onKeyDown.bind(this);
|
||||||
|
this.focusFirst = this.focusFirst.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick() {
|
onClick() {
|
||||||
|
@ -83,6 +85,13 @@ export class ContextMenuItem extends React.PureComponent {
|
||||||
this.props.option.onClick();
|
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
|
// This selects the correct node based on the key pressed
|
||||||
focusSibling(target, key) {
|
focusSibling(target, key) {
|
||||||
const parent = target.parentNode;
|
const parent = target.parentNode;
|
||||||
|
@ -138,6 +147,7 @@ export class ContextMenuItem extends React.PureComponent {
|
||||||
tabIndex="0"
|
tabIndex="0"
|
||||||
onClick={this.onClick}
|
onClick={this.onClick}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
|
ref={option.first ? this.focusFirst : null}
|
||||||
>
|
>
|
||||||
{option.icon && (
|
{option.icon && (
|
||||||
<span className={`icon icon-spacer icon-${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/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
|
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
|
||||||
|
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export class DSLinkMenu extends React.PureComponent {
|
export class DSLinkMenu extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
|
||||||
activeCard: null,
|
|
||||||
showContextMenu: false,
|
|
||||||
};
|
|
||||||
this.windowObj = this.props.windowObj || window; // Added to support unit tests
|
this.windowObj = this.props.windowObj || window; // Added to support unit tests
|
||||||
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
|
|
||||||
this.onMenuUpdate = this.onMenuUpdate.bind(this);
|
this.onMenuUpdate = this.onMenuUpdate.bind(this);
|
||||||
this.onMenuShow = this.onMenuShow.bind(this);
|
this.onMenuShow = this.onMenuShow.bind(this);
|
||||||
this.contextMenuButtonRef = React.createRef();
|
this.contextMenuButtonRef = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
onMenuButtonClick(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
this.setState({
|
|
||||||
activeCard: this.props.index,
|
|
||||||
showContextMenu: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onMenuUpdate(showContextMenu) {
|
onMenuUpdate(showContextMenu) {
|
||||||
if (!showContextMenu) {
|
if (!showContextMenu) {
|
||||||
const dsLinkMenuHostDiv = this.contextMenuButtonRef.current.parentElement;
|
const dsLinkMenuHostDiv = this.contextMenuButtonRef.current.parentElement;
|
||||||
dsLinkMenuHostDiv.parentElement.classList.remove("active", "last-item");
|
dsLinkMenuHostDiv.parentElement.classList.remove("active", "last-item");
|
||||||
}
|
}
|
||||||
this.setState({ showContextMenu });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nextAnimationFrame() {
|
nextAnimationFrame() {
|
||||||
|
@ -51,8 +38,6 @@ export class DSLinkMenu extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { index, dispatch } = this.props;
|
const { index, dispatch } = this.props;
|
||||||
const isContextMenuOpen =
|
|
||||||
this.state.showContextMenu && this.state.activeCard === index;
|
|
||||||
const TOP_STORIES_CONTEXT_MENU_OPTIONS = [
|
const TOP_STORIES_CONTEXT_MENU_OPTIONS = [
|
||||||
"CheckBookmarkOrArchive",
|
"CheckBookmarkOrArchive",
|
||||||
"CheckSavedToPocket",
|
"CheckSavedToPocket",
|
||||||
|
@ -67,20 +52,16 @@ export class DSLinkMenu extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<button
|
<ContextMenuButton
|
||||||
ref={this.contextMenuButtonRef}
|
refFunction={this.contextMenuButtonRef}
|
||||||
aria-haspopup="true"
|
tooltip={"newtab-menu-content-tooltip"}
|
||||||
className="context-menu-button icon"
|
tooltipArgs={{ title }}
|
||||||
data-l10n-id="newtab-menu-content-tooltip"
|
onUpdate={this.onMenuUpdate}
|
||||||
data-l10n-args={JSON.stringify({ title })}
|
>
|
||||||
onClick={this.onMenuButtonClick}
|
|
||||||
/>
|
|
||||||
{isContextMenuOpen && (
|
|
||||||
<LinkMenu
|
<LinkMenu
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
index={index}
|
index={index}
|
||||||
source={type.toUpperCase()}
|
source={type.toUpperCase()}
|
||||||
onUpdate={this.onMenuUpdate}
|
|
||||||
onShow={this.onMenuShow}
|
onShow={this.onMenuShow}
|
||||||
options={TOP_STORIES_CONTEXT_MENU_OPTIONS}
|
options={TOP_STORIES_CONTEXT_MENU_OPTIONS}
|
||||||
shouldSendImpressionStats={true}
|
shouldSendImpressionStats={true}
|
||||||
|
@ -95,7 +76,7 @@ export class DSLinkMenu extends React.PureComponent {
|
||||||
bookmarkGuid: this.props.bookmarkGuid,
|
bookmarkGuid: this.props.bookmarkGuid,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
</ContextMenuButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@ export class _LinkMenu extends React.PureComponent {
|
||||||
onUpdate={this.props.onUpdate}
|
onUpdate={this.props.onUpdate}
|
||||||
onShow={this.props.onShow}
|
onShow={this.props.onShow}
|
||||||
options={this.getOptions()}
|
options={this.getOptions()}
|
||||||
|
keyboardAccess={this.props.keyboardAccess}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,19 @@ const WEBEXT_SECTION_MENU_OPTIONS = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export class _SectionMenu extends React.PureComponent {
|
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() {
|
getOptions() {
|
||||||
const { props } = this;
|
const { props } = this;
|
||||||
|
|
||||||
|
@ -51,6 +64,14 @@ export class _SectionMenu extends React.PureComponent {
|
||||||
const { action, id, type, userEvent } = option;
|
const { action, id, type, userEvent } = option;
|
||||||
if (!type && id) {
|
if (!type && id) {
|
||||||
option.onClick = () => {
|
option.onClick = () => {
|
||||||
|
const hasAddEvent =
|
||||||
|
userEvent === "MENU_ADD_TOPSITE" ||
|
||||||
|
userEvent === "MENU_ADD_SEARCH";
|
||||||
|
|
||||||
|
if (props.collapsed && hasAddEvent) {
|
||||||
|
this.handleAddWhileCollapsed();
|
||||||
|
}
|
||||||
|
|
||||||
props.dispatch(action);
|
props.dispatch(action);
|
||||||
if (userEvent) {
|
if (userEvent) {
|
||||||
props.dispatch(
|
props.dispatch(
|
||||||
|
@ -75,7 +96,11 @@ export class _SectionMenu extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
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 React from "react";
|
||||||
import { ScreenshotUtils } from "content-src/lib/screenshot-utils";
|
import { ScreenshotUtils } from "content-src/lib/screenshot-utils";
|
||||||
import { TOP_SITES_MAX_SITES_PER_ROW } from "common/Reducers.jsm";
|
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 {
|
export class TopSiteLink extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -273,7 +274,6 @@ export class TopSite extends React.PureComponent {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { showContextMenu: false };
|
this.state = { showContextMenu: false };
|
||||||
this.onLinkClick = this.onLinkClick.bind(this);
|
this.onLinkClick = this.onLinkClick.bind(this);
|
||||||
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
|
|
||||||
this.onMenuUpdate = this.onMenuUpdate.bind(this);
|
this.onMenuUpdate = this.onMenuUpdate.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,21 +335,18 @@ export class TopSite extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMenuButtonClick(event) {
|
onMenuUpdate(isOpen) {
|
||||||
event.preventDefault();
|
if (isOpen) {
|
||||||
this.props.onActivate(this.props.index);
|
this.props.onActivate(this.props.index);
|
||||||
this.setState({ showContextMenu: true });
|
} else {
|
||||||
}
|
this.props.onActivate();
|
||||||
|
}
|
||||||
onMenuUpdate(showContextMenu) {
|
|
||||||
this.setState({ showContextMenu });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { props } = this;
|
const { props } = this;
|
||||||
const { link } = props;
|
const { link } = props;
|
||||||
const isContextMenuOpen =
|
const isContextMenuOpen = props.activeIndex === props.index;
|
||||||
this.state.showContextMenu && props.activeIndex === props.index;
|
|
||||||
const title = link.label || link.hostname;
|
const title = link.label || link.hostname;
|
||||||
return (
|
return (
|
||||||
<TopSiteLink
|
<TopSiteLink
|
||||||
|
@ -362,14 +359,11 @@ export class TopSite extends React.PureComponent {
|
||||||
title={title}
|
title={title}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<ContextMenuButton
|
||||||
aria-haspopup="true"
|
tooltip="newtab-menu-content-tooltip"
|
||||||
className="context-menu-button icon"
|
tooltipArgs={{ title }}
|
||||||
data-l10n-id="newtab-menu-content-tooltip"
|
onUpdate={this.onMenuUpdate}
|
||||||
data-l10n-args={JSON.stringify({ title })}
|
>
|
||||||
onClick={this.onMenuButtonClick}
|
|
||||||
/>
|
|
||||||
{isContextMenuOpen && (
|
|
||||||
<LinkMenu
|
<LinkMenu
|
||||||
dispatch={props.dispatch}
|
dispatch={props.dispatch}
|
||||||
index={props.index}
|
index={props.index}
|
||||||
|
@ -383,7 +377,7 @@ export class TopSite extends React.PureComponent {
|
||||||
siteInfo={this._getTelemetryInfo()}
|
siteInfo={this._getTelemetryInfo()}
|
||||||
source={TOP_SITES_SOURCE}
|
source={TOP_SITES_SOURCE}
|
||||||
/>
|
/>
|
||||||
)}
|
</ContextMenuButton>
|
||||||
</div>
|
</div>
|
||||||
</TopSiteLink>
|
</TopSiteLink>
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,8 +30,10 @@ export const selectLayoutRender = (state, prefs, rickRollCache) => {
|
||||||
|
|
||||||
if (rickRoll <= spocsConfig.probability) {
|
if (rickRoll <= spocsConfig.probability) {
|
||||||
spocIndex++;
|
spocIndex++;
|
||||||
recommendations.splice(position.index, 0, spoc);
|
if (!spocs.blocked.includes(spoc.url)) {
|
||||||
chosenSpocs.add(spoc);
|
recommendations.splice(position.index, 0, spoc);
|
||||||
|
chosenSpocs.add(spoc);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
unchosenSpocs.add(spoc);
|
unchosenSpocs.add(spoc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3003,6 +3003,15 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
||||||
.modalOverlayOuter.active {
|
.modalOverlayOuter.active {
|
||||||
display: block; }
|
display: block; }
|
||||||
|
|
||||||
|
.modal-scroll {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto; }
|
||||||
|
|
||||||
|
.modal-height {
|
||||||
|
padding-top: 80px; }
|
||||||
|
|
||||||
.modalOverlayInner {
|
.modalOverlayInner {
|
||||||
width: 960px;
|
width: 960px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -4108,7 +4117,7 @@ a.firstrun-link {
|
||||||
text-align: center; }
|
text-align: center; }
|
||||||
|
|
||||||
.inline-onboarding.activity-stream.welcome {
|
.inline-onboarding.activity-stream.welcome {
|
||||||
overflow-y: scroll; }
|
overflow-y: hidden; }
|
||||||
|
|
||||||
.inline-onboarding .modalOverlayInner {
|
.inline-onboarding .modalOverlayInner {
|
||||||
position: absolute; }
|
position: absolute; }
|
||||||
|
|
|
@ -3006,6 +3006,15 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
||||||
.modalOverlayOuter.active {
|
.modalOverlayOuter.active {
|
||||||
display: block; }
|
display: block; }
|
||||||
|
|
||||||
|
.modal-scroll {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto; }
|
||||||
|
|
||||||
|
.modal-height {
|
||||||
|
padding-top: 80px; }
|
||||||
|
|
||||||
.modalOverlayInner {
|
.modalOverlayInner {
|
||||||
width: 960px;
|
width: 960px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -4111,7 +4120,7 @@ a.firstrun-link {
|
||||||
text-align: center; }
|
text-align: center; }
|
||||||
|
|
||||||
.inline-onboarding.activity-stream.welcome {
|
.inline-onboarding.activity-stream.welcome {
|
||||||
overflow-y: scroll; }
|
overflow-y: hidden; }
|
||||||
|
|
||||||
.inline-onboarding .modalOverlayInner {
|
.inline-onboarding .modalOverlayInner {
|
||||||
position: absolute; }
|
position: absolute; }
|
||||||
|
|
|
@ -3003,6 +3003,15 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
||||||
.modalOverlayOuter.active {
|
.modalOverlayOuter.active {
|
||||||
display: block; }
|
display: block; }
|
||||||
|
|
||||||
|
.modal-scroll {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto; }
|
||||||
|
|
||||||
|
.modal-height {
|
||||||
|
padding-top: 80px; }
|
||||||
|
|
||||||
.modalOverlayInner {
|
.modalOverlayInner {
|
||||||
width: 960px;
|
width: 960px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -4108,7 +4117,7 @@ a.firstrun-link {
|
||||||
text-align: center; }
|
text-align: center; }
|
||||||
|
|
||||||
.inline-onboarding.activity-stream.welcome {
|
.inline-onboarding.activity-stream.welcome {
|
||||||
overflow-y: scroll; }
|
overflow-y: hidden; }
|
||||||
|
|
||||||
.inline-onboarding .modalOverlayInner {
|
.inline-onboarding .modalOverlayInner {
|
||||||
position: absolute; }
|
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,
|
handleMessageRequest: this.handleMessageRequest,
|
||||||
addImpression: this.addImpression,
|
addImpression: this.addImpression,
|
||||||
blockMessageById: this.blockMessageById,
|
blockMessageById: this.blockMessageById,
|
||||||
|
dispatch: this.dispatch,
|
||||||
});
|
});
|
||||||
ToolbarPanelHub.init({
|
ToolbarPanelHub.init(this.waitForInitialized, {
|
||||||
getMessages: this.handleMessageRequest,
|
getMessages: this.handleMessageRequest,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1889,6 +1890,7 @@ class _ASRouter {
|
||||||
await this.addImpression(action.data);
|
await this.addImpression(action.data);
|
||||||
break;
|
break;
|
||||||
case "DOORHANGER_TELEMETRY":
|
case "DOORHANGER_TELEMETRY":
|
||||||
|
case "TOOLBAR_BADGE_TELEMETRY":
|
||||||
if (this.dispatchToAS) {
|
if (this.dispatchToAS) {
|
||||||
this.dispatchToAS(ac.ASRouterUserEvent(action.data));
|
this.dispatchToAS(ac.ASRouterUserEvent(action.data));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1182,6 +1182,21 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
||||||
const filtered = spocsList.filter(s => s.url === action.data.url);
|
const filtered = spocsList.filter(s => s.url === action.data.url);
|
||||||
if (filtered.length) {
|
if (filtered.length) {
|
||||||
this._sendSpocsFill({ blocked_by_user: filtered }, false);
|
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(
|
this.store.dispatch(
|
||||||
|
|
|
@ -13,6 +13,11 @@ ChromeUtils.defineModuleGetter(
|
||||||
"ToolbarPanelHub",
|
"ToolbarPanelHub",
|
||||||
"resource://activity-stream/lib/ToolbarPanelHub.jsm"
|
"resource://activity-stream/lib/ToolbarPanelHub.jsm"
|
||||||
);
|
);
|
||||||
|
ChromeUtils.defineModuleGetter(
|
||||||
|
this,
|
||||||
|
"Services",
|
||||||
|
"resource://gre/modules/Services.jsm"
|
||||||
|
);
|
||||||
ChromeUtils.defineModuleGetter(
|
ChromeUtils.defineModuleGetter(
|
||||||
this,
|
this,
|
||||||
"setTimeout",
|
"setTimeout",
|
||||||
|
@ -23,6 +28,16 @@ ChromeUtils.defineModuleGetter(
|
||||||
"clearTimeout",
|
"clearTimeout",
|
||||||
"resource://gre/modules/Timer.jsm"
|
"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();
|
const notificationsByWindow = new WeakMap();
|
||||||
|
|
||||||
|
@ -31,33 +46,51 @@ class _ToolbarBadgeHub {
|
||||||
this.id = "toolbar-badge-hub";
|
this.id = "toolbar-badge-hub";
|
||||||
this.template = "toolbar_badge";
|
this.template = "toolbar_badge";
|
||||||
this.state = null;
|
this.state = null;
|
||||||
|
this.prefs = {
|
||||||
|
WHATSNEW_TOOLBAR_PANEL: "browser.messaging-system.whatsNewPanel.enabled",
|
||||||
|
};
|
||||||
this.removeAllNotifications = this.removeAllNotifications.bind(this);
|
this.removeAllNotifications = this.removeAllNotifications.bind(this);
|
||||||
this.removeToolbarNotification = this.removeToolbarNotification.bind(this);
|
this.removeToolbarNotification = this.removeToolbarNotification.bind(this);
|
||||||
this.addToolbarNotification = this.addToolbarNotification.bind(this);
|
this.addToolbarNotification = this.addToolbarNotification.bind(this);
|
||||||
this.registerBadgeToAllWindows = this.registerBadgeToAllWindows.bind(this);
|
this.registerBadgeToAllWindows = this.registerBadgeToAllWindows.bind(this);
|
||||||
|
this._sendTelemetry = this._sendTelemetry.bind(this);
|
||||||
|
this.sendUserEventTelemetry = this.sendUserEventTelemetry.bind(this);
|
||||||
|
|
||||||
this._handleMessageRequest = null;
|
this._handleMessageRequest = null;
|
||||||
this._addImpression = null;
|
this._addImpression = null;
|
||||||
this._blockMessageById = null;
|
this._blockMessageById = null;
|
||||||
|
this._dispatch = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(
|
async init(
|
||||||
waitForInitialized,
|
waitForInitialized,
|
||||||
{ handleMessageRequest, addImpression, blockMessageById }
|
{ handleMessageRequest, addImpression, blockMessageById, dispatch }
|
||||||
) {
|
) {
|
||||||
this._handleMessageRequest = handleMessageRequest;
|
this._handleMessageRequest = handleMessageRequest;
|
||||||
this._blockMessageById = blockMessageById;
|
this._blockMessageById = blockMessageById;
|
||||||
this._addImpression = addImpression;
|
this._addImpression = addImpression;
|
||||||
|
this._dispatch = dispatch;
|
||||||
this.state = {};
|
this.state = {};
|
||||||
// Need to wait for ASRouter to initialize before trying to fetch messages
|
// Need to wait for ASRouter to initialize before trying to fetch messages
|
||||||
await waitForInitialized;
|
await waitForInitialized;
|
||||||
this.messageRequest("toolbarBadgeUpdate");
|
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 }) {
|
executeAction({ id }) {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case "show-whatsnew-button":
|
case "show-whatsnew-button":
|
||||||
ToolbarPanelHub.enableToolbarButton();
|
ToolbarPanelHub.enableToolbarButton();
|
||||||
|
ToolbarPanelHub.enableAppmenuButton();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,6 +124,11 @@ class _ToolbarBadgeHub {
|
||||||
this.removeAllNotifications
|
this.removeAllNotifications
|
||||||
);
|
);
|
||||||
event.target.removeEventListener("click", 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
|
// Will call uninit on every window
|
||||||
EveryWindow.unregisterCallback(this.id);
|
EveryWindow.unregisterCallback(this.id);
|
||||||
|
@ -146,6 +184,8 @@ class _ToolbarBadgeHub {
|
||||||
registerBadgeToAllWindows(message) {
|
registerBadgeToAllWindows(message) {
|
||||||
// Impression should be added when the badge becomes visible
|
// Impression should be added when the badge becomes visible
|
||||||
this._addImpression(message);
|
this._addImpression(message);
|
||||||
|
// Send a telemetry ping when adding the notification badge
|
||||||
|
this.sendUserEventTelemetry("IMPRESSION", message);
|
||||||
|
|
||||||
EveryWindow.registerCallback(
|
EveryWindow.registerCallback(
|
||||||
this.id,
|
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() {
|
uninit() {
|
||||||
this._clearBadgeTimeout();
|
this._clearBadgeTimeout();
|
||||||
this.state = null;
|
this.state = null;
|
||||||
|
Services.prefs.removeObserver(this.prefs.WHATSNEW_TOOLBAR_PANEL, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,16 +30,42 @@ class _ToolbarPanelHub {
|
||||||
this._hideToolbarButton = this._hideToolbarButton.bind(this);
|
this._hideToolbarButton = this._hideToolbarButton.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
init({ getMessages }) {
|
async init(waitForInitialized, { getMessages }) {
|
||||||
this._getMessages = 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) {
|
if (this.whatsNewPanelEnabled) {
|
||||||
|
// Enable the application menu button so that the user can access
|
||||||
|
// the panel outside of the toolbar button
|
||||||
this.enableAppmenuButton();
|
this.enableAppmenuButton();
|
||||||
}
|
}
|
||||||
|
// Listen for pref changes that could turn off the feature
|
||||||
|
Services.prefs.addObserver(WHATSNEW_ENABLED_PREF, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
uninit() {
|
uninit() {
|
||||||
EveryWindow.unregisterCallback(TOOLBAR_BUTTON_ID);
|
EveryWindow.unregisterCallback(TOOLBAR_BUTTON_ID);
|
||||||
EveryWindow.unregisterCallback(APPMENU_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() {
|
get whatsNewPanelEnabled() {
|
||||||
|
@ -51,21 +77,25 @@ class _ToolbarPanelHub {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turns on the Appmenu (hamburger menu) button for all open windows and future windows.
|
// Turns on the Appmenu (hamburger menu) button for all open windows and future windows.
|
||||||
enableAppmenuButton() {
|
async enableAppmenuButton() {
|
||||||
EveryWindow.registerCallback(
|
if ((await this.messages).length) {
|
||||||
APPMENU_BUTTON_ID,
|
EveryWindow.registerCallback(
|
||||||
this._showAppmenuButton,
|
APPMENU_BUTTON_ID,
|
||||||
this._hideAppmenuButton
|
this._showAppmenuButton,
|
||||||
);
|
this._hideAppmenuButton
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turns on the Toolbar button for all open windows and future windows.
|
// Turns on the Toolbar button for all open windows and future windows.
|
||||||
enableToolbarButton() {
|
async enableToolbarButton() {
|
||||||
EveryWindow.registerCallback(
|
if ((await this.messages).length) {
|
||||||
TOOLBAR_BUTTON_ID,
|
EveryWindow.registerCallback(
|
||||||
this._showToolbarButton,
|
TOOLBAR_BUTTON_ID,
|
||||||
this._hideToolbarButton
|
this._showToolbarButton,
|
||||||
);
|
this._hideToolbarButton
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the panel is hidden we want to run some cleanup
|
// When the panel is hidden we want to run some cleanup
|
||||||
|
@ -88,11 +118,7 @@ class _ToolbarPanelHub {
|
||||||
|
|
||||||
// Render what's new messages into the panel.
|
// Render what's new messages into the panel.
|
||||||
async renderMessages(win, doc, containerId) {
|
async renderMessages(win, doc, containerId) {
|
||||||
const messages = (await this._getMessages({
|
const messages = (await this.messages).sort((m1, m2) => {
|
||||||
template: "whatsnew_panel_message",
|
|
||||||
triggerId: "whatsNewPanelOpened",
|
|
||||||
returnAll: true,
|
|
||||||
})).sort((m1, m2) => {
|
|
||||||
// Sort by published_date in descending order.
|
// Sort by published_date in descending order.
|
||||||
if (m1.content.published_date === m2.content.published_date) {
|
if (m1.content.published_date === m2.content.published_date) {
|
||||||
return 0;
|
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_contextMenu_options.js]
|
||||||
[browser_topsites_section.js]
|
[browser_topsites_section.js]
|
||||||
[browser_asrouter_cfr.js]
|
[browser_asrouter_cfr.js]
|
||||||
skip-if = fission
|
|
||||||
[browser_asrouter_bookmarkpanel.js]
|
[browser_asrouter_bookmarkpanel.js]
|
||||||
|
|
|
@ -206,12 +206,17 @@ describe("ASRouter", () => {
|
||||||
handleMessageRequest: Router.handleMessageRequest,
|
handleMessageRequest: Router.handleMessageRequest,
|
||||||
addImpression: Router.addImpression,
|
addImpression: Router.addImpression,
|
||||||
blockMessageById: Router.blockMessageById,
|
blockMessageById: Router.blockMessageById,
|
||||||
|
dispatch: Router.dispatch,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.calledWithExactly(FakeToolbarPanelHub.init, {
|
assert.calledWithExactly(
|
||||||
getMessages: Router.handleMessageRequest,
|
FakeToolbarPanelHub.init,
|
||||||
});
|
Router.waitForInitialized,
|
||||||
|
{
|
||||||
|
getMessages: Router.handleMessageRequest,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
assert.calledWithExactly(
|
assert.calledWithExactly(
|
||||||
FakeBookmarkPanelHub.init,
|
FakeBookmarkPanelHub.init,
|
||||||
|
|
|
@ -5,12 +5,18 @@ import React from "react";
|
||||||
describe("ModalOverlayWrapper", () => {
|
describe("ModalOverlayWrapper", () => {
|
||||||
let fakeDoc;
|
let fakeDoc;
|
||||||
let sandbox;
|
let sandbox;
|
||||||
|
let header;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
sandbox = sinon.createSandbox();
|
sandbox = sinon.createSandbox();
|
||||||
|
header = document.createElement("div");
|
||||||
|
|
||||||
fakeDoc = {
|
fakeDoc = {
|
||||||
addEventListener: sandbox.stub(),
|
addEventListener: sandbox.stub(),
|
||||||
removeEventListener: sandbox.stub(),
|
removeEventListener: sandbox.stub(),
|
||||||
body: { classList: { add: sandbox.stub(), remove: sandbox.stub() } },
|
body: { classList: { add: sandbox.stub(), remove: sandbox.stub() } },
|
||||||
|
getElementById() {
|
||||||
|
return header;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|
|
@ -970,6 +970,7 @@ describe("Reducers", () => {
|
||||||
lastUpdated: 123,
|
lastUpdated: 123,
|
||||||
loaded: true,
|
loaded: true,
|
||||||
frequency_caps: [],
|
frequency_caps: [],
|
||||||
|
blocked: [],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it("should handle no data from DISCOVERY_STREAM_SPOCS_UPDATE", () => {
|
it("should handle no data from DISCOVERY_STREAM_SPOCS_UPDATE", () => {
|
||||||
|
@ -980,6 +981,21 @@ describe("Reducers", () => {
|
||||||
});
|
});
|
||||||
assert.deepEqual(state.spocs, INITIAL_STATE.DiscoveryStream.spocs);
|
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", () => {
|
it("should not update state for empty action.data on DISCOVERY_STREAM_LINK_BLOCKED", () => {
|
||||||
const newState = DiscoveryStream(undefined, {
|
const newState = DiscoveryStream(undefined, {
|
||||||
type: at.DISCOVERY_STREAM_LINK_BLOCKED,
|
type: at.DISCOVERY_STREAM_LINK_BLOCKED,
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { combineReducers, createStore } from "redux";
|
||||||
import { GlobalOverrider } from "test/unit/utils";
|
import { GlobalOverrider } from "test/unit/utils";
|
||||||
import { INITIAL_STATE, reducers } from "common/Reducers.jsm";
|
import { INITIAL_STATE, reducers } from "common/Reducers.jsm";
|
||||||
import { cardContextTypes } from "content-src/components/Card/types";
|
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 { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
@ -149,18 +150,22 @@ describe("<Card>", () => {
|
||||||
const title = '"fluent"';
|
const title = '"fluent"';
|
||||||
const link = { ...DEFAULT_PROPS.link, title };
|
const link = { ...DEFAULT_PROPS.link, title };
|
||||||
|
|
||||||
wrapper = shallow(<Card {...DEFAULT_PROPS} link={link} />);
|
wrapper = mountCardWithProps({ ...DEFAULT_PROPS, link });
|
||||||
let button = wrapper.find(
|
let button = wrapper.find(ContextMenuButton).find("button");
|
||||||
"button[data-l10n-id='newtab-menu-content-tooltip']"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.equal(button.prop("data-l10n-args"), JSON.stringify({ title }));
|
assert.equal(button.prop("data-l10n-args"), JSON.stringify({ title }));
|
||||||
});
|
});
|
||||||
it("should have .active class, on card-outer if context menu is open", () => {
|
it("should have .active class, on card-outer if context menu is open", () => {
|
||||||
const button = wrapper.find(".context-menu-button");
|
const button = wrapper.find(ContextMenuButton);
|
||||||
assert.isFalse(wrapper.find(".card-outer").hasClass("active"));
|
assert.isFalse(
|
||||||
|
wrapper.find(".card-outer").hasClass("active"),
|
||||||
|
"does not have active class"
|
||||||
|
);
|
||||||
button.simulate("click", { preventDefault: () => {} });
|
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", () => {
|
it("should send SHOW_DOWNLOAD_FILE if we clicked on a download", () => {
|
||||||
const downloadLink = {
|
const downloadLink = {
|
||||||
|
@ -382,7 +387,7 @@ describe("<Card>", () => {
|
||||||
});
|
});
|
||||||
it("should not have a context menu button or LinkMenu", () => {
|
it("should not have a context menu button or LinkMenu", () => {
|
||||||
assert.isFalse(
|
assert.isFalse(
|
||||||
wrapper.find(".context-menu-button").exists(),
|
wrapper.find(ContextMenuButton).exists(),
|
||||||
"context menu button"
|
"context menu button"
|
||||||
);
|
);
|
||||||
assert.isFalse(wrapper.find(LinkMenu).exists(), "LinkMenu");
|
assert.isFalse(wrapper.find(LinkMenu).exists(), "LinkMenu");
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
ContextMenuItem,
|
ContextMenuItem,
|
||||||
} from "content-src/components/ContextMenu/ContextMenu";
|
} from "content-src/components/ContextMenu/ContextMenu";
|
||||||
|
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
|
||||||
import { mount, shallow } from "enzyme";
|
import { mount, shallow } from "enzyme";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
@ -11,6 +12,88 @@ const DEFAULT_PROPS = {
|
||||||
tabbableOptionsLength: 0,
|
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>", () => {
|
describe("<ContextMenu>", () => {
|
||||||
it("should render all the options provided", () => {
|
it("should render all the options provided", () => {
|
||||||
const options = [
|
const options = [
|
||||||
|
@ -79,7 +162,7 @@ describe("<ContextMenu>", () => {
|
||||||
const wrapper = mount(<ContextMenu {...DEFAULT_PROPS} options={options} />);
|
const wrapper = mount(<ContextMenu {...DEFAULT_PROPS} options={options} />);
|
||||||
assert.lengthOf(wrapper.find(".context-menu-item"), 1);
|
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 onClick = sinon.spy();
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
<ContextMenu {...DEFAULT_PROPS} options={[{ label: "item1", onClick }]} />
|
<ContextMenu {...DEFAULT_PROPS} options={[{ label: "item1", onClick }]} />
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { mount, shallow } from "enzyme";
|
import { mount, shallow } from "enzyme";
|
||||||
import { DSLinkMenu } from "content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu";
|
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 { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
@ -18,7 +19,6 @@ describe("<DSLinkMenu>", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should remove active on Menu Update", () => {
|
it("Should remove active on Menu Update", () => {
|
||||||
wrapper.setState({ showContextMenu: true });
|
|
||||||
// Add active class name to DSLinkMenu parent node
|
// Add active class name to DSLinkMenu parent node
|
||||||
// to simulate menu open state
|
// to simulate menu open state
|
||||||
parentNode.classList.add("active");
|
parentNode.classList.add("active");
|
||||||
|
@ -28,7 +28,6 @@ describe("<DSLinkMenu>", () => {
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|
||||||
assert.isEmpty(parentNode.className);
|
assert.isEmpty(parentNode.className);
|
||||||
assert.isFalse(wrapper.state(["showContextMenu"]));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should add active on Menu Show", async () => {
|
it("Should add active on Menu Show", async () => {
|
||||||
|
@ -76,7 +75,7 @@ describe("<DSLinkMenu>", () => {
|
||||||
|
|
||||||
it("should parse args for fluent correctly ", () => {
|
it("should parse args for fluent correctly ", () => {
|
||||||
const title = '"fluent"';
|
const title = '"fluent"';
|
||||||
wrapper = shallow(<DSLinkMenu title={title} />);
|
wrapper = mount(<DSLinkMenu title={title} />);
|
||||||
|
|
||||||
const button = wrapper.find(
|
const button = wrapper.find(
|
||||||
"button[data-l10n-id='newtab-menu-content-tooltip']"
|
"button[data-l10n-id='newtab-menu-content-tooltip']"
|
||||||
|
@ -96,23 +95,25 @@ describe("<DSLinkMenu>", () => {
|
||||||
|
|
||||||
it("should render a context menu button", () => {
|
it("should render a context menu button", () => {
|
||||||
assert.ok(wrapper.exists());
|
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", () => {
|
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: () => {} });
|
button.simulate("click", { preventDefault: () => {} });
|
||||||
assert.equal(wrapper.find(LinkMenu).length, 1);
|
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
|
wrapper
|
||||||
.find(".context-menu-button")
|
.find(ContextMenuButton)
|
||||||
.simulate("click", { preventDefault: () => {} });
|
.simulate("click", { preventDefault: () => {} });
|
||||||
const linkMenuProps = wrapper.find(LinkMenu).props();
|
const linkMenuProps = wrapper.find(LinkMenu).props();
|
||||||
[
|
[
|
||||||
"dispatch",
|
"dispatch",
|
||||||
"onUpdate",
|
|
||||||
"onShow",
|
"onShow",
|
||||||
"site",
|
"site",
|
||||||
"index",
|
"index",
|
||||||
|
@ -124,7 +125,7 @@ describe("<DSLinkMenu>", () => {
|
||||||
|
|
||||||
it("should pass through the correct menu options to LinkMenu", () => {
|
it("should pass through the correct menu options to LinkMenu", () => {
|
||||||
wrapper
|
wrapper
|
||||||
.find(".context-menu-button")
|
.find(ContextMenuButton)
|
||||||
.simulate("click", { preventDefault: () => {} });
|
.simulate("click", { preventDefault: () => {} });
|
||||||
const linkMenuProps = wrapper.find(LinkMenu).props();
|
const linkMenuProps = wrapper.find(LinkMenu).props();
|
||||||
assert.deepEqual(linkMenuProps.options, [
|
assert.deepEqual(linkMenuProps.options, [
|
||||||
|
|
|
@ -164,17 +164,6 @@ describe("<SectionMenu>", () => {
|
||||||
});
|
});
|
||||||
describe(".onClick", () => {
|
describe(".onClick", () => {
|
||||||
const dispatch = sinon.stub();
|
const dispatch = sinon.stub();
|
||||||
const propOptions = [
|
|
||||||
"Separator",
|
|
||||||
"MoveUp",
|
|
||||||
"MoveDown",
|
|
||||||
"RemoveSection",
|
|
||||||
"CollapseSection",
|
|
||||||
"ExpandSection",
|
|
||||||
"ManageSection",
|
|
||||||
"AddTopSite",
|
|
||||||
"PrivacyNotice",
|
|
||||||
];
|
|
||||||
const expectedActionData = {
|
const expectedActionData = {
|
||||||
"newtab-section-menu-move-up": { id: "sectionId", direction: -1 },
|
"newtab-section-menu-move-up": { id: "sectionId", direction: -1 },
|
||||||
"newtab-section-menu-move-down": { id: "sectionId", direction: +1 },
|
"newtab-section-menu-move-down": { id: "sectionId", direction: +1 },
|
||||||
|
@ -197,11 +186,7 @@ describe("<SectionMenu>", () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const { options } = shallow(
|
const { options } = shallow(
|
||||||
<SectionMenu
|
<SectionMenu {...DEFAULT_PROPS} dispatch={dispatch} />
|
||||||
{...DEFAULT_PROPS}
|
|
||||||
dispatch={dispatch}
|
|
||||||
options={propOptions}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
.find(ContextMenu)
|
.find(ContextMenu)
|
||||||
.props();
|
.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 { TopSiteForm } from "content-src/components/TopSites/TopSiteForm";
|
||||||
import { TopSiteFormInput } from "content-src/components/TopSites/TopSiteFormInput";
|
import { TopSiteFormInput } from "content-src/components/TopSites/TopSiteFormInput";
|
||||||
import { _TopSites as TopSites } from "content-src/components/TopSites/TopSites";
|
import { _TopSites as TopSites } from "content-src/components/TopSites/TopSites";
|
||||||
|
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
|
||||||
|
|
||||||
const perfSvc = {
|
const perfSvc = {
|
||||||
mark() {},
|
mark() {},
|
||||||
|
@ -724,24 +725,14 @@ describe("<TopSite>", () => {
|
||||||
});
|
});
|
||||||
it("should render a context menu button", () => {
|
it("should render a context menu button", () => {
|
||||||
const wrapper = shallow(<TopSite link={link} />);
|
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} />);
|
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);
|
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", () => {
|
it("should pass onUpdate, site, options, and index to LinkMenu", () => {
|
||||||
const wrapper = shallow(<TopSite link={link} />);
|
const wrapper = shallow(<TopSite link={link} />);
|
||||||
wrapper
|
|
||||||
.find(".context-menu-button")
|
|
||||||
.simulate("click", { preventDefault: () => {} });
|
|
||||||
const linkMenuProps = wrapper.find(LinkMenu).props();
|
const linkMenuProps = wrapper.find(LinkMenu).props();
|
||||||
["onUpdate", "site", "index", "options"].forEach(prop =>
|
["onUpdate", "site", "index", "options"].forEach(prop =>
|
||||||
assert.property(linkMenuProps, prop)
|
assert.property(linkMenuProps, prop)
|
||||||
|
@ -749,9 +740,6 @@ describe("<TopSite>", () => {
|
||||||
});
|
});
|
||||||
it("should pass through the correct menu options to LinkMenu", () => {
|
it("should pass through the correct menu options to LinkMenu", () => {
|
||||||
const wrapper = shallow(<TopSite link={link} />);
|
const wrapper = shallow(<TopSite link={link} />);
|
||||||
wrapper
|
|
||||||
.find(".context-menu-button")
|
|
||||||
.simulate("click", { preventDefault: () => {} });
|
|
||||||
const linkMenuProps = wrapper.find(LinkMenu).props();
|
const linkMenuProps = wrapper.find(LinkMenu).props();
|
||||||
assert.deepEqual(linkMenuProps.options, [
|
assert.deepEqual(linkMenuProps.options, [
|
||||||
"CheckPinTopSite",
|
"CheckPinTopSite",
|
||||||
|
|
|
@ -702,7 +702,7 @@ describe("selectLayoutRender", () => {
|
||||||
type: "foo3",
|
type: "foo3",
|
||||||
properties: { items: 3 },
|
properties: { items: 3 },
|
||||||
feed: { url: "foo3.com" },
|
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: "foo4", properties: { items: 3 }, feed: { url: "foo4.com" } },
|
||||||
{ type: "foo5" },
|
{ type: "foo5" },
|
||||||
|
@ -751,7 +751,7 @@ describe("selectLayoutRender", () => {
|
||||||
type: "foo3",
|
type: "foo3",
|
||||||
properties: { items: 3 },
|
properties: { items: 3 },
|
||||||
feed: { url: "foo3.com" },
|
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: "foo4", properties: { items: 3 }, feed: { url: "foo4.com" } },
|
||||||
{ type: "foo5" },
|
{ type: "foo5" },
|
||||||
|
@ -840,4 +840,67 @@ describe("selectLayoutRender", () => {
|
||||||
assert.equal(layoutRender[0].components[0].type, "TopSites");
|
assert.equal(layoutRender[0].components[0].type, "TopSites");
|
||||||
assert.equal(layoutRender[0].components[1], undefined);
|
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"
|
"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", () => {
|
describe("#onAction: INIT", () => {
|
||||||
|
|
|
@ -7,6 +7,8 @@ describe("ToolbarBadgeHub", () => {
|
||||||
let sandbox;
|
let sandbox;
|
||||||
let instance;
|
let instance;
|
||||||
let fakeAddImpression;
|
let fakeAddImpression;
|
||||||
|
let fakeDispatch;
|
||||||
|
let isBrowserPrivateStub;
|
||||||
let fxaMessage;
|
let fxaMessage;
|
||||||
let whatsnewMessage;
|
let whatsnewMessage;
|
||||||
let fakeElement;
|
let fakeElement;
|
||||||
|
@ -14,11 +16,15 @@ describe("ToolbarBadgeHub", () => {
|
||||||
let everyWindowStub;
|
let everyWindowStub;
|
||||||
let clearTimeoutStub;
|
let clearTimeoutStub;
|
||||||
let setTimeoutStub;
|
let setTimeoutStub;
|
||||||
|
let addObserverStub;
|
||||||
|
let removeObserverStub;
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
globals = new GlobalOverrider();
|
globals = new GlobalOverrider();
|
||||||
sandbox = sinon.createSandbox();
|
sandbox = sinon.createSandbox();
|
||||||
instance = new _ToolbarBadgeHub();
|
instance = new _ToolbarBadgeHub();
|
||||||
fakeAddImpression = sandbox.stub();
|
fakeAddImpression = sandbox.stub();
|
||||||
|
fakeDispatch = sandbox.stub();
|
||||||
|
isBrowserPrivateStub = sandbox.stub();
|
||||||
const msgs = await PanelTestProvider.getMessages();
|
const msgs = await PanelTestProvider.getMessages();
|
||||||
fxaMessage = msgs.find(({ id }) => id === "FXA_ACCOUNTS_BADGE");
|
fxaMessage = msgs.find(({ id }) => id === "FXA_ACCOUNTS_BADGE");
|
||||||
whatsnewMessage = msgs.find(({ id }) => id.includes("WHATS_NEW_BADGE_"));
|
whatsnewMessage = msgs.find(({ id }) => id.includes("WHATS_NEW_BADGE_"));
|
||||||
|
@ -40,9 +46,30 @@ describe("ToolbarBadgeHub", () => {
|
||||||
};
|
};
|
||||||
clearTimeoutStub = sandbox.stub();
|
clearTimeoutStub = sandbox.stub();
|
||||||
setTimeoutStub = sandbox.stub();
|
setTimeoutStub = sandbox.stub();
|
||||||
globals.set("EveryWindow", everyWindowStub);
|
const fakeWindow = {
|
||||||
globals.set("setTimeout", setTimeoutStub);
|
ownerGlobal: {
|
||||||
globals.set("clearTimeout", clearTimeoutStub);
|
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(() => {
|
afterEach(() => {
|
||||||
sandbox.restore();
|
sandbox.restore();
|
||||||
|
@ -60,8 +87,21 @@ describe("ToolbarBadgeHub", () => {
|
||||||
assert.calledOnce(instance.messageRequest);
|
assert.calledOnce(instance.messageRequest);
|
||||||
assert.calledWithExactly(instance.messageRequest, "toolbarBadgeUpdate");
|
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", () => {
|
describe("#uninit", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
instance.init(sandbox.stub().resolves(), {});
|
||||||
|
});
|
||||||
it("should clear any setTimeout cbs", () => {
|
it("should clear any setTimeout cbs", () => {
|
||||||
instance.init(sandbox.stub().resolves(), {});
|
instance.init(sandbox.stub().resolves(), {});
|
||||||
|
|
||||||
|
@ -72,6 +112,16 @@ describe("ToolbarBadgeHub", () => {
|
||||||
assert.calledOnce(clearTimeoutStub);
|
assert.calledOnce(clearTimeoutStub);
|
||||||
assert.calledWithExactly(clearTimeoutStub, 2);
|
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", () => {
|
describe("messageRequest", () => {
|
||||||
let handleMessageRequestStub;
|
let handleMessageRequestStub;
|
||||||
|
@ -166,6 +216,7 @@ describe("ToolbarBadgeHub", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
instance.init(sandbox.stub().resolves(), {
|
instance.init(sandbox.stub().resolves(), {
|
||||||
addImpression: fakeAddImpression,
|
addImpression: fakeAddImpression,
|
||||||
|
dispatch: fakeDispatch,
|
||||||
});
|
});
|
||||||
sandbox.stub(instance, "addToolbarNotification").returns(fakeElement);
|
sandbox.stub(instance, "addToolbarNotification").returns(fakeElement);
|
||||||
sandbox.stub(instance, "removeToolbarNotification");
|
sandbox.stub(instance, "removeToolbarNotification");
|
||||||
|
@ -212,6 +263,18 @@ describe("ToolbarBadgeHub", () => {
|
||||||
assert.calledOnce(instance.removeToolbarNotification);
|
assert.calledOnce(instance.removeToolbarNotification);
|
||||||
assert.calledWithExactly(instance.removeToolbarNotification, fakeElement);
|
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", () => {
|
it("should unregister notifications when forcing a badge via devtools", () => {
|
||||||
instance.registerBadgeNotificationListener(fxaMessage, { force: true });
|
instance.registerBadgeNotificationListener(fxaMessage, { force: true });
|
||||||
|
|
||||||
|
@ -228,6 +291,16 @@ describe("ToolbarBadgeHub", () => {
|
||||||
|
|
||||||
instance.executeAction({ id: "show-whatsnew-button" });
|
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);
|
assert.calledOnce(stub);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -245,6 +318,7 @@ describe("ToolbarBadgeHub", () => {
|
||||||
let blockMessageByIdStub;
|
let blockMessageByIdStub;
|
||||||
let fakeEvent;
|
let fakeEvent;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
instance.init(sandbox.stub().resolves(), { dispatch: fakeDispatch });
|
||||||
blockMessageByIdStub = sandbox.stub();
|
blockMessageByIdStub = sandbox.stub();
|
||||||
sandbox.stub(instance, "_blockMessageById").value(blockMessageByIdStub);
|
sandbox.stub(instance, "_blockMessageById").value(blockMessageByIdStub);
|
||||||
instance.state = { notification: { id: fxaMessage.id } };
|
instance.state = { notification: { id: fxaMessage.id } };
|
||||||
|
@ -307,6 +381,18 @@ describe("ToolbarBadgeHub", () => {
|
||||||
instance.removeAllNotifications
|
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", () => {
|
it("should remove the event listeners after succesfully focusing the element", () => {
|
||||||
fakeEvent.type = "keypress";
|
fakeEvent.type = "keypress";
|
||||||
fakeEvent.key = "Enter";
|
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 fakeElementById;
|
||||||
let createdElements = [];
|
let createdElements = [];
|
||||||
let eventListeners = {};
|
let eventListeners = {};
|
||||||
|
let addObserverStub;
|
||||||
|
let removeObserverStub;
|
||||||
|
let getBoolPrefStub;
|
||||||
|
let waitForInitializedStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
sandbox = sinon.createSandbox();
|
sandbox = sinon.createSandbox();
|
||||||
globals = new GlobalOverrider();
|
globals = new GlobalOverrider();
|
||||||
instance = new _ToolbarPanelHub();
|
instance = new _ToolbarPanelHub();
|
||||||
|
waitForInitializedStub = sandbox.stub().resolves();
|
||||||
fakeElementById = {
|
fakeElementById = {
|
||||||
setAttribute: sandbox.stub(),
|
setAttribute: sandbox.stub(),
|
||||||
removeAttribute: sandbox.stub(),
|
removeAttribute: sandbox.stub(),
|
||||||
|
@ -59,38 +64,140 @@ describe("ToolbarPanelHub", () => {
|
||||||
registerCallback: sandbox.stub(),
|
registerCallback: sandbox.stub(),
|
||||||
unregisterCallback: sandbox.stub(),
|
unregisterCallback: sandbox.stub(),
|
||||||
};
|
};
|
||||||
|
addObserverStub = sandbox.stub();
|
||||||
|
removeObserverStub = sandbox.stub();
|
||||||
|
getBoolPrefStub = sandbox.stub();
|
||||||
globals.set("EveryWindow", everyWindowStub);
|
globals.set("EveryWindow", everyWindowStub);
|
||||||
|
globals.set("Services", {
|
||||||
|
...Services,
|
||||||
|
prefs: {
|
||||||
|
addObserver: addObserverStub,
|
||||||
|
removeObserver: removeObserverStub,
|
||||||
|
getBoolPref: getBoolPrefStub,
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
instance.uninit();
|
instance.uninit();
|
||||||
sandbox.restore();
|
sandbox.restore();
|
||||||
|
globals.restore();
|
||||||
});
|
});
|
||||||
it("should create an instance", () => {
|
it("should create an instance", () => {
|
||||||
assert.ok(instance);
|
assert.ok(instance);
|
||||||
});
|
});
|
||||||
it("should not enableAppmenuButton() on init() if pref is not enabled", () => {
|
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.enableAppmenuButton = sandbox.stub();
|
||||||
instance.init({ getMessages: () => {} });
|
instance.init(waitForInitializedStub, { getMessages: () => {} });
|
||||||
assert.notCalled(instance.enableAppmenuButton);
|
assert.notCalled(instance.enableAppmenuButton);
|
||||||
});
|
});
|
||||||
it("should enableAppmenuButton() on init() if pref is enabled", () => {
|
it("should enableAppmenuButton() on init() if pref is enabled", async () => {
|
||||||
sandbox.stub(global.Services.prefs, "getBoolPref").returns(true);
|
getBoolPrefStub.returns(true);
|
||||||
instance.enableAppmenuButton = sandbox.stub();
|
instance.enableAppmenuButton = sandbox.stub();
|
||||||
instance.init({ getMessages: () => {} });
|
|
||||||
|
await instance.init(waitForInitializedStub, { getMessages: () => {} });
|
||||||
|
|
||||||
assert.calledOnce(instance.enableAppmenuButton);
|
assert.calledOnce(instance.enableAppmenuButton);
|
||||||
});
|
});
|
||||||
it("should unregisterCallback on uninit()", () => {
|
it("should unregisterCallback on uninit()", () => {
|
||||||
instance.uninit();
|
instance.uninit();
|
||||||
assert.calledTwice(everyWindowStub.unregisterCallback);
|
assert.calledTwice(everyWindowStub.unregisterCallback);
|
||||||
});
|
});
|
||||||
it("should registerCallback on enableAppmenuButton()", () => {
|
it("should observe pref changes on init", async () => {
|
||||||
instance.enableAppmenuButton();
|
await instance.init(waitForInitializedStub, {});
|
||||||
assert.calledOnce(everyWindowStub.registerCallback);
|
|
||||||
|
assert.calledOnce(addObserverStub);
|
||||||
|
assert.calledWithExactly(
|
||||||
|
addObserverStub,
|
||||||
|
"browser.messaging-system.whatsNewPanel.enabled",
|
||||||
|
instance
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it("should registerCallback on enableToolbarButton()", () => {
|
it("should remove the observer on uninit", () => {
|
||||||
instance.enableToolbarButton();
|
instance.uninit();
|
||||||
assert.calledOnce(everyWindowStub.registerCallback);
|
|
||||||
|
assert.calledOnce(removeObserverStub);
|
||||||
|
assert.calledWithExactly(
|
||||||
|
removeObserverStub,
|
||||||
|
"browser.messaging-system.whatsNewPanel.enabled",
|
||||||
|
instance
|
||||||
|
);
|
||||||
|
});
|
||||||
|
describe("#observe", () => {
|
||||||
|
it("should uninit if the pref is turned off", () => {
|
||||||
|
sandbox.stub(instance, "uninit");
|
||||||
|
getBoolPrefStub.returns(false);
|
||||||
|
|
||||||
|
instance.observe(
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"browser.messaging-system.whatsNewPanel.enabled"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.calledOnce(instance.uninit);
|
||||||
|
});
|
||||||
|
it("shouldn't do anything if the pref is true", () => {
|
||||||
|
sandbox.stub(instance, "uninit");
|
||||||
|
getBoolPrefStub.returns(true);
|
||||||
|
|
||||||
|
instance.observe(
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"browser.messaging-system.whatsNewPanel.enabled"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.notCalled(instance.uninit);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("#enableAppmenuButton", () => {
|
||||||
|
it("should registerCallback on enableAppmenuButton() if there are messages", async () => {
|
||||||
|
instance.init(waitForInitializedStub, {
|
||||||
|
getMessages: sandbox.stub().resolves([{}, {}]),
|
||||||
|
});
|
||||||
|
// init calls `enableAppmenuButton`
|
||||||
|
everyWindowStub.registerCallback.resetHistory();
|
||||||
|
|
||||||
|
await instance.enableAppmenuButton();
|
||||||
|
|
||||||
|
assert.calledOnce(everyWindowStub.registerCallback);
|
||||||
|
assert.calledWithExactly(
|
||||||
|
everyWindowStub.registerCallback,
|
||||||
|
"appMenu-whatsnew-button",
|
||||||
|
sinon.match.func,
|
||||||
|
sinon.match.func
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("should not registerCallback on enableAppmenuButton() if there are no messages", async () => {
|
||||||
|
instance.init(waitForInitializedStub, {
|
||||||
|
getMessages: sandbox.stub().resolves([]),
|
||||||
|
});
|
||||||
|
// init calls `enableAppmenuButton`
|
||||||
|
everyWindowStub.registerCallback.resetHistory();
|
||||||
|
|
||||||
|
await instance.enableAppmenuButton();
|
||||||
|
|
||||||
|
assert.notCalled(everyWindowStub.registerCallback);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("#enableToolbarButton", () => {
|
||||||
|
it("should registerCallback on enableToolbarButton if messages.length", async () => {
|
||||||
|
instance.init(waitForInitializedStub, {
|
||||||
|
getMessages: sandbox.stub().resolves([{}, {}]),
|
||||||
|
});
|
||||||
|
|
||||||
|
await instance.enableToolbarButton();
|
||||||
|
|
||||||
|
assert.calledOnce(everyWindowStub.registerCallback);
|
||||||
|
});
|
||||||
|
it("should not registerCallback on enableToolbarButton if no messages", async () => {
|
||||||
|
instance.init(waitForInitializedStub, {
|
||||||
|
getMessages: sandbox.stub().resolves([]),
|
||||||
|
});
|
||||||
|
|
||||||
|
await instance.enableToolbarButton();
|
||||||
|
|
||||||
|
assert.notCalled(everyWindowStub.registerCallback);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
it("should unhide appmenu button on _showAppmenuButton()", () => {
|
it("should unhide appmenu button on _showAppmenuButton()", () => {
|
||||||
instance._showAppmenuButton(fakeWindow);
|
instance._showAppmenuButton(fakeWindow);
|
||||||
|
@ -113,7 +220,7 @@ describe("ToolbarPanelHub", () => {
|
||||||
m => m.template === "whatsnew_panel_message"
|
m => m.template === "whatsnew_panel_message"
|
||||||
);
|
);
|
||||||
messages[0].content.link_text = { string_id: "link_text_id" };
|
messages[0].content.link_text = { string_id: "link_text_id" };
|
||||||
instance.init({
|
instance.init(waitForInitializedStub, {
|
||||||
getMessages: sandbox
|
getMessages: sandbox
|
||||||
.stub()
|
.stub()
|
||||||
.returns([messages[0], messages[2], messages[1]]),
|
.returns([messages[0], messages[2], messages[1]]),
|
||||||
|
@ -143,14 +250,14 @@ describe("ToolbarPanelHub", () => {
|
||||||
const uniqueDates = [
|
const uniqueDates = [
|
||||||
...new Set(messages.map(m => m.content.published_date)),
|
...new Set(messages.map(m => m.content.published_date)),
|
||||||
];
|
];
|
||||||
instance.init({
|
instance.init(waitForInitializedStub, {
|
||||||
getMessages: sandbox.stub().returns(messages),
|
getMessages: sandbox.stub().returns(messages),
|
||||||
});
|
});
|
||||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
||||||
assert.callCount(instance._createDateElement, uniqueDates.length);
|
assert.callCount(instance._createDateElement, uniqueDates.length);
|
||||||
});
|
});
|
||||||
it("should listen for panelhidden and remove the toolbar button", async () => {
|
it("should listen for panelhidden and remove the toolbar button", async () => {
|
||||||
instance.init({
|
instance.init(waitForInitializedStub, {
|
||||||
getMessages: sandbox.stub().returns([]),
|
getMessages: sandbox.stub().returns([]),
|
||||||
});
|
});
|
||||||
fakeDocument.getElementById
|
fakeDocument.getElementById
|
||||||
|
@ -162,7 +269,7 @@ describe("ToolbarPanelHub", () => {
|
||||||
assert.notCalled(fakeElementById.addEventListener);
|
assert.notCalled(fakeElementById.addEventListener);
|
||||||
});
|
});
|
||||||
it("should listen for panelhidden and remove the toolbar button", async () => {
|
it("should listen for panelhidden and remove the toolbar button", async () => {
|
||||||
instance.init({
|
instance.init(waitForInitializedStub, {
|
||||||
getMessages: sandbox.stub().returns([]),
|
getMessages: sandbox.stub().returns([]),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
ac_add_options --enable-official-branding
|
ac_add_options --enable-official-branding
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
. "$topsrcdir/build/mozconfig.common.override"
|
. "$topsrcdir/build/mozconfig.common.override"
|
||||||
|
|
|
@ -5,7 +5,7 @@ MOZ_REQUIRE_SIGNING=0
|
||||||
|
|
||||||
ac_add_options --disable-install-strip
|
ac_add_options --disable-install-strip
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
ac_add_options --enable-instruments
|
ac_add_options --enable-instruments
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ if test `uname -s` != Linux; then
|
||||||
ac_add_options --enable-dtrace
|
ac_add_options --enable-dtrace
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
ac_add_options --with-branding=browser/branding/nightly
|
ac_add_options --with-branding=browser/branding/nightly
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
ac_add_options --enable-official-branding
|
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
|
# safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
|
||||||
# defines.sh during the beta cycle
|
# defines.sh during the beta cycle
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
ac_add_options --enable-official-branding
|
ac_add_options --enable-official-branding
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
unset ENABLE_CLANG_PLUGIN
|
unset ENABLE_CLANG_PLUGIN
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
# Add-on signing is not required for DevEdition
|
# Add-on signing is not required for DevEdition
|
||||||
MOZ_REQUIRE_SIGNING=0
|
MOZ_REQUIRE_SIGNING=0
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
ac_add_options --with-branding=browser/branding/aurora
|
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-win64"
|
||||||
. "$topsrcdir/browser/config/mozconfigs/win64-aarch64/common-opt"
|
. "$topsrcdir/browser/config/mozconfigs/win64-aarch64/common-opt"
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
ac_add_options --with-branding=browser/branding/nightly
|
ac_add_options --with-branding=browser/branding/nightly
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
ac_add_options --enable-official-branding
|
ac_add_options --enable-official-branding
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
unset ENABLE_CLANG_PLUGIN
|
unset ENABLE_CLANG_PLUGIN
|
||||||
|
|
||||||
|
|
|
@ -6,4 +6,6 @@ export MOZ_PGO=1
|
||||||
|
|
||||||
ac_add_options --enable-official-branding
|
ac_add_options --enable-official-branding
|
||||||
|
|
||||||
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
. "$topsrcdir/build/mozconfig.common.override"
|
. "$topsrcdir/build/mozconfig.common.override"
|
||||||
|
|
|
@ -18,7 +18,3 @@ export MOZILLA_OFFICIAL=1
|
||||||
|
|
||||||
# Package js shell.
|
# Package js shell.
|
||||||
export MOZ_PACKAGE_JSSHELL=1
|
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
|
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
|
||||||
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
|
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
|
||||||
|
|
||||||
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
. "$topsrcdir/build/mozconfig.common.override"
|
. "$topsrcdir/build/mozconfig.common.override"
|
||||||
|
|
|
@ -4,4 +4,6 @@
|
||||||
|
|
||||||
ac_add_options --with-branding=browser/branding/nightly
|
ac_add_options --with-branding=browser/branding/nightly
|
||||||
|
|
||||||
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
. "$topsrcdir/build/mozconfig.common.override"
|
. "$topsrcdir/build/mozconfig.common.override"
|
||||||
|
|
|
@ -9,6 +9,8 @@ export MOZ_PGO=1
|
||||||
|
|
||||||
ac_add_options --enable-official-branding
|
ac_add_options --enable-official-branding
|
||||||
|
|
||||||
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
# safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
|
# safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
|
||||||
# defines.sh during the beta cycle
|
# defines.sh during the beta cycle
|
||||||
export BUILDING_RELEASE=1
|
export BUILDING_RELEASE=1
|
||||||
|
|
|
@ -1,15 +1,8 @@
|
||||||
diff -Nru valgrind-3.14.0/debian/changelog valgrind-3.14.0/debian/changelog
|
diff -Nru valgrind-3.15.0/debian/changelog valgrind-3.15.0/debian/changelog
|
||||||
--- valgrind-3.14.0/debian/changelog 2018-11-15 09:21:43.000000000 +0900
|
--- valgrind-3.15.0/debian/changelog 2019-07-14 19:23:29.000000000 +0900
|
||||||
+++ valgrind-3.14.0/debian/changelog 2018-11-30 15:20:10.000000000 +0900
|
+++ valgrind-3.15.0/debian/changelog 2019-07-17 18:19:13.000000000 +0900
|
||||||
@@ -1,3 +1,21 @@
|
@@ -1,3 +1,14 @@
|
||||||
+valgrind (1:3.14.0-1.deb7moz2) wheezy; urgency=medium
|
+valgrind (1:3.15.0-1.deb7moz1) 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
|
|
||||||
+
|
+
|
||||||
+ * Mozilla backport for wheezy.
|
+ * Mozilla backport for wheezy.
|
||||||
+ * debian/control, debian/compat: Drop debhelper compat back to 7, which
|
+ * 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.
|
+ add --parallel back.
|
||||||
+ * debian/valgrind-mpi.install: Use non-multiarch path.
|
+ * 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)
|
* New upstream release
|
||||||
diff -Nru valgrind-3.14.0/debian/compat valgrind-3.14.0/debian/compat
|
diff -Nru valgrind-3.15.0/debian/compat valgrind-3.15.0/debian/compat
|
||||||
--- valgrind-3.14.0/debian/compat 2018-11-15 09:21:43.000000000 +0900
|
--- valgrind-3.15.0/debian/compat 2019-07-14 19:23:29.000000000 +0900
|
||||||
+++ valgrind-3.14.0/debian/compat 2018-11-15 11:45:25.000000000 +0900
|
+++ valgrind-3.15.0/debian/compat 2019-07-17 18:19:13.000000000 +0900
|
||||||
@@ -1 +1 @@
|
@@ -1 +1 @@
|
||||||
-11
|
-12
|
||||||
+7
|
+7
|
||||||
diff -Nru valgrind-3.14.0/debian/control valgrind-3.14.0/debian/control
|
diff -Nru valgrind-3.15.0/debian/control valgrind-3.15.0/debian/control
|
||||||
--- valgrind-3.14.0/debian/control 2018-11-15 09:21:43.000000000 +0900
|
--- valgrind-3.15.0/debian/control 2019-07-14 19:23:29.000000000 +0900
|
||||||
+++ valgrind-3.14.0/debian/control 2018-11-15 11:45:25.000000000 +0900
|
+++ valgrind-3.15.0/debian/control 2019-07-17 18:19:13.000000000 +0900
|
||||||
@@ -2,7 +2,8 @@
|
@@ -2,7 +2,8 @@
|
||||||
Section: devel
|
Section: devel
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Alessandro Ghedini <ghedo@debian.org>
|
Maintainer: Alessandro Ghedini <ghedo@debian.org>
|
||||||
-Build-Depends: debhelper (>= 11),
|
-Build-Depends: debhelper (>= 12),
|
||||||
+Build-Depends: debhelper (>= 7.0.50~),
|
+Build-Depends: debhelper (>= 7.0.50~),
|
||||||
+ dh-autoreconf,
|
+ dh-autoreconf,
|
||||||
gdb,
|
gdb,
|
||||||
gcc-multilib [amd64],
|
gcc-multilib [amd64],
|
||||||
libc6-dev-i386 [amd64],
|
libc6-dev-i386 [amd64],
|
||||||
diff -Nru valgrind-3.14.0/debian/patches/series valgrind-3.14.0/debian/patches/series
|
diff -Nru valgrind-3.15.0/debian/rules valgrind-3.15.0/debian/rules
|
||||||
--- valgrind-3.14.0/debian/patches/series 2018-11-15 09:21:43.000000000 +0900
|
--- valgrind-3.15.0/debian/rules 2019-07-14 19:23:29.000000000 +0900
|
||||||
+++ valgrind-3.14.0/debian/patches/series 2018-11-30 15:19:14.000000000 +0900
|
+++ valgrind-3.15.0/debian/rules 2019-07-17 18:19:13.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
|
|
||||||
@@ -11,7 +11,7 @@
|
@@ -11,7 +11,7 @@
|
||||||
LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
|
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 FAQ.txt
|
||||||
$(MAKE) -C docs html-docs
|
$(MAKE) -C docs html-docs
|
||||||
$(MAKE) -C docs man-pages
|
$(MAKE) -C docs man-pages
|
||||||
diff -Nru valgrind-3.14.0/debian/valgrind-mpi.install valgrind-3.14.0/debian/valgrind-mpi.install
|
diff -Nru valgrind-3.15.0/debian/valgrind-mpi.install valgrind-3.15.0/debian/valgrind-mpi.install
|
||||||
--- valgrind-3.14.0/debian/valgrind-mpi.install 2018-11-15 09:21:43.000000000 +0900
|
--- valgrind-3.15.0/debian/valgrind-mpi.install 2019-07-14 19:23:29.000000000 +0900
|
||||||
+++ valgrind-3.14.0/debian/valgrind-mpi.install 2018-11-15 11:45:25.000000000 +0900
|
+++ valgrind-3.15.0/debian/valgrind-mpi.install 2019-07-17 18:19:13.000000000 +0900
|
||||||
@@ -1 +1 @@
|
@@ -1 +1 @@
|
||||||
-usr/lib/*/valgrind/libmpiwrap*
|
-usr/lib/*/valgrind/libmpiwrap*
|
||||||
+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.
|
# 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':
|
if c_compiler.type == 'clang-cl' and pgo and not profile_generate and value.origin == 'default':
|
||||||
value = ['thin']
|
value = ['cross']
|
||||||
|
|
||||||
if value:
|
if value:
|
||||||
enabled = True
|
enabled = True
|
||||||
|
|
|
@ -26,7 +26,7 @@ else
|
||||||
|
|
||||||
if [ -n "$MOZ_PGO" ]; then
|
if [ -n "$MOZ_PGO" ]; then
|
||||||
if [ -z "$USE_ARTIFACT" ]; then
|
if [ -z "$USE_ARTIFACT" ]; then
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
if [ -n "$MOZ_PGO_PROFILE_USE" ]; then
|
if [ -n "$MOZ_PGO_PROFILE_USE" ]; then
|
||||||
ac_add_options --enable-profile-use
|
ac_add_options --enable-profile-use
|
||||||
ac_add_options --with-pgo-jarlog=/builds/worker/fetches/en-US.log
|
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(
|
static already_AddRefed<ScaledFont> CreateScaledFontForDWriteFont(
|
||||||
IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
|
IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
|
||||||
const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
|
const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
|
||||||
bool aUseEmbeddedBitmap, bool aForceGDIMode,
|
bool aUseEmbeddedBitmap, int aRenderingMode,
|
||||||
IDWriteRenderingParams* aParams, Float aGamma, Float aContrast);
|
IDWriteRenderingParams* aParams, Float aGamma, Float aContrast);
|
||||||
|
|
||||||
static void SetSystemTextQuality(uint8_t aQuality);
|
static void SetSystemTextQuality(uint8_t aQuality);
|
||||||
|
|
|
@ -959,10 +959,11 @@ void Factory::D2DCleanup() {
|
||||||
already_AddRefed<ScaledFont> Factory::CreateScaledFontForDWriteFont(
|
already_AddRefed<ScaledFont> Factory::CreateScaledFontForDWriteFont(
|
||||||
IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
|
IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
|
||||||
const RefPtr<UnscaledFont>& aUnscaledFont, float aSize,
|
const RefPtr<UnscaledFont>& aUnscaledFont, float aSize,
|
||||||
bool aUseEmbeddedBitmap, bool aForceGDIMode,
|
bool aUseEmbeddedBitmap, int aRenderingMode,
|
||||||
IDWriteRenderingParams* aParams, Float aGamma, Float aContrast) {
|
IDWriteRenderingParams* aParams, Float aGamma, Float aContrast) {
|
||||||
return MakeAndAddRef<ScaledFontDWrite>(aFontFace, aUnscaledFont, aSize,
|
return MakeAndAddRef<ScaledFontDWrite>(aFontFace, aUnscaledFont, aSize,
|
||||||
aUseEmbeddedBitmap, aForceGDIMode,
|
aUseEmbeddedBitmap,
|
||||||
|
(DWRITE_RENDERING_MODE)aRenderingMode,
|
||||||
aParams, aGamma, aContrast, aStyle);
|
aParams, aGamma, aContrast, aStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,14 +126,14 @@ static inline DWRITE_FONT_STRETCH DWriteFontStretchFromStretch(
|
||||||
ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace* aFontFace,
|
ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace* aFontFace,
|
||||||
const RefPtr<UnscaledFont>& aUnscaledFont,
|
const RefPtr<UnscaledFont>& aUnscaledFont,
|
||||||
Float aSize, bool aUseEmbeddedBitmap,
|
Float aSize, bool aUseEmbeddedBitmap,
|
||||||
bool aForceGDIMode,
|
DWRITE_RENDERING_MODE aRenderingMode,
|
||||||
IDWriteRenderingParams* aParams,
|
IDWriteRenderingParams* aParams,
|
||||||
Float aGamma, Float aContrast,
|
Float aGamma, Float aContrast,
|
||||||
const gfxFontStyle* aStyle)
|
const gfxFontStyle* aStyle)
|
||||||
: ScaledFontBase(aUnscaledFont, aSize),
|
: ScaledFontBase(aUnscaledFont, aSize),
|
||||||
mFontFace(aFontFace),
|
mFontFace(aFontFace),
|
||||||
mUseEmbeddedBitmap(aUseEmbeddedBitmap),
|
mUseEmbeddedBitmap(aUseEmbeddedBitmap),
|
||||||
mForceGDIMode(aForceGDIMode),
|
mRenderingMode(aRenderingMode),
|
||||||
mParams(aParams),
|
mParams(aParams),
|
||||||
mGamma(aGamma),
|
mGamma(aGamma),
|
||||||
mContrast(aContrast) {
|
mContrast(aContrast) {
|
||||||
|
@ -184,7 +184,7 @@ SkTypeface* ScaledFontDWrite::CreateSkTypeface() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle,
|
return SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle,
|
||||||
mForceGDIMode, gamma, contrast);
|
mRenderingMode, gamma, contrast);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -416,7 +416,7 @@ ScaledFontDWrite::InstanceData::InstanceData(
|
||||||
const wr::FontInstanceOptions* aOptions,
|
const wr::FontInstanceOptions* aOptions,
|
||||||
const wr::FontInstancePlatformOptions* aPlatformOptions)
|
const wr::FontInstancePlatformOptions* aPlatformOptions)
|
||||||
: mUseEmbeddedBitmap(false),
|
: mUseEmbeddedBitmap(false),
|
||||||
mForceGDIMode(false),
|
mRenderingMode(DWRITE_RENDERING_MODE_DEFAULT),
|
||||||
mGamma(2.2f),
|
mGamma(2.2f),
|
||||||
mContrast(1.0f) {
|
mContrast(1.0f) {
|
||||||
if (aOptions) {
|
if (aOptions) {
|
||||||
|
@ -424,7 +424,11 @@ ScaledFontDWrite::InstanceData::InstanceData(
|
||||||
mUseEmbeddedBitmap = true;
|
mUseEmbeddedBitmap = true;
|
||||||
}
|
}
|
||||||
if (aOptions->flags & wr::FontInstanceFlags_FORCE_GDI) {
|
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) {
|
if (aPlatformOptions) {
|
||||||
|
@ -505,6 +509,16 @@ bool ScaledFontDWrite::GetWRFontInstanceOptions(
|
||||||
} else {
|
} else {
|
||||||
options.flags |= wr::FontInstanceFlags_SUBPIXEL_POSITION;
|
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()) {
|
if (Factory::GetBGRSubpixelOrder()) {
|
||||||
options.flags |= wr::FontInstanceFlags_SUBPIXEL_BGR;
|
options.flags |= wr::FontInstanceFlags_SUBPIXEL_BGR;
|
||||||
}
|
}
|
||||||
|
@ -594,7 +608,7 @@ already_AddRefed<ScaledFont> UnscaledFontDWrite::CreateScaledFont(
|
||||||
|
|
||||||
RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(
|
RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(
|
||||||
face, this, aGlyphSize, instanceData.mUseEmbeddedBitmap,
|
face, this, aGlyphSize, instanceData.mUseEmbeddedBitmap,
|
||||||
instanceData.mForceGDIMode, nullptr, instanceData.mGamma,
|
instanceData.mRenderingMode, nullptr, instanceData.mGamma,
|
||||||
instanceData.mContrast);
|
instanceData.mContrast);
|
||||||
|
|
||||||
if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
|
if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
|
||||||
|
|
|
@ -27,15 +27,17 @@ class ScaledFontDWrite final : public ScaledFontBase {
|
||||||
: ScaledFontBase(aUnscaledFont, aSize),
|
: ScaledFontBase(aUnscaledFont, aSize),
|
||||||
mFontFace(aFont),
|
mFontFace(aFont),
|
||||||
mUseEmbeddedBitmap(false),
|
mUseEmbeddedBitmap(false),
|
||||||
mForceGDIMode(false),
|
mRenderingMode(DWRITE_RENDERING_MODE_DEFAULT),
|
||||||
mGamma(2.2f),
|
mGamma(2.2f),
|
||||||
mContrast(1.0f) {}
|
mContrast(1.0f) {}
|
||||||
|
|
||||||
ScaledFontDWrite(IDWriteFontFace* aFontFace,
|
ScaledFontDWrite(IDWriteFontFace* aFontFace,
|
||||||
const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
|
const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
|
||||||
bool aUseEmbeddedBitmap, bool aForceGDIMode,
|
bool aUseEmbeddedBitmap,
|
||||||
IDWriteRenderingParams* aParams, Float aGamma,
|
DWRITE_RENDERING_MODE aRenderingMode,
|
||||||
Float aContrast, const gfxFontStyle* aStyle = nullptr);
|
IDWriteRenderingParams* aParams,
|
||||||
|
Float aGamma, Float aContrast,
|
||||||
|
const gfxFontStyle* aStyle = nullptr);
|
||||||
|
|
||||||
FontType GetType() const override { return FontType::DWRITE; }
|
FontType GetType() const override { return FontType::DWRITE; }
|
||||||
|
|
||||||
|
@ -61,8 +63,9 @@ class ScaledFontDWrite final : public ScaledFontBase {
|
||||||
|
|
||||||
AntialiasMode GetDefaultAAMode() override;
|
AntialiasMode GetDefaultAAMode() override;
|
||||||
|
|
||||||
bool UseEmbeddedBitmaps() { return mUseEmbeddedBitmap; }
|
bool UseEmbeddedBitmaps() const { return mUseEmbeddedBitmap; }
|
||||||
bool ForceGDIMode() { return mForceGDIMode; }
|
bool ForceGDIMode() const { return mRenderingMode == DWRITE_RENDERING_MODE_GDI_CLASSIC; }
|
||||||
|
DWRITE_RENDERING_MODE GetRenderingMode() const { return mRenderingMode; }
|
||||||
|
|
||||||
#ifdef USE_SKIA
|
#ifdef USE_SKIA
|
||||||
SkTypeface* CreateSkTypeface() override;
|
SkTypeface* CreateSkTypeface() override;
|
||||||
|
@ -71,7 +74,7 @@ class ScaledFontDWrite final : public ScaledFontBase {
|
||||||
|
|
||||||
RefPtr<IDWriteFontFace> mFontFace;
|
RefPtr<IDWriteFontFace> mFontFace;
|
||||||
bool mUseEmbeddedBitmap;
|
bool mUseEmbeddedBitmap;
|
||||||
bool mForceGDIMode;
|
DWRITE_RENDERING_MODE mRenderingMode;
|
||||||
// DrawTargetD2D1 requires the IDWriteRenderingParams,
|
// DrawTargetD2D1 requires the IDWriteRenderingParams,
|
||||||
// but we also separately need to store the gamma and contrast
|
// but we also separately need to store the gamma and contrast
|
||||||
// since Skia needs to be able to access these without having
|
// since Skia needs to be able to access these without having
|
||||||
|
@ -94,7 +97,7 @@ class ScaledFontDWrite final : public ScaledFontBase {
|
||||||
struct InstanceData {
|
struct InstanceData {
|
||||||
explicit InstanceData(ScaledFontDWrite* aScaledFont)
|
explicit InstanceData(ScaledFontDWrite* aScaledFont)
|
||||||
: mUseEmbeddedBitmap(aScaledFont->mUseEmbeddedBitmap),
|
: mUseEmbeddedBitmap(aScaledFont->mUseEmbeddedBitmap),
|
||||||
mForceGDIMode(aScaledFont->mForceGDIMode),
|
mRenderingMode(aScaledFont->mRenderingMode),
|
||||||
mGamma(aScaledFont->mGamma),
|
mGamma(aScaledFont->mGamma),
|
||||||
mContrast(aScaledFont->mContrast) {}
|
mContrast(aScaledFont->mContrast) {}
|
||||||
|
|
||||||
|
@ -102,7 +105,7 @@ class ScaledFontDWrite final : public ScaledFontBase {
|
||||||
const wr::FontInstancePlatformOptions* aPlatformOptions);
|
const wr::FontInstancePlatformOptions* aPlatformOptions);
|
||||||
|
|
||||||
bool mUseEmbeddedBitmap;
|
bool mUseEmbeddedBitmap;
|
||||||
bool mForceGDIMode;
|
DWRITE_RENDERING_MODE mRenderingMode;
|
||||||
Float mGamma;
|
Float mGamma;
|
||||||
Float mContrast;
|
Float mContrast;
|
||||||
};
|
};
|
||||||
|
|
|
@ -53,7 +53,7 @@ struct IDWriteFontFallback;
|
||||||
SK_API SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
|
SK_API SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
|
||||||
IDWriteFontFace* aFontFace,
|
IDWriteFontFace* aFontFace,
|
||||||
SkFontStyle aStyle,
|
SkFontStyle aStyle,
|
||||||
bool aForceGDI,
|
DWRITE_RENDERING_MODE aRenderingMode,
|
||||||
float aGamma,
|
float aGamma,
|
||||||
float aContrast);
|
float aContrast);
|
||||||
|
|
||||||
|
|
|
@ -351,11 +351,11 @@ SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
|
||||||
SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
|
SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
|
||||||
IDWriteFontFace* aFontFace,
|
IDWriteFontFace* aFontFace,
|
||||||
SkFontStyle aStyle,
|
SkFontStyle aStyle,
|
||||||
bool aForceGDI,
|
DWRITE_RENDERING_MODE aRenderingMode,
|
||||||
float aGamma,
|
float aGamma,
|
||||||
float aContrast)
|
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) ||
|
range.fVersion >= 1) ||
|
||||||
realTextSize > SkIntToScalar(20) || !is_hinted(this, typeface)) {
|
realTextSize > SkIntToScalar(20) || !is_hinted(this, typeface)) {
|
||||||
fTextSizeRender = realTextSize;
|
fTextSizeRender = realTextSize;
|
||||||
|
fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
|
||||||
fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
|
fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
|
||||||
fTextSizeMeasure = realTextSize;
|
fTextSizeMeasure = realTextSize;
|
||||||
fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
|
fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
|
||||||
|
|
||||||
IDWriteRenderingParams* params = sk_get_dwrite_default_rendering_params();
|
|
||||||
DWriteFontTypeface* typeface = static_cast<DWriteFontTypeface*>(getTypeface());
|
DWriteFontTypeface* typeface = static_cast<DWriteFontTypeface*>(getTypeface());
|
||||||
if (params &&
|
switch (typeface->GetRenderingMode()) {
|
||||||
!SUCCEEDED(typeface->fDWriteFontFace->GetRecommendedRenderingMode(
|
case DWRITE_RENDERING_MODE_NATURAL:
|
||||||
fTextSizeRender,
|
case DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC:
|
||||||
1.0f,
|
fRenderingMode = typeface->GetRenderingMode();
|
||||||
fMeasuringMode,
|
break;
|
||||||
params,
|
default:
|
||||||
&fRenderingMode))) {
|
if (IDWriteRenderingParams* params = sk_get_dwrite_default_rendering_params()) {
|
||||||
fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
|
typeface->fDWriteFontFace->GetRecommendedRenderingMode(
|
||||||
|
fTextSizeRender, 1.0f, fMeasuringMode, params, &fRenderingMode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't support outline mode right now.
|
// We don't support outline mode right now.
|
||||||
|
|
|
@ -57,7 +57,7 @@ private:
|
||||||
, fDWriteFontFamily(SkSafeRefComPtr(fontFamily))
|
, fDWriteFontFamily(SkSafeRefComPtr(fontFamily))
|
||||||
, fDWriteFont(SkSafeRefComPtr(font))
|
, fDWriteFont(SkSafeRefComPtr(font))
|
||||||
, fDWriteFontFace(SkRefComPtr(fontFace))
|
, fDWriteFontFace(SkRefComPtr(fontFace))
|
||||||
, fForceGDI(false)
|
, fRenderingMode(DWRITE_RENDERING_MODE_DEFAULT)
|
||||||
, fGamma(2.2f)
|
, fGamma(2.2f)
|
||||||
, fContrast(1.0f)
|
, fContrast(1.0f)
|
||||||
{
|
{
|
||||||
|
@ -105,20 +105,21 @@ public:
|
||||||
static DWriteFontTypeface* Create(IDWriteFactory* factory,
|
static DWriteFontTypeface* Create(IDWriteFactory* factory,
|
||||||
IDWriteFontFace* fontFace,
|
IDWriteFontFace* fontFace,
|
||||||
SkFontStyle aStyle,
|
SkFontStyle aStyle,
|
||||||
bool aForceGDI,
|
DWRITE_RENDERING_MODE aRenderingMode,
|
||||||
float aGamma,
|
float aGamma,
|
||||||
float aContrast) {
|
float aContrast) {
|
||||||
DWriteFontTypeface* typeface =
|
DWriteFontTypeface* typeface =
|
||||||
new DWriteFontTypeface(aStyle, factory, fontFace,
|
new DWriteFontTypeface(aStyle, factory, fontFace,
|
||||||
nullptr, nullptr,
|
nullptr, nullptr,
|
||||||
nullptr, nullptr);
|
nullptr, nullptr);
|
||||||
typeface->fForceGDI = aForceGDI;
|
typeface->fRenderingMode = aRenderingMode;
|
||||||
typeface->fGamma = aGamma;
|
typeface->fGamma = aGamma;
|
||||||
typeface->fContrast = aContrast;
|
typeface->fContrast = aContrast;
|
||||||
return typeface;
|
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:
|
protected:
|
||||||
void weak_dispose() const override {
|
void weak_dispose() const override {
|
||||||
|
@ -156,7 +157,7 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef SkTypeface INHERITED;
|
typedef SkTypeface INHERITED;
|
||||||
bool fForceGDI;
|
DWRITE_RENDERING_MODE fRenderingMode;
|
||||||
float fGamma;
|
float fGamma;
|
||||||
float fContrast;
|
float fContrast;
|
||||||
};
|
};
|
||||||
|
|
|
@ -646,11 +646,14 @@ already_AddRefed<ScaledFont> gfxDWriteFont::GetScaledFont(
|
||||||
: gfxWindowsPlatform::TEXT_RENDERING_NORMAL)
|
: gfxWindowsPlatform::TEXT_RENDERING_NORMAL)
|
||||||
: gfxWindowsPlatform::TEXT_RENDERING_NO_CLEARTYPE);
|
: gfxWindowsPlatform::TEXT_RENDERING_NO_CLEARTYPE);
|
||||||
|
|
||||||
|
DWRITE_RENDERING_MODE renderingMode =
|
||||||
|
forceGDI ? DWRITE_RENDERING_MODE_GDI_CLASSIC : params->GetRenderingMode();
|
||||||
|
|
||||||
const gfxFontStyle* fontStyle = GetStyle();
|
const gfxFontStyle* fontStyle = GetStyle();
|
||||||
mAzureScaledFont = Factory::CreateScaledFontForDWriteFont(
|
mAzureScaledFont = Factory::CreateScaledFontForDWriteFont(
|
||||||
mFontFace, fontStyle, GetUnscaledFont(), GetAdjustedSize(),
|
mFontFace, fontStyle, GetUnscaledFont(), GetAdjustedSize(),
|
||||||
useEmbeddedBitmap, forceGDI, params, params->GetGamma(),
|
useEmbeddedBitmap, (int)renderingMode,
|
||||||
params->GetEnhancedContrast());
|
params, params->GetGamma(), params->GetEnhancedContrast());
|
||||||
if (!mAzureScaledFont) {
|
if (!mAzureScaledFont) {
|
||||||
return nullptr;
|
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
|
// 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
|
// 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.
|
// 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
|
// See if the required capacity fits within the already-allocated space
|
||||||
if (mCapacity + aGlyphCount <= mBufSize) {
|
if (mCapacity <= mBufSize) {
|
||||||
mCapacity += aGlyphCount;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// We need to grow the buffer: determine a new size, allocate, and
|
// 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
|
// copy the existing data over if we didn't use realloc (which would
|
||||||
// do it automatically).
|
// do it automatically).
|
||||||
mBufSize = std::max(mCapacity + aGlyphCount, mBufSize * 2);
|
mBufSize = std::max(mCapacity, mBufSize * 2);
|
||||||
if (mBuffer == *mAutoBuffer.addr()) {
|
if (mBuffer == *mAutoBuffer.addr()) {
|
||||||
// switching from autobuffer to malloc, so we need to copy
|
// switching from autobuffer to malloc, so we need to copy
|
||||||
mBuffer = reinterpret_cast<Glyph*>(moz_xmalloc(mBufSize * sizeof(Glyph)));
|
mBuffer = reinterpret_cast<Glyph*>(moz_xmalloc(mBufSize * sizeof(Glyph)));
|
||||||
|
@ -1596,12 +1602,15 @@ class GlyphBufferAzure {
|
||||||
mBuffer = reinterpret_cast<Glyph*>(
|
mBuffer = reinterpret_cast<Glyph*>(
|
||||||
moz_xrealloc(mBuffer, mBufSize * sizeof(Glyph)));
|
moz_xrealloc(mBuffer, mBufSize * sizeof(Glyph)));
|
||||||
}
|
}
|
||||||
mCapacity += aGlyphCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputGlyph(uint32_t aGlyphID, const gfx::Point& aPt) {
|
void OutputGlyph(uint32_t aGlyphID, const gfx::Point& aPt) {
|
||||||
// Check that AddCapacity has been used appropriately!
|
// If the buffer is full, flush to make room for the new glyph.
|
||||||
MOZ_ASSERT(mNumGlyphs < mCapacity);
|
if (mNumGlyphs >= mCapacity) {
|
||||||
|
// Check that AddCapacity has been used appropriately!
|
||||||
|
MOZ_ASSERT(mCapacity > 0 && mNumGlyphs == mCapacity);
|
||||||
|
Flush();
|
||||||
|
}
|
||||||
Glyph* glyph = mBuffer + mNumGlyphs++;
|
Glyph* glyph = mBuffer + mNumGlyphs++;
|
||||||
glyph->mIndex = aGlyphID;
|
glyph->mIndex = aGlyphID;
|
||||||
glyph->mPosition = aPt;
|
glyph->mPosition = aPt;
|
||||||
|
@ -1805,7 +1814,7 @@ bool gfxFont::DrawGlyphs(const gfxShapedText* aShapedText,
|
||||||
|
|
||||||
// Allocate buffer space for the run, assuming all simple glyphs.
|
// Allocate buffer space for the run, assuming all simple glyphs.
|
||||||
uint32_t capacityMult = 1 + aBuffer.mFontParams.extraStrikes;
|
uint32_t capacityMult = 1 + aBuffer.mFontParams.extraStrikes;
|
||||||
aBuffer.AddCapacity(capacityMult * aCount);
|
aBuffer.AddCapacity(aCount, capacityMult);
|
||||||
|
|
||||||
bool emittedGlyphs = false;
|
bool emittedGlyphs = false;
|
||||||
|
|
||||||
|
@ -1825,7 +1834,7 @@ bool gfxFont::DrawGlyphs(const gfxShapedText* aShapedText,
|
||||||
uint32_t glyphCount = glyphData->GetGlyphCount();
|
uint32_t glyphCount = glyphData->GetGlyphCount();
|
||||||
if (glyphCount > 0) {
|
if (glyphCount > 0) {
|
||||||
// Add extra buffer capacity to allow for multiple-glyph entry.
|
// Add extra buffer capacity to allow for multiple-glyph entry.
|
||||||
aBuffer.AddCapacity(capacityMult * (glyphCount - 1));
|
aBuffer.AddCapacity(glyphCount - 1, capacityMult);
|
||||||
const gfxShapedText::DetailedGlyph* details =
|
const gfxShapedText::DetailedGlyph* details =
|
||||||
aShapedText->GetDetailedGlyphs(aOffset + i);
|
aShapedText->GetDetailedGlyphs(aOffset + i);
|
||||||
MOZ_ASSERT(details, "missing DetailedGlyph!");
|
MOZ_ASSERT(details, "missing DetailedGlyph!");
|
||||||
|
|
|
@ -122,7 +122,15 @@ void brush_vs(
|
||||||
|
|
||||||
vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
|
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;
|
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
|
// Derive the texture coordinates for this image, based on
|
||||||
// whether the source image is a local-space or screen-space
|
// whether the source image is a local-space or screen-space
|
||||||
// image.
|
// image.
|
||||||
|
@ -137,14 +145,6 @@ void brush_vs(
|
||||||
default:
|
default:
|
||||||
break;
|
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
|
#endif
|
||||||
|
|
||||||
// Offset and scale vUv here to avoid doing it in the fragment shader.
|
// Offset and scale vUv here to avoid doing it in the fragment shader.
|
||||||
|
|
|
@ -1226,7 +1226,7 @@ impl BatchBuilder {
|
||||||
textures,
|
textures,
|
||||||
[
|
[
|
||||||
ShaderColorMode::Image as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
|
ShaderColorMode::Image as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
|
||||||
RasterizationSpace::Screen as i32,
|
RasterizationSpace::Local as i32,
|
||||||
get_shader_opacity(1.0),
|
get_shader_opacity(1.0),
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
|
|
|
@ -240,7 +240,7 @@ impl FrameBuilder {
|
||||||
retained_tiles: RetainedTiles,
|
retained_tiles: RetainedTiles,
|
||||||
globals: FrameGlobalResources,
|
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.pending_retained_tiles = retained_tiles;
|
||||||
self.globals = globals;
|
self.globals = globals;
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,26 +111,35 @@ struct PictureInfo {
|
||||||
_spatial_node_index: SpatialNodeIndex,
|
_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
|
/// Stores a list of cached picture tiles that are retained
|
||||||
/// between new scenes.
|
/// between new scenes.
|
||||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||||
pub struct RetainedTiles {
|
pub struct RetainedTiles {
|
||||||
/// The tiles retained between display lists.
|
/// The tiles retained between display lists.
|
||||||
#[cfg_attr(feature = "capture", serde(skip))] //TODO
|
#[cfg_attr(feature = "capture", serde(skip))] //TODO
|
||||||
pub tiles: FastHashMap<TileKey, Tile>,
|
pub caches: FastHashMap<usize, PictureCacheState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RetainedTiles {
|
impl RetainedTiles {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
RetainedTiles {
|
RetainedTiles {
|
||||||
tiles: FastHashMap::default(),
|
caches: FastHashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merge items from one retained tiles into another.
|
/// Merge items from one retained tiles into another.
|
||||||
pub fn merge(&mut self, other: RetainedTiles) {
|
pub fn merge(&mut self, other: RetainedTiles) {
|
||||||
assert!(self.tiles.is_empty() || other.tiles.is_empty());
|
assert!(self.caches.is_empty() || other.caches.is_empty());
|
||||||
self.tiles.extend(other.tiles);
|
if self.caches.is_empty() {
|
||||||
|
self.caches = other.caches;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,14 +207,6 @@ impl From<PropertyBinding<f32>> for OpacityBinding {
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct TileId(usize);
|
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.
|
/// Information about a cached tile.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Tile {
|
pub struct Tile {
|
||||||
|
@ -504,7 +505,7 @@ pub struct TileCacheInstance {
|
||||||
/// The positioning node for this tile cache.
|
/// The positioning node for this tile cache.
|
||||||
pub spatial_node_index: SpatialNodeIndex,
|
pub spatial_node_index: SpatialNodeIndex,
|
||||||
/// Hash of tiles present in this picture.
|
/// 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.
|
/// A helper struct to map local rects into surface coords.
|
||||||
map_local_to_surface: SpaceMapper<LayoutPixel, PicturePixel>,
|
map_local_to_surface: SpaceMapper<LayoutPixel, PicturePixel>,
|
||||||
/// List of opacity bindings, with some extra information
|
/// List of opacity bindings, with some extra information
|
||||||
|
@ -525,7 +526,7 @@ pub struct TileCacheInstance {
|
||||||
/// Local clip rect for this tile cache.
|
/// Local clip rect for this tile cache.
|
||||||
pub local_clip_rect: PictureRect,
|
pub local_clip_rect: PictureRect,
|
||||||
/// A list of tiles that are valid and visible, which should be drawn to the main scene.
|
/// 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.
|
/// The world space viewport that this tile cache draws into.
|
||||||
/// Any clips outside this viewport can be ignored (and must be removed so that
|
/// Any clips outside this viewport can be ignored (and must be removed so that
|
||||||
/// we can draw outside the bounds of the viewport).
|
/// 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
|
/// The allowed subpixel mode for this surface, which depends on the detected
|
||||||
/// opacity of the background.
|
/// opacity of the background.
|
||||||
pub subpixel_mode: SubpixelMode,
|
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 {
|
impl TileCacheInstance {
|
||||||
|
@ -572,6 +577,7 @@ impl TileCacheInstance {
|
||||||
background_color,
|
background_color,
|
||||||
opaque_rect: PictureRect::zero(),
|
opaque_rect: PictureRect::zero(),
|
||||||
subpixel_mode: SubpixelMode::Allow,
|
subpixel_mode: SubpixelMode::Allow,
|
||||||
|
fract_offset: PictureVector2D::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,6 +645,53 @@ impl TileCacheInstance {
|
||||||
frame_context.clip_scroll_tree,
|
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
|
let spatial_node = &frame_context
|
||||||
.clip_scroll_tree
|
.clip_scroll_tree
|
||||||
.spatial_nodes[self.spatial_node_index.0 as usize];
|
.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_p0 = TileOffset::new(x0, y0);
|
||||||
self.tile_bounds_p1 = TileOffset::new(x1, y1);
|
self.tile_bounds_p1 = TileOffset::new(x1, y1);
|
||||||
|
|
||||||
// TODO(gw): Tidy this up as we add better support for retaining
|
let mut world_culling_rect = WorldRect::zero();
|
||||||
// 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 old_tiles = mem::replace(
|
let mut old_tiles = mem::replace(
|
||||||
&mut self.tiles,
|
&mut self.tiles,
|
||||||
FastHashMap::default(),
|
FastHashMap::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut world_culling_rect = WorldRect::zero();
|
|
||||||
|
|
||||||
for y in y0 .. y1 {
|
for y in y0 .. y1 {
|
||||||
for x in x0 .. x1 {
|
for x in x0 .. x1 {
|
||||||
let key = TileKey {
|
let key = TileOffset::new(x, y);
|
||||||
offset: TileOffset::new(x, y),
|
|
||||||
slice: self.slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut tile = old_tiles
|
let mut tile = old_tiles
|
||||||
.remove(&key)
|
.remove(&key)
|
||||||
|
@ -784,10 +822,13 @@ impl TileCacheInstance {
|
||||||
Tile::new(next_id)
|
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(
|
tile.rect = PictureRect::new(
|
||||||
PicturePoint::new(
|
PicturePoint::new(
|
||||||
x as f32 * self.tile_size.width,
|
x as f32 * self.tile_size.width + fract_offset.x,
|
||||||
y as f32 * self.tile_size.height,
|
y as f32 * self.tile_size.height + fract_offset.y,
|
||||||
),
|
),
|
||||||
self.tile_size,
|
self.tile_size,
|
||||||
);
|
);
|
||||||
|
@ -808,8 +849,9 @@ impl TileCacheInstance {
|
||||||
|
|
||||||
// Do tile invalidation for any dependencies that we know now.
|
// Do tile invalidation for any dependencies that we know now.
|
||||||
for (_, tile) in &mut self.tiles {
|
for (_, tile) in &mut self.tiles {
|
||||||
// Start frame assuming that the tile has the same content.
|
// Start frame assuming that the tile has the same content,
|
||||||
tile.is_same_content = true;
|
// unless the fractional offset of the transform root changed.
|
||||||
|
tile.is_same_content = !fract_changed;
|
||||||
|
|
||||||
// Content has changed if any opacity bindings changed.
|
// Content has changed if any opacity bindings changed.
|
||||||
for binding in tile.descriptor.opacity_bindings.items() {
|
for binding in tile.descriptor.opacity_bindings.items() {
|
||||||
|
@ -1043,10 +1085,7 @@ impl TileCacheInstance {
|
||||||
for y in p0.y .. p1.y {
|
for y in p0.y .. p1.y {
|
||||||
for x in p0.x .. p1.x {
|
for x in p0.x .. p1.x {
|
||||||
// TODO(gw): Convert to 2d array temporarily to avoid hash lookups per-tile?
|
// TODO(gw): Convert to 2d array temporarily to avoid hash lookups per-tile?
|
||||||
let key = TileKey {
|
let key = TileOffset::new(x, y);
|
||||||
slice: self.slice,
|
|
||||||
offset: TileOffset::new(x, y),
|
|
||||||
};
|
|
||||||
let tile = self.tiles.get_mut(&key).expect("bug: no tile");
|
let tile = self.tiles.get_mut(&key).expect("bug: no tile");
|
||||||
|
|
||||||
// Mark if the tile is cacheable at all.
|
// Mark if the tile is cacheable at all.
|
||||||
|
@ -1239,29 +1278,9 @@ impl TileCacheInstance {
|
||||||
TILE_SIZE_WIDTH,
|
TILE_SIZE_WIDTH,
|
||||||
TILE_SIZE_HEIGHT,
|
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(
|
resource_cache.texture_cache.update_picture_cache(
|
||||||
tile_size,
|
tile_size,
|
||||||
&mut tile.handle,
|
&mut tile.handle,
|
||||||
uv_rect_kind,
|
|
||||||
gpu_cache,
|
gpu_cache,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2048,7 +2067,15 @@ impl PicturePrimitive {
|
||||||
retained_tiles: &mut RetainedTiles,
|
retained_tiles: &mut RetainedTiles,
|
||||||
) {
|
) {
|
||||||
if let Some(tile_cache) = self.tile_cache.take() {
|
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;
|
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_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);
|
let cache_item = frame_state.resource_cache.texture_cache.get(&tile.handle);
|
||||||
|
|
||||||
|
@ -2449,7 +2476,7 @@ impl PicturePrimitive {
|
||||||
},
|
},
|
||||||
tile_size,
|
tile_size,
|
||||||
pic_index,
|
pic_index,
|
||||||
content_origin,
|
content_origin.to_i32(),
|
||||||
UvRectKind::Rect,
|
UvRectKind::Rect,
|
||||||
surface_spatial_node_index,
|
surface_spatial_node_index,
|
||||||
device_pixel_scale,
|
device_pixel_scale,
|
||||||
|
|
|
@ -113,6 +113,10 @@ fn dwrite_render_mode(
|
||||||
FontRenderMode::Alpha | FontRenderMode::Subpixel => {
|
FontRenderMode::Alpha | FontRenderMode::Subpixel => {
|
||||||
if bitmaps || font.flags.contains(FontInstanceFlags::FORCE_GDI) {
|
if bitmaps || font.flags.contains(FontInstanceFlags::FORCE_GDI) {
|
||||||
dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC
|
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 {
|
} else {
|
||||||
font_face.get_recommended_rendering_mode_default_params(em_size, 1.0, measure_mode)
|
font_face.get_recommended_rendering_mode_default_params(em_size, 1.0, measure_mode)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1316,7 +1316,6 @@ impl TextureCache {
|
||||||
&mut self,
|
&mut self,
|
||||||
tile_size: DeviceIntSize,
|
tile_size: DeviceIntSize,
|
||||||
handle: &mut TextureCacheHandle,
|
handle: &mut TextureCacheHandle,
|
||||||
uv_rect_kind: UvRectKind,
|
|
||||||
gpu_cache: &mut GpuCache,
|
gpu_cache: &mut GpuCache,
|
||||||
) {
|
) {
|
||||||
debug_assert!(self.now.is_valid());
|
debug_assert!(self.now.is_valid());
|
||||||
|
@ -1348,11 +1347,10 @@ impl TextureCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload the resource rect and texture array layer.
|
// Upload the resource rect and texture array layer.
|
||||||
let entry = self.entries
|
self.entries
|
||||||
.get_opt_mut(handle)
|
.get_opt_mut(handle)
|
||||||
.expect("BUG: handle must be valid now");
|
.expect("BUG: handle must be valid now")
|
||||||
entry.uv_rect_kind = uv_rect_kind;
|
.update_gpu_cache(gpu_cache);
|
||||||
entry.update_gpu_cache(gpu_cache);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -182,6 +182,8 @@ bitflags! {
|
||||||
|
|
||||||
// Windows flags
|
// Windows flags
|
||||||
const FORCE_GDI = 1 << 16;
|
const FORCE_GDI = 1 << 16;
|
||||||
|
const FORCE_SYMMETRIC = 1 << 17;
|
||||||
|
const NO_SYMMETRIC = 1 << 18;
|
||||||
|
|
||||||
// Mac flags
|
// Mac flags
|
||||||
const FONT_SMOOTHING = 1 << 16;
|
const FONT_SMOOTHING = 1 << 16;
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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 euclid::{point2, size2, rect};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicIsize, Ordering};
|
use std::sync::atomic::{AtomicIsize, Ordering};
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use webrender::api::*;
|
use webrender::api::*;
|
||||||
use webrender::api::units::*;
|
use webrender::api::units::*;
|
||||||
|
use crate::{WindowWrapper, NotifierEvent};
|
||||||
|
use crate::blob;
|
||||||
|
use crate::reftest::{ReftestImage, ReftestImageComparison};
|
||||||
use crate::wrench::Wrench;
|
use crate::wrench::Wrench;
|
||||||
|
|
||||||
pub struct RawtestHarness<'a> {
|
pub struct RawtestHarness<'a> {
|
||||||
|
@ -53,6 +54,41 @@ impl<'a> RawtestHarness<'a> {
|
||||||
self.wrench.renderer.read_pixels_rgba8(window_rect)
|
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(
|
fn submit_dl(
|
||||||
&mut self,
|
&mut self,
|
||||||
epoch: &mut Epoch,
|
epoch: &mut Epoch,
|
||||||
|
@ -574,7 +610,7 @@ impl<'a> RawtestHarness<'a> {
|
||||||
|
|
||||||
let pixels = self.render_and_get_pixels(window_rect);
|
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
|
// Leaving a tiled blob image in the resource cache
|
||||||
// confuses the `test_capture`. TODO: remove this
|
// confuses the `test_capture`. TODO: remove this
|
||||||
|
@ -667,7 +703,6 @@ impl<'a> RawtestHarness<'a> {
|
||||||
// use png;
|
// use png;
|
||||||
// png::save_flipped("out1.png", &pixels_first, window_rect.size);
|
// png::save_flipped("out1.png", &pixels_first, window_rect.size);
|
||||||
// png::save_flipped("out2.png", &pixels_second, window_rect.size);
|
// png::save_flipped("out2.png", &pixels_second, window_rect.size);
|
||||||
|
|
||||||
assert!(pixels_first != pixels_second);
|
assert!(pixels_first != pixels_second);
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
|
@ -905,8 +940,8 @@ impl<'a> RawtestHarness<'a> {
|
||||||
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
|
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
|
||||||
let pixels_third = self.render_and_get_pixels(window_rect);
|
let pixels_third = self.render_and_get_pixels(window_rect);
|
||||||
|
|
||||||
assert!(pixels_first == pixels_second);
|
|
||||||
assert!(pixels_first != pixels_third);
|
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
|
// 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 first = do_test(false);
|
||||||
let second = do_test(true);
|
let second = do_test(true);
|
||||||
|
|
||||||
assert_eq!(first, second);
|
self.compare_pixels(first, second, window_rect.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// regression test for #2769
|
// regression test for #2769
|
||||||
|
@ -1141,7 +1176,7 @@ impl<'a> RawtestHarness<'a> {
|
||||||
|
|
||||||
// 5. render the built frame and compare
|
// 5. render the built frame and compare
|
||||||
let pixels1 = self.render_and_get_pixels(window_rect);
|
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
|
// 6. rebuild the scene and compare again
|
||||||
let mut txn = Transaction::new();
|
let mut txn = Transaction::new();
|
||||||
|
@ -1149,7 +1184,7 @@ impl<'a> RawtestHarness<'a> {
|
||||||
txn.generate_frame();
|
txn.generate_frame();
|
||||||
self.wrench.api.send_transaction(captured.document_id, txn);
|
self.wrench.api.send_transaction(captured.document_id, txn);
|
||||||
let pixels2 = self.render_and_get_pixels(window_rect);
|
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) {
|
fn test_zero_height_window(&mut self) {
|
||||||
|
|
|
@ -114,11 +114,11 @@ impl Display for Reftest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ReftestImage {
|
pub struct ReftestImage {
|
||||||
data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
size: DeviceIntSize,
|
pub size: DeviceIntSize,
|
||||||
}
|
}
|
||||||
enum ReftestImageComparison {
|
pub enum ReftestImageComparison {
|
||||||
Equal,
|
Equal,
|
||||||
NotEqual {
|
NotEqual {
|
||||||
max_difference: usize,
|
max_difference: usize,
|
||||||
|
@ -127,7 +127,7 @@ enum ReftestImageComparison {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReftestImage {
|
impl ReftestImage {
|
||||||
fn compare(&self, other: &ReftestImage) -> ReftestImageComparison {
|
pub fn compare(&self, other: &ReftestImage) -> ReftestImageComparison {
|
||||||
assert_eq!(self.size, other.size);
|
assert_eq!(self.size, other.size);
|
||||||
assert_eq!(self.data.len(), other.data.len());
|
assert_eq!(self.data.len(), other.data.len());
|
||||||
assert_eq!(self.data.len() % 4, 0);
|
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 width = self.size.width;
|
||||||
let height = self.size.height;
|
let height = self.size.height;
|
||||||
|
|
||||||
|
|
|
@ -395,7 +395,7 @@ JS::Result<Ok> BinASTParserPerTokenizer<Tok>::finishLazyFunction(
|
||||||
if (funbox->strict()) {
|
if (funbox->strict()) {
|
||||||
lazy->setStrict();
|
lazy->setStrict();
|
||||||
}
|
}
|
||||||
lazy->setIsBinAST();
|
MOZ_ASSERT(lazy->isBinAST());
|
||||||
fun->initLazyScript(lazy);
|
fun->initLazyScript(lazy);
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
|
|
|
@ -2070,9 +2070,7 @@ class JSScript : public js::gc::TenuredCell {
|
||||||
// Whether the Parser declared 'arguments'.
|
// Whether the Parser declared 'arguments'.
|
||||||
ShouldDeclareArguments = 1 << 25,
|
ShouldDeclareArguments = 1 << 25,
|
||||||
|
|
||||||
// Whether source is BinAST.
|
// (1 << 26) is unused.
|
||||||
// FIXME: JSScript should also set this flag correctly.
|
|
||||||
IsBinAST = 1 << 26,
|
|
||||||
|
|
||||||
// Whether this script contains a direct eval statement.
|
// Whether this script contains a direct eval statement.
|
||||||
HasDirectEval = 1 << 27,
|
HasDirectEval = 1 << 27,
|
||||||
|
@ -3522,8 +3520,7 @@ class LazyScript : public gc::TenuredCell {
|
||||||
return frontend::ParseGoal::Script;
|
return frontend::ParseGoal::Script;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isBinAST() const { return hasFlag(ImmutableFlags::IsBinAST); }
|
bool isBinAST() const { return scriptSource()->hasBinASTSource(); }
|
||||||
void setIsBinAST() { setFlag(ImmutableFlags::IsBinAST); }
|
|
||||||
|
|
||||||
bool strict() const { return hasFlag(ImmutableFlags::Strict); }
|
bool strict() const { return hasFlag(ImmutableFlags::Strict); }
|
||||||
void setStrict() { setFlag(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 NM="$topsrcdir/clang/bin/llvm-nm"
|
||||||
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
export MOZILLA_OFFICIAL=1
|
export MOZILLA_OFFICIAL=1
|
||||||
export MOZ_ANDROID_POCKET=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 NM="$topsrcdir/clang/bin/llvm-nm"
|
||||||
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
export MOZILLA_OFFICIAL=1
|
export MOZILLA_OFFICIAL=1
|
||||||
export MOZ_ANDROID_POCKET=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 NM="$topsrcdir/clang/bin/llvm-nm"
|
||||||
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
|
. "$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 NM="$topsrcdir/clang/bin/llvm-nm"
|
||||||
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
|
. "$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 NM="$topsrcdir/clang/bin/llvm-nm"
|
||||||
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
|
. "$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 NM="$topsrcdir/clang/bin/llvm-nm"
|
||||||
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
|
. "$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 NM="$topsrcdir/clang/bin/llvm-nm"
|
||||||
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
export MOZILLA_OFFICIAL=1
|
export MOZILLA_OFFICIAL=1
|
||||||
export MOZ_ANDROID_POCKET=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 NM="$topsrcdir/clang/bin/llvm-nm"
|
||||||
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
|
||||||
|
|
||||||
export MOZ_LTO=1
|
export MOZ_LTO=cross
|
||||||
|
|
||||||
export MOZILLA_OFFICIAL=1
|
export MOZILLA_OFFICIAL=1
|
||||||
export MOZ_ANDROID_POCKET=1
|
export MOZ_ANDROID_POCKET=1
|
||||||
|
|
|
@ -75,8 +75,6 @@ jobs:
|
||||||
symbol: I(deb7-bb)
|
symbol: I(deb7-bb)
|
||||||
parent: debian7-base
|
parent: debian7-base
|
||||||
definition: debian-build
|
definition: debian-build
|
||||||
packages:
|
|
||||||
- deb7-valgrind
|
|
||||||
args:
|
args:
|
||||||
ARCH: amd64
|
ARCH: amd64
|
||||||
debian7-amd64-build:
|
debian7-amd64-build:
|
||||||
|
|
|
@ -192,8 +192,8 @@ jobs:
|
||||||
run:
|
run:
|
||||||
using: debian-package
|
using: debian-package
|
||||||
dsc:
|
dsc:
|
||||||
url: http://snapshot.debian.org/archive/debian/20181115T045552Z/pool/main/v/valgrind/valgrind_3.14.0-1.dsc
|
url: http://snapshot.debian.org/archive/debian/20190714T155055Z/pool/main/v/valgrind/valgrind_3.15.0-1.dsc
|
||||||
sha256: 6709e2fe4e8251ee32f3cfbf2c6ee106a5cfa3e8dc672cf1dd5f2b26e72b64ee
|
sha256: 2bd467ec486302060ed4e91452231b2c0a09afcd51c25d4ba8d3028218328681
|
||||||
patch: valgrind-wheezy.diff
|
patch: valgrind-wheezy.diff
|
||||||
|
|
||||||
deb7-dh-python:
|
deb7-dh-python:
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
[background-position-x-valid.html]
|
[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]
|
[e.style['background-position-x'\] = "x-end" should set the property value]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,3 @@
|
||||||
[e.style['background-position-y'\] = "y-end" should set the property value]
|
[e.style['background-position-y'\] = "y-end" should set the property value]
|
||||||
expected: FAIL
|
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", "-20%");
|
||||||
test_valid_value("background-position-x", "10px");
|
test_valid_value("background-position-x", "10px");
|
||||||
test_valid_value("background-position-x", "0.5em");
|
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", "left -20%");
|
||||||
test_valid_value("background-position-x", "right 10px");
|
test_valid_value("background-position-x", "right 10px");
|
||||||
test_valid_value("background-position-x", "-20%, 10px");
|
test_valid_value("background-position-x", "-20%, 10px");
|
||||||
test_valid_value("background-position-x", "center, left, right");
|
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", "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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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", "-20%");
|
||||||
test_valid_value("background-position-y", "10px");
|
test_valid_value("background-position-y", "10px");
|
||||||
test_valid_value("background-position-y", "0.5em");
|
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", "top -20%");
|
||||||
test_valid_value("background-position-y", "bottom 10px");
|
test_valid_value("background-position-y", "bottom 10px");
|
||||||
test_valid_value("background-position-y", "-20%, 10px");
|
test_valid_value("background-position-y", "-20%, 10px");
|
||||||
test_valid_value("background-position-y", "center, top, bottom");
|
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", "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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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 double thin", "thin double green");
|
||||||
test_valid_value("border-block-start", "green", ["green", "medium none 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", "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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -13,14 +13,14 @@
|
||||||
<script>
|
<script>
|
||||||
// <length> | thin | medium | thick
|
// <length> | thin | medium | thick
|
||||||
test_valid_value("border-block-start-width", "10px");
|
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", "thick");
|
||||||
test_valid_value("border-block-start-width", "thin");
|
test_valid_value("border-block-start-width", "thin");
|
||||||
test_valid_value("border-block-end-width", "0", "0px");
|
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-end-width", "medium");
|
||||||
test_valid_value("border-block-width", "10px");
|
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");
|
test_valid_value("border-block-width", "10px 10px", "10px");
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</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 double thin", "thin double green");
|
||||||
test_valid_value("border-inline-start", "green", ["green", "medium none 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", "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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -13,14 +13,14 @@
|
||||||
<script>
|
<script>
|
||||||
// <length> | thin | medium | thick
|
// <length> | thin | medium | thick
|
||||||
test_valid_value("border-inline-start-width", "10px");
|
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", "thick");
|
||||||
test_valid_value("border-inline-start-width", "thin");
|
test_valid_value("border-inline-start-width", "thin");
|
||||||
test_valid_value("border-inline-end-width", "0", "0px");
|
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-end-width", "medium");
|
||||||
test_valid_value("border-inline-width", "10px");
|
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");
|
test_valid_value("border-inline-width", "10px 10px", "10px");
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -15,16 +15,16 @@
|
||||||
test_valid_value("inset-block-start", "auto");
|
test_valid_value("inset-block-start", "auto");
|
||||||
test_valid_value("inset-block-end", "-10px");
|
test_valid_value("inset-block-end", "-10px");
|
||||||
test_valid_value("inset-inline-start", "-20%");
|
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", "auto");
|
||||||
test_valid_value("inset-block", "-10px");
|
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-block", "auto auto", "auto");
|
||||||
test_valid_value("inset-inline", "-20%");
|
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", "-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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<script>
|
<script>
|
||||||
test_valid_value("border-spacing", "0px");
|
test_valid_value("border-spacing", "0px");
|
||||||
test_valid_value("border-spacing", "10px 20px");
|
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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Загрузка…
Ссылка в новой задаче