Merge mozilla-central to mozilla-inbound. CLOSED TREE

This commit is contained in:
Csoregi Natalia 2019-07-19 12:50:23 +03:00
Родитель 0dc4fc1dc2 96403eac4f
Коммит b11f89739a
99 изменённых файлов: 1493 добавлений и 807 удалений

Просмотреть файл

@ -56,6 +56,7 @@ for (const type of [
"DISCOVERY_STREAM_SPOCS_ENDPOINT",
"DISCOVERY_STREAM_SPOCS_FILL",
"DISCOVERY_STREAM_SPOCS_UPDATE",
"DISCOVERY_STREAM_SPOC_BLOCKED",
"DISCOVERY_STREAM_SPOC_IMPRESSION",
"DOWNLOAD_CHANGED",
"FAKE_FOCUS_SEARCH",

Просмотреть файл

@ -66,6 +66,7 @@ const INITIAL_STATE = {
data: {}, // {spocs: []}
loaded: false,
frequency_caps: [],
blocked: [],
},
},
Search: {
@ -612,6 +613,14 @@ function DiscoveryStream(prevState = INITIAL_STATE.DiscoveryStream, action) {
};
}
return prevState;
case at.DISCOVERY_STREAM_SPOC_BLOCKED:
return {
...prevState,
spocs: {
...prevState.spocs,
blocked: [...prevState.spocs.blocked, action.data.url],
},
};
case at.DISCOVERY_STREAM_LINK_BLOCKED:
return isNotReady()
? prevState

Просмотреть файл

@ -19,11 +19,26 @@ export class ModalOverlayWrapper extends React.PureComponent {
componentWillMount() {
this.props.document.addEventListener("keydown", this.onKeyDown);
this.props.document.body.classList.add("modal-open");
this.header = this.props.document.getElementById(
"header-asrouter-container"
);
if (this.header) {
this.header.classList.add("modal-scroll");
this.props.document.getElementById("root").classList.add("modal-height");
}
}
componentWillUnmount() {
this.props.document.removeEventListener("keydown", this.onKeyDown);
this.props.document.body.classList.remove("modal-open");
if (this.header) {
this.header.classList.remove("modal-scroll");
this.props.document
.getElementById("root")
.classList.remove("modal-height");
}
}
render() {

Просмотреть файл

@ -19,6 +19,19 @@
}
}
.modal-scroll {
position: absolute;
width: 100%;
height: 100%;
overflow: auto;
}
.modal-height {
// "Welcome header" has 40px of padding and 36px font size that get neglected using position absolute
// causing this to visually collide with the newtab searchbar
padding-top: 80px;
}
.modalOverlayInner {
width: 960px;
position: fixed;
@ -30,7 +43,6 @@
display: none;
z-index: 1101;
// modal takes over entire screen
@media(max-width: 960px) {
width: 100%;

Просмотреть файл

@ -401,7 +401,7 @@
.inline-onboarding {
&.activity-stream.welcome {
overflow-y: scroll;
overflow-y: hidden;
}
.modalOverlayInner {

Просмотреть файл

@ -5,6 +5,7 @@
import { actionCreators as ac, actionTypes as at } from "common/Actions.jsm";
import { cardContextTypes } from "./types";
import { connect } from "react-redux";
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
import React from "react";
import { ScreenshotUtils } from "content-src/lib/screenshot-utils";
@ -27,11 +28,9 @@ export class _Card extends React.PureComponent {
this.state = {
activeCard: null,
imageLoaded: false,
showContextMenu: false,
cardImage: null,
};
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
this.onMenuButtonUpdate = this.onMenuButtonUpdate.bind(this);
this.onLinkClick = this.onLinkClick.bind(this);
}
@ -117,12 +116,12 @@ export class _Card extends React.PureComponent {
return nextState;
}
onMenuButtonClick(event) {
event.preventDefault();
this.setState({
activeCard: this.props.index,
showContextMenu: true,
});
onMenuButtonUpdate(isOpen) {
if (isOpen) {
this.setState({ activeCard: this.props.index });
} else {
this.setState({ activeCard: null });
}
}
/**
@ -191,10 +190,6 @@ export class _Card extends React.PureComponent {
}
}
onMenuUpdate(showContextMenu) {
this.setState({ showContextMenu });
}
componentDidMount() {
this.maybeLoadImage();
}
@ -239,8 +234,7 @@ export class _Card extends React.PureComponent {
} = this.props;
const { props } = this;
const title = link.title || link.hostname;
const isContextMenuOpen =
this.state.showContextMenu && this.state.activeCard === index;
const isContextMenuOpen = this.state.activeCard === index;
// Display "now" as "trending" until we have new strings #3402
const { icon, fluentID } =
cardContextTypes[link.type === "now" ? "trending" : link.type] || {};
@ -329,25 +323,21 @@ export class _Card extends React.PureComponent {
</div>
</a>
{!props.placeholder && (
<button
aria-haspopup="true"
data-l10n-id="newtab-menu-content-tooltip"
data-l10n-args={JSON.stringify({ title })}
className="context-menu-button icon"
onClick={this.onMenuButtonClick}
/>
)}
{isContextMenuOpen && (
<ContextMenuButton
tooltip="newtab-menu-content-tooltip"
tooltipArgs={{ title }}
onUpdate={this.onMenuButtonUpdate}
>
<LinkMenu
dispatch={dispatch}
index={index}
source={eventSource}
onUpdate={this.onMenuUpdate}
options={link.contextMenuOptions || contextMenuOptions}
site={link}
siteInfo={this._getTelemetryInfo()}
shouldSendImpressionStats={shouldSendImpressionStats}
/>
</ContextMenuButton>
)}
</li>
);

Просмотреть файл

@ -8,6 +8,7 @@ import { FluentOrText } from "content-src/components/FluentOrText/FluentOrText";
import React from "react";
import { SectionMenu } from "content-src/components/SectionMenu/SectionMenu";
import { SectionMenuOptions } from "content-src/lib/section-menu-options";
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
const VISIBLE = "visible";
const VISIBILITY_CHANGE_EVENT = "visibilitychange";
@ -20,7 +21,6 @@ export class CollapsibleSection extends React.PureComponent {
this.onKeyPress = this.onKeyPress.bind(this);
this.onTransitionEnd = this.onTransitionEnd.bind(this);
this.enableOrDisableAnimation = this.enableOrDisableAnimation.bind(this);
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuButtonMouseEnter = this.onMenuButtonMouseEnter.bind(this);
this.onMenuButtonMouseLeave = this.onMenuButtonMouseLeave.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
@ -158,11 +158,6 @@ export class CollapsibleSection extends React.PureComponent {
);
}
onMenuButtonClick(event) {
event.preventDefault();
this.setState({ showContextMenu: true });
}
onMenuButtonMouseEnter() {
this.setState({ menuButtonHover: true });
}
@ -258,14 +253,11 @@ export class CollapsibleSection extends React.PureComponent {
</span>
</h3>
<div>
<button
aria-haspopup="true"
className="context-menu-button icon"
data-l10n-id="newtab-menu-section-tooltip"
onClick={this.onMenuButtonClick}
ref={this.setContextMenuButtonRef}
/>
{showContextMenu && (
<ContextMenuButton
tooltip="newtab-menu-section-tooltip"
onUpdate={this.onMenuUpdate}
refFunction={this.setContextMenuButtonRef}
>
<SectionMenu
id={id}
extraOptions={extraMenuOptions}
@ -273,14 +265,13 @@ export class CollapsibleSection extends React.PureComponent {
showPrefName={showPrefName}
privacyNoticeURL={privacyNoticeURL}
collapsed={collapsed}
onUpdate={this.onMenuUpdate}
isFixed={isFixed}
isFirst={isFirst}
isLast={isLast}
dispatch={dispatch}
isWebExtension={isWebExtension}
/>
)}
</ContextMenuButton>
</div>
</div>
<ErrorBoundary className="section-body-fallback">

Просмотреть файл

@ -60,6 +60,7 @@ export class ContextMenu extends React.PureComponent {
key={i}
option={option}
hideContext={this.hideContext}
keyboardAccess={this.props.keyboardAccess}
tabIndex="0"
/>
)
@ -76,6 +77,7 @@ export class ContextMenuItem extends React.PureComponent {
super(props);
this.onClick = this.onClick.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.focusFirst = this.focusFirst.bind(this);
}
onClick() {
@ -83,6 +85,13 @@ export class ContextMenuItem extends React.PureComponent {
this.props.option.onClick();
}
// Focus the first menu item if the menu was accessed via the keyboard.
focusFirst(button) {
if (this.props.keyboardAccess && button) {
button.focus();
}
}
// This selects the correct node based on the key pressed
focusSibling(target, key) {
const parent = target.parentNode;
@ -138,6 +147,7 @@ export class ContextMenuItem extends React.PureComponent {
tabIndex="0"
onClick={this.onClick}
onKeyDown={this.onKeyDown}
ref={option.first ? this.focusFirst : null}
>
{option.icon && (
<span className={`icon icon-spacer icon-${option.icon}`} />

Просмотреть файл

@ -0,0 +1,72 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from "react";
export class ContextMenuButton extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
showContextMenu: false,
contextMenuKeyboard: false,
};
this.onClick = this.onClick.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.onUpdate = this.onUpdate.bind(this);
}
openContextMenu(isKeyBoard, event) {
if (this.props.onUpdate) {
this.props.onUpdate(true);
}
this.setState({
showContextMenu: true,
contextMenuKeyboard: isKeyBoard,
});
}
onClick(event) {
event.preventDefault();
this.openContextMenu(false, event);
}
onKeyDown(event) {
if (event.key === "Enter") {
event.preventDefault();
this.openContextMenu(true, event);
}
}
onUpdate(showContextMenu) {
if (this.props.onUpdate) {
this.props.onUpdate(showContextMenu);
}
this.setState({ showContextMenu });
}
render() {
const { tooltipArgs, tooltip, children, refFunction } = this.props;
const { showContextMenu, contextMenuKeyboard } = this.state;
return (
<React.Fragment>
<button
aria-haspopup="true"
data-l10n-id={tooltip}
data-l10n-args={tooltipArgs ? JSON.stringify(tooltipArgs) : null}
className="context-menu-button icon"
onKeyDown={this.onKeyDown}
onClick={this.onClick}
ref={refFunction}
/>
{showContextMenu
? React.cloneElement(children, {
keyboardAccess: contextMenuKeyboard,
onUpdate: this.onUpdate,
})
: null}
</React.Fragment>
);
}
}

Просмотреть файл

@ -3,36 +3,23 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
import React from "react";
export class DSLinkMenu extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
activeCard: null,
showContextMenu: false,
};
this.windowObj = this.props.windowObj || window; // Added to support unit tests
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
this.onMenuShow = this.onMenuShow.bind(this);
this.contextMenuButtonRef = React.createRef();
}
onMenuButtonClick(event) {
event.preventDefault();
this.setState({
activeCard: this.props.index,
showContextMenu: true,
});
}
onMenuUpdate(showContextMenu) {
if (!showContextMenu) {
const dsLinkMenuHostDiv = this.contextMenuButtonRef.current.parentElement;
dsLinkMenuHostDiv.parentElement.classList.remove("active", "last-item");
}
this.setState({ showContextMenu });
}
nextAnimationFrame() {
@ -51,8 +38,6 @@ export class DSLinkMenu extends React.PureComponent {
render() {
const { index, dispatch } = this.props;
const isContextMenuOpen =
this.state.showContextMenu && this.state.activeCard === index;
const TOP_STORIES_CONTEXT_MENU_OPTIONS = [
"CheckBookmarkOrArchive",
"CheckSavedToPocket",
@ -67,20 +52,16 @@ export class DSLinkMenu extends React.PureComponent {
return (
<div>
<button
ref={this.contextMenuButtonRef}
aria-haspopup="true"
className="context-menu-button icon"
data-l10n-id="newtab-menu-content-tooltip"
data-l10n-args={JSON.stringify({ title })}
onClick={this.onMenuButtonClick}
/>
{isContextMenuOpen && (
<ContextMenuButton
refFunction={this.contextMenuButtonRef}
tooltip={"newtab-menu-content-tooltip"}
tooltipArgs={{ title }}
onUpdate={this.onMenuUpdate}
>
<LinkMenu
dispatch={dispatch}
index={index}
source={type.toUpperCase()}
onUpdate={this.onMenuUpdate}
onShow={this.onMenuShow}
options={TOP_STORIES_CONTEXT_MENU_OPTIONS}
shouldSendImpressionStats={true}
@ -95,7 +76,7 @@ export class DSLinkMenu extends React.PureComponent {
bookmarkGuid: this.props.bookmarkGuid,
}}
/>
)}
</ContextMenuButton>
</div>
);
}

Просмотреть файл

@ -85,6 +85,7 @@ export class _LinkMenu extends React.PureComponent {
onUpdate={this.props.onUpdate}
onShow={this.props.onShow}
options={this.getOptions()}
keyboardAccess={this.props.keyboardAccess}
/>
);
}

Просмотреть файл

@ -26,6 +26,19 @@ const WEBEXT_SECTION_MENU_OPTIONS = [
];
export class _SectionMenu extends React.PureComponent {
handleAddWhileCollapsed() {
const { action, userEvent } = SectionMenuOptions.ExpandSection(this.props);
this.props.dispatch(action);
if (userEvent) {
this.props.dispatch(
ac.UserEvent({
event: userEvent,
source: this.props.source,
})
);
}
}
getOptions() {
const { props } = this;
@ -51,6 +64,14 @@ export class _SectionMenu extends React.PureComponent {
const { action, id, type, userEvent } = option;
if (!type && id) {
option.onClick = () => {
const hasAddEvent =
userEvent === "MENU_ADD_TOPSITE" ||
userEvent === "MENU_ADD_SEARCH";
if (props.collapsed && hasAddEvent) {
this.handleAddWhileCollapsed();
}
props.dispatch(action);
if (userEvent) {
props.dispatch(
@ -75,7 +96,11 @@ export class _SectionMenu extends React.PureComponent {
render() {
return (
<ContextMenu onUpdate={this.props.onUpdate} options={this.getOptions()} />
<ContextMenu
onUpdate={this.props.onUpdate}
options={this.getOptions()}
keyboardAccess={this.props.keyboardAccess}
/>
);
}
}

Просмотреть файл

@ -14,6 +14,7 @@ import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
import React from "react";
import { ScreenshotUtils } from "content-src/lib/screenshot-utils";
import { TOP_SITES_MAX_SITES_PER_ROW } from "common/Reducers.jsm";
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
export class TopSiteLink extends React.PureComponent {
constructor(props) {
@ -273,7 +274,6 @@ export class TopSite extends React.PureComponent {
super(props);
this.state = { showContextMenu: false };
this.onLinkClick = this.onLinkClick.bind(this);
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
this.onMenuUpdate = this.onMenuUpdate.bind(this);
}
@ -335,21 +335,18 @@ export class TopSite extends React.PureComponent {
}
}
onMenuButtonClick(event) {
event.preventDefault();
onMenuUpdate(isOpen) {
if (isOpen) {
this.props.onActivate(this.props.index);
this.setState({ showContextMenu: true });
} else {
this.props.onActivate();
}
onMenuUpdate(showContextMenu) {
this.setState({ showContextMenu });
}
render() {
const { props } = this;
const { link } = props;
const isContextMenuOpen =
this.state.showContextMenu && props.activeIndex === props.index;
const isContextMenuOpen = props.activeIndex === props.index;
const title = link.label || link.hostname;
return (
<TopSiteLink
@ -362,14 +359,11 @@ export class TopSite extends React.PureComponent {
title={title}
>
<div>
<button
aria-haspopup="true"
className="context-menu-button icon"
data-l10n-id="newtab-menu-content-tooltip"
data-l10n-args={JSON.stringify({ title })}
onClick={this.onMenuButtonClick}
/>
{isContextMenuOpen && (
<ContextMenuButton
tooltip="newtab-menu-content-tooltip"
tooltipArgs={{ title }}
onUpdate={this.onMenuUpdate}
>
<LinkMenu
dispatch={props.dispatch}
index={props.index}
@ -383,7 +377,7 @@ export class TopSite extends React.PureComponent {
siteInfo={this._getTelemetryInfo()}
source={TOP_SITES_SOURCE}
/>
)}
</ContextMenuButton>
</div>
</TopSiteLink>
);

Просмотреть файл

@ -30,8 +30,10 @@ export const selectLayoutRender = (state, prefs, rickRollCache) => {
if (rickRoll <= spocsConfig.probability) {
spocIndex++;
if (!spocs.blocked.includes(spoc.url)) {
recommendations.splice(position.index, 0, spoc);
chosenSpocs.add(spoc);
}
} else {
unchosenSpocs.add(spoc);
}

Просмотреть файл

@ -3003,6 +3003,15 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
.modalOverlayOuter.active {
display: block; }
.modal-scroll {
position: absolute;
width: 100%;
height: 100%;
overflow: auto; }
.modal-height {
padding-top: 80px; }
.modalOverlayInner {
width: 960px;
position: fixed;
@ -4108,7 +4117,7 @@ a.firstrun-link {
text-align: center; }
.inline-onboarding.activity-stream.welcome {
overflow-y: scroll; }
overflow-y: hidden; }
.inline-onboarding .modalOverlayInner {
position: absolute; }

Просмотреть файл

@ -3006,6 +3006,15 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
.modalOverlayOuter.active {
display: block; }
.modal-scroll {
position: absolute;
width: 100%;
height: 100%;
overflow: auto; }
.modal-height {
padding-top: 80px; }
.modalOverlayInner {
width: 960px;
position: fixed;
@ -4111,7 +4120,7 @@ a.firstrun-link {
text-align: center; }
.inline-onboarding.activity-stream.welcome {
overflow-y: scroll; }
overflow-y: hidden; }
.inline-onboarding .modalOverlayInner {
position: absolute; }

Просмотреть файл

@ -3003,6 +3003,15 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
.modalOverlayOuter.active {
display: block; }
.modal-scroll {
position: absolute;
width: 100%;
height: 100%;
overflow: auto; }
.modal-height {
padding-top: 80px; }
.modalOverlayInner {
width: 960px;
position: fixed;
@ -4108,7 +4117,7 @@ a.firstrun-link {
text-align: center; }
.inline-onboarding.activity-stream.welcome {
overflow-y: scroll; }
overflow-y: hidden; }
.inline-onboarding .modalOverlayInner {
position: absolute; }

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1068,3 +1068,21 @@ This reports an enrollment ping when a user gets enrolled in a Trailhead experim
}
}
```
## Feature Callouts interaction pings
This reports when a user has seen or clicked a badge/notification in the browser toolbar in a non-PBM window
```
{
"locale": "en-US",
"client_id": "9da773d8-4356-f54f-b7cf-6134726bcf3d",
"version": "70.0a1",
"release_channel": "default",
"addon_version": "20190712095934",
"action": "cfr_user_event",
"source": "CFR",
"message_id": "FXA_ACCOUNTS_BADGE",
"event": ["CLICK" | "IMPRESSION"],
}
```

Просмотреть файл

@ -728,8 +728,9 @@ class _ASRouter {
handleMessageRequest: this.handleMessageRequest,
addImpression: this.addImpression,
blockMessageById: this.blockMessageById,
dispatch: this.dispatch,
});
ToolbarPanelHub.init({
ToolbarPanelHub.init(this.waitForInitialized, {
getMessages: this.handleMessageRequest,
});
@ -1889,6 +1890,7 @@ class _ASRouter {
await this.addImpression(action.data);
break;
case "DOORHANGER_TELEMETRY":
case "TOOLBAR_BADGE_TELEMETRY":
if (this.dispatchToAS) {
this.dispatchToAS(ac.ASRouterUserEvent(action.data));
}

Просмотреть файл

@ -1182,6 +1182,21 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
const filtered = spocsList.filter(s => s.url === action.data.url);
if (filtered.length) {
this._sendSpocsFill({ blocked_by_user: filtered }, false);
// If we're blocking a spoc, we want a slightly different treatment for open tabs.
this.store.dispatch(
ac.AlsoToPreloaded({
type: at.DISCOVERY_STREAM_LINK_BLOCKED,
data: action.data,
})
);
this.store.dispatch(
ac.BroadcastToContent({
type: at.DISCOVERY_STREAM_SPOC_BLOCKED,
data: action.data,
})
);
return;
}
}
this.store.dispatch(

Просмотреть файл

@ -13,6 +13,11 @@ ChromeUtils.defineModuleGetter(
"ToolbarPanelHub",
"resource://activity-stream/lib/ToolbarPanelHub.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Services",
"resource://gre/modules/Services.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"setTimeout",
@ -23,6 +28,16 @@ ChromeUtils.defineModuleGetter(
"clearTimeout",
"resource://gre/modules/Timer.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Services",
"resource://gre/modules/Services.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm"
);
const notificationsByWindow = new WeakMap();
@ -31,33 +46,51 @@ class _ToolbarBadgeHub {
this.id = "toolbar-badge-hub";
this.template = "toolbar_badge";
this.state = null;
this.prefs = {
WHATSNEW_TOOLBAR_PANEL: "browser.messaging-system.whatsNewPanel.enabled",
};
this.removeAllNotifications = this.removeAllNotifications.bind(this);
this.removeToolbarNotification = this.removeToolbarNotification.bind(this);
this.addToolbarNotification = this.addToolbarNotification.bind(this);
this.registerBadgeToAllWindows = this.registerBadgeToAllWindows.bind(this);
this._sendTelemetry = this._sendTelemetry.bind(this);
this.sendUserEventTelemetry = this.sendUserEventTelemetry.bind(this);
this._handleMessageRequest = null;
this._addImpression = null;
this._blockMessageById = null;
this._dispatch = null;
}
async init(
waitForInitialized,
{ handleMessageRequest, addImpression, blockMessageById }
{ handleMessageRequest, addImpression, blockMessageById, dispatch }
) {
this._handleMessageRequest = handleMessageRequest;
this._blockMessageById = blockMessageById;
this._addImpression = addImpression;
this._dispatch = dispatch;
this.state = {};
// Need to wait for ASRouter to initialize before trying to fetch messages
await waitForInitialized;
this.messageRequest("toolbarBadgeUpdate");
// Listen for pref changes that could trigger new badges
Services.prefs.addObserver(this.prefs.WHATSNEW_TOOLBAR_PANEL, this);
}
observe(aSubject, aTopic, aPrefName) {
switch (aPrefName) {
case this.prefs.WHATSNEW_TOOLBAR_PANEL:
this.messageRequest("toolbarBadgeUpdate");
break;
}
}
executeAction({ id }) {
switch (id) {
case "show-whatsnew-button":
ToolbarPanelHub.enableToolbarButton();
ToolbarPanelHub.enableAppmenuButton();
break;
}
}
@ -91,6 +124,11 @@ class _ToolbarBadgeHub {
this.removeAllNotifications
);
event.target.removeEventListener("click", this.removeAllNotifications);
// If we have an event it means the user interacted with the badge
// we should send telemetry
if (this.state.notification) {
this.sendUserEventTelemetry("CLICK", this.state.notification);
}
}
// Will call uninit on every window
EveryWindow.unregisterCallback(this.id);
@ -146,6 +184,8 @@ class _ToolbarBadgeHub {
registerBadgeToAllWindows(message) {
// Impression should be added when the badge becomes visible
this._addImpression(message);
// Send a telemetry ping when adding the notification badge
this.sendUserEventTelemetry("IMPRESSION", message);
EveryWindow.registerCallback(
this.id,
@ -191,9 +231,34 @@ class _ToolbarBadgeHub {
}
}
_sendTelemetry(ping) {
this._dispatch({
type: "TOOLBAR_BADGE_TELEMETRY",
data: { action: "cfr_user_event", source: "CFR", ...ping },
});
}
sendUserEventTelemetry(event, message) {
const win = Services.wm.getMostRecentWindow("navigator:browser");
// Only send pings for non private browsing windows
if (
win &&
!PrivateBrowsingUtils.isBrowserPrivate(
win.ownerGlobal.gBrowser.selectedBrowser
)
) {
this._sendTelemetry({
message_id: message.id,
bucket_id: message.id,
event,
});
}
}
uninit() {
this._clearBadgeTimeout();
this.state = null;
Services.prefs.removeObserver(this.prefs.WHATSNEW_TOOLBAR_PANEL, this);
}
}

Просмотреть файл

@ -30,16 +30,42 @@ class _ToolbarPanelHub {
this._hideToolbarButton = this._hideToolbarButton.bind(this);
}
init({ getMessages }) {
async init(waitForInitialized, { getMessages }) {
this._getMessages = getMessages;
// Wait for ASRouter messages to become available in order to know
// if we can show the What's New panel
await waitForInitialized;
if (this.whatsNewPanelEnabled) {
// Enable the application menu button so that the user can access
// the panel outside of the toolbar button
this.enableAppmenuButton();
}
// Listen for pref changes that could turn off the feature
Services.prefs.addObserver(WHATSNEW_ENABLED_PREF, this);
}
uninit() {
EveryWindow.unregisterCallback(TOOLBAR_BUTTON_ID);
EveryWindow.unregisterCallback(APPMENU_BUTTON_ID);
Services.prefs.removeObserver(WHATSNEW_ENABLED_PREF, this);
}
observe(aSubject, aTopic, aPrefName) {
switch (aPrefName) {
case WHATSNEW_ENABLED_PREF:
if (!this.whatsNewPanelEnabled) {
this.uninit();
}
break;
}
}
get messages() {
return this._getMessages({
template: "whatsnew_panel_message",
triggerId: "whatsNewPanelOpened",
returnAll: true,
});
}
get whatsNewPanelEnabled() {
@ -51,22 +77,26 @@ class _ToolbarPanelHub {
}
// Turns on the Appmenu (hamburger menu) button for all open windows and future windows.
enableAppmenuButton() {
async enableAppmenuButton() {
if ((await this.messages).length) {
EveryWindow.registerCallback(
APPMENU_BUTTON_ID,
this._showAppmenuButton,
this._hideAppmenuButton
);
}
}
// Turns on the Toolbar button for all open windows and future windows.
enableToolbarButton() {
async enableToolbarButton() {
if ((await this.messages).length) {
EveryWindow.registerCallback(
TOOLBAR_BUTTON_ID,
this._showToolbarButton,
this._hideToolbarButton
);
}
}
// When the panel is hidden we want to run some cleanup
_onPanelHidden(win) {
@ -88,11 +118,7 @@ class _ToolbarPanelHub {
// Render what's new messages into the panel.
async renderMessages(win, doc, containerId) {
const messages = (await this._getMessages({
template: "whatsnew_panel_message",
triggerId: "whatsNewPanelOpened",
returnAll: true,
})).sort((m1, m2) => {
const messages = (await this.messages).sort((m1, m2) => {
// Sort by published_date in descending order.
if (m1.content.published_date === m2.content.published_date) {
return 0;

Просмотреть файл

@ -26,5 +26,4 @@ skip-if = (os == "linux") # Test setup only implemented for OSX and Windows
[browser_topsites_contextMenu_options.js]
[browser_topsites_section.js]
[browser_asrouter_cfr.js]
skip-if = fission
[browser_asrouter_bookmarkpanel.js]

Просмотреть файл

@ -206,12 +206,17 @@ describe("ASRouter", () => {
handleMessageRequest: Router.handleMessageRequest,
addImpression: Router.addImpression,
blockMessageById: Router.blockMessageById,
dispatch: Router.dispatch,
}
);
assert.calledWithExactly(FakeToolbarPanelHub.init, {
assert.calledWithExactly(
FakeToolbarPanelHub.init,
Router.waitForInitialized,
{
getMessages: Router.handleMessageRequest,
});
}
);
assert.calledWithExactly(
FakeBookmarkPanelHub.init,

Просмотреть файл

@ -5,12 +5,18 @@ import React from "react";
describe("ModalOverlayWrapper", () => {
let fakeDoc;
let sandbox;
let header;
beforeEach(() => {
sandbox = sinon.createSandbox();
header = document.createElement("div");
fakeDoc = {
addEventListener: sandbox.stub(),
removeEventListener: sandbox.stub(),
body: { classList: { add: sandbox.stub(), remove: sandbox.stub() } },
getElementById() {
return header;
},
};
});
afterEach(() => {

Просмотреть файл

@ -970,6 +970,7 @@ describe("Reducers", () => {
lastUpdated: 123,
loaded: true,
frequency_caps: [],
blocked: [],
});
});
it("should handle no data from DISCOVERY_STREAM_SPOCS_UPDATE", () => {
@ -980,6 +981,21 @@ describe("Reducers", () => {
});
assert.deepEqual(state.spocs, INITIAL_STATE.DiscoveryStream.spocs);
});
it("should add blocked spocs to blocked array with DISCOVERY_STREAM_SPOC_BLOCKED", () => {
const firstState = DiscoveryStream(undefined, {
type: at.DISCOVERY_STREAM_SPOC_BLOCKED,
data: { url: "https://foo.com" },
});
const secondState = DiscoveryStream(firstState, {
type: at.DISCOVERY_STREAM_SPOC_BLOCKED,
data: { url: "https://bar.com" },
});
assert.deepEqual(firstState.spocs.blocked, ["https://foo.com"]);
assert.deepEqual(secondState.spocs.blocked, [
"https://foo.com",
"https://bar.com",
]);
});
it("should not update state for empty action.data on DISCOVERY_STREAM_LINK_BLOCKED", () => {
const newState = DiscoveryStream(undefined, {
type: at.DISCOVERY_STREAM_LINK_BLOCKED,

Просмотреть файл

@ -7,6 +7,7 @@ import { combineReducers, createStore } from "redux";
import { GlobalOverrider } from "test/unit/utils";
import { INITIAL_STATE, reducers } from "common/Reducers.jsm";
import { cardContextTypes } from "content-src/components/Card/types";
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
import { Provider } from "react-redux";
import React from "react";
@ -149,18 +150,22 @@ describe("<Card>", () => {
const title = '"fluent"';
const link = { ...DEFAULT_PROPS.link, title };
wrapper = shallow(<Card {...DEFAULT_PROPS} link={link} />);
let button = wrapper.find(
"button[data-l10n-id='newtab-menu-content-tooltip']"
);
wrapper = mountCardWithProps({ ...DEFAULT_PROPS, link });
let button = wrapper.find(ContextMenuButton).find("button");
assert.equal(button.prop("data-l10n-args"), JSON.stringify({ title }));
});
it("should have .active class, on card-outer if context menu is open", () => {
const button = wrapper.find(".context-menu-button");
assert.isFalse(wrapper.find(".card-outer").hasClass("active"));
const button = wrapper.find(ContextMenuButton);
assert.isFalse(
wrapper.find(".card-outer").hasClass("active"),
"does not have active class"
);
button.simulate("click", { preventDefault: () => {} });
assert.isTrue(wrapper.find(".card-outer").hasClass("active"));
assert.isTrue(
wrapper.find(".card-outer").hasClass("active"),
"has active class"
);
});
it("should send SHOW_DOWNLOAD_FILE if we clicked on a download", () => {
const downloadLink = {
@ -382,7 +387,7 @@ describe("<Card>", () => {
});
it("should not have a context menu button or LinkMenu", () => {
assert.isFalse(
wrapper.find(".context-menu-button").exists(),
wrapper.find(ContextMenuButton).exists(),
"context menu button"
);
assert.isFalse(wrapper.find(LinkMenu).exists(), "LinkMenu");

Просмотреть файл

@ -2,6 +2,7 @@ import {
ContextMenu,
ContextMenuItem,
} from "content-src/components/ContextMenu/ContextMenu";
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
import { mount, shallow } from "enzyme";
import React from "react";
@ -11,6 +12,88 @@ const DEFAULT_PROPS = {
tabbableOptionsLength: 0,
};
const DEFAULT_MENU_OPTIONS = [
"MoveUp",
"MoveDown",
"Separator",
"RemoveSection",
"CheckCollapsed",
"Separator",
"ManageSection",
];
const FakeMenu = props => {
return <div>{props.children}</div>;
};
describe("<ContextMenuButton>", () => {
let sandbox;
beforeEach(() => {
sandbox = sinon.createSandbox();
});
afterEach(() => {
sandbox.restore();
});
it("should call onUpdate when clicked", () => {
const onUpdate = sandbox.spy();
const wrapper = mount(
<ContextMenuButton onUpdate={onUpdate}>
<FakeMenu />
</ContextMenuButton>
);
wrapper.find(".context-menu-button").simulate("click");
assert.calledOnce(onUpdate);
});
it("should call onUpdate when activated with Enter", () => {
const onUpdate = sandbox.spy();
const wrapper = mount(
<ContextMenuButton onUpdate={onUpdate}>
<FakeMenu />
</ContextMenuButton>
);
wrapper.find(".context-menu-button").simulate("keydown", { key: "Enter" });
assert.calledOnce(onUpdate);
});
it("should call onClick", () => {
const onClick = sandbox.spy(ContextMenuButton.prototype, "onClick");
const wrapper = mount(
<ContextMenuButton>
<FakeMenu />
</ContextMenuButton>
);
wrapper.find("button").simulate("click");
assert.calledOnce(onClick);
});
it("should have a default keyboardAccess prop of false", () => {
const wrapper = mount(
<ContextMenuButton>
<ContextMenu options={DEFAULT_MENU_OPTIONS} />
</ContextMenuButton>
);
wrapper.setState({ showContextMenu: true });
assert.equal(wrapper.find(ContextMenu).prop("keyboardAccess"), false);
});
it("should pass the keyboardAccess prop down to ContextMenu", () => {
const wrapper = mount(
<ContextMenuButton>
<ContextMenu options={DEFAULT_MENU_OPTIONS} />
</ContextMenuButton>
);
wrapper.setState({ showContextMenu: true, contextMenuKeyboard: true });
assert.equal(wrapper.find(ContextMenu).prop("keyboardAccess"), true);
});
it("should call focusFirst when keyboardAccess is true", () => {
const wrapper = mount(
<ContextMenuButton>
<ContextMenu options={[{ label: "item1", first: true }]} />
</ContextMenuButton>
);
const focusFirst = sandbox.spy(ContextMenuItem.prototype, "focusFirst");
wrapper.setState({ showContextMenu: true, contextMenuKeyboard: true });
assert.calledOnce(focusFirst);
});
});
describe("<ContextMenu>", () => {
it("should render all the options provided", () => {
const options = [
@ -79,7 +162,7 @@ describe("<ContextMenu>", () => {
const wrapper = mount(<ContextMenu {...DEFAULT_PROPS} options={options} />);
assert.lengthOf(wrapper.find(".context-menu-item"), 1);
});
it("should call onClick when onKeyDown is called", () => {
it("should call onClick when onKeyDown is called with Enter", () => {
const onClick = sinon.spy();
const wrapper = mount(
<ContextMenu {...DEFAULT_PROPS} options={[{ label: "item1", onClick }]} />

Просмотреть файл

@ -1,5 +1,6 @@
import { mount, shallow } from "enzyme";
import { DSLinkMenu } from "content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu";
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
import React from "react";
@ -18,7 +19,6 @@ describe("<DSLinkMenu>", () => {
});
it("Should remove active on Menu Update", () => {
wrapper.setState({ showContextMenu: true });
// Add active class name to DSLinkMenu parent node
// to simulate menu open state
parentNode.classList.add("active");
@ -28,7 +28,6 @@ describe("<DSLinkMenu>", () => {
wrapper.update();
assert.isEmpty(parentNode.className);
assert.isFalse(wrapper.state(["showContextMenu"]));
});
it("Should add active on Menu Show", async () => {
@ -76,7 +75,7 @@ describe("<DSLinkMenu>", () => {
it("should parse args for fluent correctly ", () => {
const title = '"fluent"';
wrapper = shallow(<DSLinkMenu title={title} />);
wrapper = mount(<DSLinkMenu title={title} />);
const button = wrapper.find(
"button[data-l10n-id='newtab-menu-content-tooltip']"
@ -96,23 +95,25 @@ describe("<DSLinkMenu>", () => {
it("should render a context menu button", () => {
assert.ok(wrapper.exists());
assert.ok(wrapper.find(".context-menu-button").exists());
assert.ok(
wrapper.find(ContextMenuButton).exists(),
"context menu button exists"
);
});
it("should render LinkMenu when context menu button is clicked", () => {
let button = wrapper.find(".context-menu-button");
let button = wrapper.find(ContextMenuButton);
button.simulate("click", { preventDefault: () => {} });
assert.equal(wrapper.find(LinkMenu).length, 1);
});
it("should pass dispatch, onUpdate, onShow, site, options, shouldSendImpressionStats, source and index to LinkMenu", () => {
it("should pass dispatch, onShow, site, options, shouldSendImpressionStats, source and index to LinkMenu", () => {
wrapper
.find(".context-menu-button")
.find(ContextMenuButton)
.simulate("click", { preventDefault: () => {} });
const linkMenuProps = wrapper.find(LinkMenu).props();
[
"dispatch",
"onUpdate",
"onShow",
"site",
"index",
@ -124,7 +125,7 @@ describe("<DSLinkMenu>", () => {
it("should pass through the correct menu options to LinkMenu", () => {
wrapper
.find(".context-menu-button")
.find(ContextMenuButton)
.simulate("click", { preventDefault: () => {} });
const linkMenuProps = wrapper.find(LinkMenu).props();
assert.deepEqual(linkMenuProps.options, [

Просмотреть файл

@ -164,17 +164,6 @@ describe("<SectionMenu>", () => {
});
describe(".onClick", () => {
const dispatch = sinon.stub();
const propOptions = [
"Separator",
"MoveUp",
"MoveDown",
"RemoveSection",
"CollapseSection",
"ExpandSection",
"ManageSection",
"AddTopSite",
"PrivacyNotice",
];
const expectedActionData = {
"newtab-section-menu-move-up": { id: "sectionId", direction: -1 },
"newtab-section-menu-move-down": { id: "sectionId", direction: +1 },
@ -197,11 +186,7 @@ describe("<SectionMenu>", () => {
},
};
const { options } = shallow(
<SectionMenu
{...DEFAULT_PROPS}
dispatch={dispatch}
options={propOptions}
/>
<SectionMenu {...DEFAULT_PROPS} dispatch={dispatch} />
)
.find(ContextMenu)
.props();
@ -238,4 +223,54 @@ describe("<SectionMenu>", () => {
});
});
});
describe("dispatch expand section if section is collapsed and adding top site", () => {
const dispatch = sinon.stub();
const expectedExpandData = {
id: DEFAULT_PROPS.id,
value: { collapsed: false },
};
const expectedAddData = { index: -1 };
const { options } = shallow(
<SectionMenu
{...DEFAULT_PROPS}
collapsed={true}
dispatch={dispatch}
extraOptions={["AddTopSite"]}
/>
)
.find(ContextMenu)
.props();
afterEach(() => dispatch.reset());
assert.equal(options[0].id, "newtab-section-menu-add-topsite");
options
.filter(o => o.id === "newtab-section-menu-add-topsite")
.forEach(option => {
it(`should dispatch an action to expand the section and to add a topsite after expanding`, () => {
option.onClick();
const [expandAction] = dispatch.firstCall.args;
assert.deepEqual(expandAction.data, expectedExpandData);
const [addAction] = dispatch.thirdCall.args;
assert.deepEqual(addAction.data, expectedAddData);
});
it(`should dispatch the expand userEvent and add topsite userEvent after expanding`, () => {
option.onClick();
assert.ok(dispatch.thirdCall.calledWith(option.action));
const [expandUserEvent] = dispatch.secondCall.args;
assert.isUserEventAction(expandUserEvent);
assert.propertyVal(
expandUserEvent.data,
"source",
DEFAULT_PROPS.source
);
const [addUserEvent] = dispatch.lastCall.args;
assert.isUserEventAction(addUserEvent);
assert.propertyVal(addUserEvent.data, "source", DEFAULT_PROPS.source);
});
});
});
});

Просмотреть файл

@ -22,6 +22,7 @@ import { mount, shallow } from "enzyme";
import { TopSiteForm } from "content-src/components/TopSites/TopSiteForm";
import { TopSiteFormInput } from "content-src/components/TopSites/TopSiteFormInput";
import { _TopSites as TopSites } from "content-src/components/TopSites/TopSites";
import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
const perfSvc = {
mark() {},
@ -724,24 +725,14 @@ describe("<TopSite>", () => {
});
it("should render a context menu button", () => {
const wrapper = shallow(<TopSite link={link} />);
assert.equal(wrapper.find(".context-menu-button").length, 1);
assert.equal(wrapper.find(ContextMenuButton).length, 1);
});
it("should render a link menu when button is clicked", () => {
it("should render a link menu", () => {
const wrapper = shallow(<TopSite link={link} />);
let button = wrapper.find(".context-menu-button");
assert.equal(wrapper.find(LinkMenu).length, 0);
button.simulate("click", { preventDefault: () => {} });
assert.equal(wrapper.find(LinkMenu).length, 1);
});
it("should not render a link menu by default", () => {
const wrapper = shallow(<TopSite link={link} />);
assert.equal(wrapper.find(LinkMenu).length, 0);
});
it("should pass onUpdate, site, options, and index to LinkMenu", () => {
const wrapper = shallow(<TopSite link={link} />);
wrapper
.find(".context-menu-button")
.simulate("click", { preventDefault: () => {} });
const linkMenuProps = wrapper.find(LinkMenu).props();
["onUpdate", "site", "index", "options"].forEach(prop =>
assert.property(linkMenuProps, prop)
@ -749,9 +740,6 @@ describe("<TopSite>", () => {
});
it("should pass through the correct menu options to LinkMenu", () => {
const wrapper = shallow(<TopSite link={link} />);
wrapper
.find(".context-menu-button")
.simulate("click", { preventDefault: () => {} });
const linkMenuProps = wrapper.find(LinkMenu).props();
assert.deepEqual(linkMenuProps.options, [
"CheckPinTopSite",

Просмотреть файл

@ -702,7 +702,7 @@ describe("selectLayoutRender", () => {
type: "foo3",
properties: { items: 3 },
feed: { url: "foo3.com" },
spocs: { positions: [{ index: 0, probability: 1 }] },
spocs: { positions: [{ index: 0 }], probability: 1 },
},
{ type: "foo4", properties: { items: 3 }, feed: { url: "foo4.com" } },
{ type: "foo5" },
@ -751,7 +751,7 @@ describe("selectLayoutRender", () => {
type: "foo3",
properties: { items: 3 },
feed: { url: "foo3.com" },
spocs: { positions: [{ index: 0, probability: 1 }] },
spocs: { positions: [{ index: 0 }], probability: 1 },
},
{ type: "foo4", properties: { items: 3 }, feed: { url: "foo4.com" } },
{ type: "foo5" },
@ -840,4 +840,67 @@ describe("selectLayoutRender", () => {
assert.equal(layoutRender[0].components[0].type, "TopSites");
assert.equal(layoutRender[0].components[1], undefined);
});
it("should skip rendering a spoc in position if that spoc is blocked for that session", () => {
const fakeLayout = [
{
width: 3,
components: [
{
type: "foo1",
properties: { items: 3 },
feed: { url: "foo1.com" },
spocs: { positions: [{ index: 0 }], probability: 1 },
},
],
},
];
const fakeSpocsData = {
lastUpdated: 0,
spocs: {
spocs: [{ name: "spoc", url: "https://foo.com" }],
},
};
store.dispatch({
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
data: { layout: fakeLayout },
});
store.dispatch({
type: at.DISCOVERY_STREAM_FEED_UPDATE,
data: {
feed: { data: { recommendations: [{ name: "rec" }] } },
url: "foo1.com",
},
});
store.dispatch({
type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
data: fakeSpocsData,
});
const { layoutRender: layout1 } = selectLayoutRender(
store.getState().DiscoveryStream,
{},
[]
);
store.dispatch({
type: at.DISCOVERY_STREAM_SPOC_BLOCKED,
data: { url: "https://foo.com" },
});
const { layoutRender: layout2 } = selectLayoutRender(
store.getState().DiscoveryStream,
{},
[]
);
assert.deepEqual(layout1[0].components[0].data.recommendations[0], {
name: "spoc",
url: "https://foo.com",
pos: 0,
});
assert.deepEqual(layout2[0].components[0].data.recommendations[0], {
name: "rec",
pos: 0,
});
});
});

Просмотреть файл

@ -1429,6 +1429,20 @@ describe("DiscoveryStreamFeed", () => {
"not_a_spoc.com"
);
});
it("should dispatch a DISCOVERY_STREAM_SPOC_BLOCKED for a blocked spoc", async () => {
Object.defineProperty(feed, "showSpocs", { get: () => true });
sandbox.spy(feed.store, "dispatch");
await feed.onAction({
type: at.PLACES_LINK_BLOCKED,
data: { url: "foo.com" },
});
assert.equal(
feed.store.dispatch.thirdCall.args[0].type,
"DISCOVERY_STREAM_SPOC_BLOCKED"
);
});
});
describe("#onAction: INIT", () => {

Просмотреть файл

@ -7,6 +7,8 @@ describe("ToolbarBadgeHub", () => {
let sandbox;
let instance;
let fakeAddImpression;
let fakeDispatch;
let isBrowserPrivateStub;
let fxaMessage;
let whatsnewMessage;
let fakeElement;
@ -14,11 +16,15 @@ describe("ToolbarBadgeHub", () => {
let everyWindowStub;
let clearTimeoutStub;
let setTimeoutStub;
let addObserverStub;
let removeObserverStub;
beforeEach(async () => {
globals = new GlobalOverrider();
sandbox = sinon.createSandbox();
instance = new _ToolbarBadgeHub();
fakeAddImpression = sandbox.stub();
fakeDispatch = sandbox.stub();
isBrowserPrivateStub = sandbox.stub();
const msgs = await PanelTestProvider.getMessages();
fxaMessage = msgs.find(({ id }) => id === "FXA_ACCOUNTS_BADGE");
whatsnewMessage = msgs.find(({ id }) => id.includes("WHATS_NEW_BADGE_"));
@ -40,9 +46,30 @@ describe("ToolbarBadgeHub", () => {
};
clearTimeoutStub = sandbox.stub();
setTimeoutStub = sandbox.stub();
globals.set("EveryWindow", everyWindowStub);
globals.set("setTimeout", setTimeoutStub);
globals.set("clearTimeout", clearTimeoutStub);
const fakeWindow = {
ownerGlobal: {
gBrowser: {
selectedBrowser: "browser",
},
},
};
addObserverStub = sandbox.stub();
removeObserverStub = sandbox.stub();
globals.set({
EveryWindow: everyWindowStub,
PrivateBrowsingUtils: { isBrowserPrivate: isBrowserPrivateStub },
setTimeout: setTimeoutStub,
clearTimeout: clearTimeoutStub,
Services: {
wm: {
getMostRecentWindow: () => fakeWindow,
},
prefs: {
addObserver: addObserverStub,
removeObserver: removeObserverStub,
},
},
});
});
afterEach(() => {
sandbox.restore();
@ -60,8 +87,21 @@ describe("ToolbarBadgeHub", () => {
assert.calledOnce(instance.messageRequest);
assert.calledWithExactly(instance.messageRequest, "toolbarBadgeUpdate");
});
it("should add a pref observer", async () => {
await instance.init(sandbox.stub().resolves(), {});
assert.calledOnce(addObserverStub);
assert.calledWithExactly(
addObserverStub,
instance.prefs.WHATSNEW_TOOLBAR_PANEL,
instance
);
});
});
describe("#uninit", () => {
beforeEach(async () => {
instance.init(sandbox.stub().resolves(), {});
});
it("should clear any setTimeout cbs", () => {
instance.init(sandbox.stub().resolves(), {});
@ -72,6 +112,16 @@ describe("ToolbarBadgeHub", () => {
assert.calledOnce(clearTimeoutStub);
assert.calledWithExactly(clearTimeoutStub, 2);
});
it("should remove the pref observer", () => {
instance.uninit();
assert.calledOnce(removeObserverStub);
assert.calledWithExactly(
removeObserverStub,
instance.prefs.WHATSNEW_TOOLBAR_PANEL,
instance
);
});
});
describe("messageRequest", () => {
let handleMessageRequestStub;
@ -166,6 +216,7 @@ describe("ToolbarBadgeHub", () => {
beforeEach(() => {
instance.init(sandbox.stub().resolves(), {
addImpression: fakeAddImpression,
dispatch: fakeDispatch,
});
sandbox.stub(instance, "addToolbarNotification").returns(fakeElement);
sandbox.stub(instance, "removeToolbarNotification");
@ -212,6 +263,18 @@ describe("ToolbarBadgeHub", () => {
assert.calledOnce(instance.removeToolbarNotification);
assert.calledWithExactly(instance.removeToolbarNotification, fakeElement);
});
it("should send an impression", async () => {
sandbox.stub(instance, "sendUserEventTelemetry");
instance.registerBadgeNotificationListener(fxaMessage);
assert.calledOnce(instance.sendUserEventTelemetry);
assert.calledWithExactly(
instance.sendUserEventTelemetry,
"IMPRESSION",
fxaMessage
);
});
it("should unregister notifications when forcing a badge via devtools", () => {
instance.registerBadgeNotificationListener(fxaMessage, { force: true });
@ -228,6 +291,16 @@ describe("ToolbarBadgeHub", () => {
instance.executeAction({ id: "show-whatsnew-button" });
assert.calledOnce(stub);
});
it("should call ToolbarPanelHub.enableAppmenuButton", () => {
const stub = sandbox.stub(
_ToolbarPanelHub.prototype,
"enableAppmenuButton"
);
instance.executeAction({ id: "show-whatsnew-button" });
assert.calledOnce(stub);
});
});
@ -245,6 +318,7 @@ describe("ToolbarBadgeHub", () => {
let blockMessageByIdStub;
let fakeEvent;
beforeEach(() => {
instance.init(sandbox.stub().resolves(), { dispatch: fakeDispatch });
blockMessageByIdStub = sandbox.stub();
sandbox.stub(instance, "_blockMessageById").value(blockMessageByIdStub);
instance.state = { notification: { id: fxaMessage.id } };
@ -307,6 +381,18 @@ describe("ToolbarBadgeHub", () => {
instance.removeAllNotifications
);
});
it("should send telemetry", () => {
fakeEvent.type = "click";
fakeEvent.button = 0;
sandbox.stub(instance, "sendUserEventTelemetry");
instance.removeAllNotifications(fakeEvent);
assert.calledOnce(instance.sendUserEventTelemetry);
assert.calledWithExactly(instance.sendUserEventTelemetry, "CLICK", {
id: "FXA_ACCOUNTS_BADGE",
});
});
it("should remove the event listeners after succesfully focusing the element", () => {
fakeEvent.type = "keypress";
fakeEvent.key = "Enter";
@ -367,4 +453,43 @@ describe("ToolbarBadgeHub", () => {
);
});
});
describe("#sendUserEventTelemetry", () => {
beforeEach(() => {
instance.init(sandbox.stub().resolves(), { dispatch: fakeDispatch });
});
it("should check for private window and not send", () => {
isBrowserPrivateStub.returns(true);
instance.sendUserEventTelemetry("CLICK", { id: fxaMessage });
assert.notCalled(instance._dispatch);
});
it("should check for private window and send", () => {
isBrowserPrivateStub.returns(false);
instance.sendUserEventTelemetry("CLICK", { id: fxaMessage });
assert.calledOnce(fakeDispatch);
const [ping] = instance._dispatch.firstCall.args;
assert.propertyVal(ping, "type", "TOOLBAR_BADGE_TELEMETRY");
assert.propertyVal(ping.data, "event", "CLICK");
});
});
describe("#observe", () => {
it("should make a message request when the whats new pref is changed", () => {
sandbox.stub(instance, "messageRequest");
instance.observe("", "", instance.prefs.WHATSNEW_TOOLBAR_PANEL);
assert.calledOnce(instance.messageRequest);
assert.calledWithExactly(instance.messageRequest, "toolbarBadgeUpdate");
});
it("should not react to other pref changes", () => {
sandbox.stub(instance, "messageRequest");
instance.observe("", "", "foo");
assert.notCalled(instance.messageRequest);
});
});
});

Просмотреть файл

@ -12,11 +12,16 @@ describe("ToolbarPanelHub", () => {
let fakeElementById;
let createdElements = [];
let eventListeners = {};
let addObserverStub;
let removeObserverStub;
let getBoolPrefStub;
let waitForInitializedStub;
beforeEach(async () => {
sandbox = sinon.createSandbox();
globals = new GlobalOverrider();
instance = new _ToolbarPanelHub();
waitForInitializedStub = sandbox.stub().resolves();
fakeElementById = {
setAttribute: sandbox.stub(),
removeAttribute: sandbox.stub(),
@ -59,38 +64,140 @@ describe("ToolbarPanelHub", () => {
registerCallback: sandbox.stub(),
unregisterCallback: sandbox.stub(),
};
addObserverStub = sandbox.stub();
removeObserverStub = sandbox.stub();
getBoolPrefStub = sandbox.stub();
globals.set("EveryWindow", everyWindowStub);
globals.set("Services", {
...Services,
prefs: {
addObserver: addObserverStub,
removeObserver: removeObserverStub,
getBoolPref: getBoolPrefStub,
},
});
});
afterEach(() => {
instance.uninit();
sandbox.restore();
globals.restore();
});
it("should create an instance", () => {
assert.ok(instance);
});
it("should not enableAppmenuButton() on init() if pref is not enabled", () => {
sandbox.stub(global.Services.prefs, "getBoolPref").returns(false);
getBoolPrefStub.returns(false);
instance.enableAppmenuButton = sandbox.stub();
instance.init({ getMessages: () => {} });
instance.init(waitForInitializedStub, { getMessages: () => {} });
assert.notCalled(instance.enableAppmenuButton);
});
it("should enableAppmenuButton() on init() if pref is enabled", () => {
sandbox.stub(global.Services.prefs, "getBoolPref").returns(true);
it("should enableAppmenuButton() on init() if pref is enabled", async () => {
getBoolPrefStub.returns(true);
instance.enableAppmenuButton = sandbox.stub();
instance.init({ getMessages: () => {} });
await instance.init(waitForInitializedStub, { getMessages: () => {} });
assert.calledOnce(instance.enableAppmenuButton);
});
it("should unregisterCallback on uninit()", () => {
instance.uninit();
assert.calledTwice(everyWindowStub.unregisterCallback);
});
it("should registerCallback on enableAppmenuButton()", () => {
instance.enableAppmenuButton();
it("should observe pref changes on init", async () => {
await instance.init(waitForInitializedStub, {});
assert.calledOnce(addObserverStub);
assert.calledWithExactly(
addObserverStub,
"browser.messaging-system.whatsNewPanel.enabled",
instance
);
});
it("should remove the observer on uninit", () => {
instance.uninit();
assert.calledOnce(removeObserverStub);
assert.calledWithExactly(
removeObserverStub,
"browser.messaging-system.whatsNewPanel.enabled",
instance
);
});
describe("#observe", () => {
it("should uninit if the pref is turned off", () => {
sandbox.stub(instance, "uninit");
getBoolPrefStub.returns(false);
instance.observe(
"",
"",
"browser.messaging-system.whatsNewPanel.enabled"
);
assert.calledOnce(instance.uninit);
});
it("shouldn't do anything if the pref is true", () => {
sandbox.stub(instance, "uninit");
getBoolPrefStub.returns(true);
instance.observe(
"",
"",
"browser.messaging-system.whatsNewPanel.enabled"
);
assert.notCalled(instance.uninit);
});
});
describe("#enableAppmenuButton", () => {
it("should registerCallback on enableAppmenuButton() if there are messages", async () => {
instance.init(waitForInitializedStub, {
getMessages: sandbox.stub().resolves([{}, {}]),
});
// init calls `enableAppmenuButton`
everyWindowStub.registerCallback.resetHistory();
await instance.enableAppmenuButton();
assert.calledOnce(everyWindowStub.registerCallback);
assert.calledWithExactly(
everyWindowStub.registerCallback,
"appMenu-whatsnew-button",
sinon.match.func,
sinon.match.func
);
});
it("should not registerCallback on enableAppmenuButton() if there are no messages", async () => {
instance.init(waitForInitializedStub, {
getMessages: sandbox.stub().resolves([]),
});
// init calls `enableAppmenuButton`
everyWindowStub.registerCallback.resetHistory();
await instance.enableAppmenuButton();
assert.notCalled(everyWindowStub.registerCallback);
});
});
describe("#enableToolbarButton", () => {
it("should registerCallback on enableToolbarButton if messages.length", async () => {
instance.init(waitForInitializedStub, {
getMessages: sandbox.stub().resolves([{}, {}]),
});
await instance.enableToolbarButton();
assert.calledOnce(everyWindowStub.registerCallback);
});
it("should registerCallback on enableToolbarButton()", () => {
instance.enableToolbarButton();
assert.calledOnce(everyWindowStub.registerCallback);
it("should not registerCallback on enableToolbarButton if no messages", async () => {
instance.init(waitForInitializedStub, {
getMessages: sandbox.stub().resolves([]),
});
await instance.enableToolbarButton();
assert.notCalled(everyWindowStub.registerCallback);
});
});
it("should unhide appmenu button on _showAppmenuButton()", () => {
instance._showAppmenuButton(fakeWindow);
@ -113,7 +220,7 @@ describe("ToolbarPanelHub", () => {
m => m.template === "whatsnew_panel_message"
);
messages[0].content.link_text = { string_id: "link_text_id" };
instance.init({
instance.init(waitForInitializedStub, {
getMessages: sandbox
.stub()
.returns([messages[0], messages[2], messages[1]]),
@ -143,14 +250,14 @@ describe("ToolbarPanelHub", () => {
const uniqueDates = [
...new Set(messages.map(m => m.content.published_date)),
];
instance.init({
instance.init(waitForInitializedStub, {
getMessages: sandbox.stub().returns(messages),
});
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
assert.callCount(instance._createDateElement, uniqueDates.length);
});
it("should listen for panelhidden and remove the toolbar button", async () => {
instance.init({
instance.init(waitForInitializedStub, {
getMessages: sandbox.stub().returns([]),
});
fakeDocument.getElementById
@ -162,7 +269,7 @@ describe("ToolbarPanelHub", () => {
assert.notCalled(fakeElementById.addEventListener);
});
it("should listen for panelhidden and remove the toolbar button", async () => {
instance.init({
instance.init(waitForInitializedStub, {
getMessages: sandbox.stub().returns([]),
});

Просмотреть файл

@ -2,6 +2,6 @@
ac_add_options --enable-official-branding
export MOZ_LTO=1
export MOZ_LTO=cross
. "$topsrcdir/build/mozconfig.common.override"

Просмотреть файл

@ -5,7 +5,7 @@ MOZ_REQUIRE_SIGNING=0
ac_add_options --disable-install-strip
export MOZ_LTO=1
export MOZ_LTO=cross
ac_add_options --enable-instruments

Просмотреть файл

@ -8,7 +8,7 @@ if test `uname -s` != Linux; then
ac_add_options --enable-dtrace
fi
export MOZ_LTO=1
export MOZ_LTO=cross
ac_add_options --with-branding=browser/branding/nightly

Просмотреть файл

@ -5,7 +5,7 @@
ac_add_options --enable-official-branding
export MOZ_LTO=1
export MOZ_LTO=cross
# safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
# defines.sh during the beta cycle

Просмотреть файл

@ -4,7 +4,7 @@
ac_add_options --enable-official-branding
export MOZ_LTO=1
export MOZ_LTO=cross
unset ENABLE_CLANG_PLUGIN

Просмотреть файл

@ -5,7 +5,7 @@
# Add-on signing is not required for DevEdition
MOZ_REQUIRE_SIGNING=0
export MOZ_LTO=1
export MOZ_LTO=cross
ac_add_options --with-branding=browser/branding/aurora

Просмотреть файл

@ -2,7 +2,7 @@
. "$topsrcdir/browser/config/mozconfigs/win64-aarch64/common-win64"
. "$topsrcdir/browser/config/mozconfigs/win64-aarch64/common-opt"
export MOZ_LTO=1
export MOZ_LTO=cross
ac_add_options --with-branding=browser/branding/nightly

Просмотреть файл

@ -7,7 +7,7 @@
ac_add_options --enable-official-branding
export MOZ_LTO=1
export MOZ_LTO=cross
unset ENABLE_CLANG_PLUGIN

Просмотреть файл

@ -6,4 +6,6 @@ export MOZ_PGO=1
ac_add_options --enable-official-branding
export MOZ_LTO=cross
. "$topsrcdir/build/mozconfig.common.override"

Просмотреть файл

@ -18,7 +18,3 @@ export MOZILLA_OFFICIAL=1
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1
if [ -n "$MOZ_PGO" ]; then
RUSTFLAGS="-Clinker-plugin-lto"
fi

Просмотреть файл

@ -12,4 +12,6 @@ ac_add_options --with-branding=browser/branding/aurora
# Enable MOZ_ALLOW_LEGACY_EXTENSIONS
ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
export MOZ_LTO=cross
. "$topsrcdir/build/mozconfig.common.override"

Просмотреть файл

@ -4,4 +4,6 @@
ac_add_options --with-branding=browser/branding/nightly
export MOZ_LTO=cross
. "$topsrcdir/build/mozconfig.common.override"

Просмотреть файл

@ -9,6 +9,8 @@ export MOZ_PGO=1
ac_add_options --enable-official-branding
export MOZ_LTO=cross
# safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
# defines.sh during the beta cycle
export BUILDING_RELEASE=1

Просмотреть файл

@ -1,15 +1,8 @@
diff -Nru valgrind-3.14.0/debian/changelog valgrind-3.14.0/debian/changelog
--- valgrind-3.14.0/debian/changelog 2018-11-15 09:21:43.000000000 +0900
+++ valgrind-3.14.0/debian/changelog 2018-11-30 15:20:10.000000000 +0900
@@ -1,3 +1,21 @@
+valgrind (1:3.14.0-1.deb7moz2) wheezy; urgency=medium
+
+ * debian/patches/vg401112: Cherry-pick upstream revision
+ f2c03ce3babe51eecbf03735f726c4028a162857.
+
+ -- Mike Hommey <glandium@debian.org> Fri, 30 Nov 2018 15:20:10 +0900
+
+valgrind (1:3.14.0-1.deb7moz1) wheezy; urgency=medium
diff -Nru valgrind-3.15.0/debian/changelog valgrind-3.15.0/debian/changelog
--- valgrind-3.15.0/debian/changelog 2019-07-14 19:23:29.000000000 +0900
+++ valgrind-3.15.0/debian/changelog 2019-07-17 18:19:13.000000000 +0900
@@ -1,3 +1,14 @@
+valgrind (1:3.15.0-1.deb7moz1) wheezy; urgency=medium
+
+ * Mozilla backport for wheezy.
+ * debian/control, debian/compat: Drop debhelper compat back to 7, which
@ -18,192 +11,33 @@ diff -Nru valgrind-3.14.0/debian/changelog valgrind-3.14.0/debian/changelog
+ add --parallel back.
+ * debian/valgrind-mpi.install: Use non-multiarch path.
+
+ -- Mike Hommey <glandium@mozilla.com> Thu, 15 Nov 2018 11:45:25 +0900
+ -- Mike Hommey <glandium@mozilla.com> Wed, 17 Jul 2019 18:19:13 +0900
+
valgrind (1:3.14.0-1) unstable; urgency=medium
valgrind (1:3.15.0-1) unstable; urgency=medium
* New upstream release (Closes: #913208)
diff -Nru valgrind-3.14.0/debian/compat valgrind-3.14.0/debian/compat
--- valgrind-3.14.0/debian/compat 2018-11-15 09:21:43.000000000 +0900
+++ valgrind-3.14.0/debian/compat 2018-11-15 11:45:25.000000000 +0900
* New upstream release
diff -Nru valgrind-3.15.0/debian/compat valgrind-3.15.0/debian/compat
--- valgrind-3.15.0/debian/compat 2019-07-14 19:23:29.000000000 +0900
+++ valgrind-3.15.0/debian/compat 2019-07-17 18:19:13.000000000 +0900
@@ -1 +1 @@
-11
-12
+7
diff -Nru valgrind-3.14.0/debian/control valgrind-3.14.0/debian/control
--- valgrind-3.14.0/debian/control 2018-11-15 09:21:43.000000000 +0900
+++ valgrind-3.14.0/debian/control 2018-11-15 11:45:25.000000000 +0900
diff -Nru valgrind-3.15.0/debian/control valgrind-3.15.0/debian/control
--- valgrind-3.15.0/debian/control 2019-07-14 19:23:29.000000000 +0900
+++ valgrind-3.15.0/debian/control 2019-07-17 18:19:13.000000000 +0900
@@ -2,7 +2,8 @@
Section: devel
Priority: optional
Maintainer: Alessandro Ghedini <ghedo@debian.org>
-Build-Depends: debhelper (>= 11),
-Build-Depends: debhelper (>= 12),
+Build-Depends: debhelper (>= 7.0.50~),
+ dh-autoreconf,
gdb,
gcc-multilib [amd64],
libc6-dev-i386 [amd64],
diff -Nru valgrind-3.14.0/debian/patches/series valgrind-3.14.0/debian/patches/series
--- valgrind-3.14.0/debian/patches/series 2018-11-15 09:21:43.000000000 +0900
+++ valgrind-3.14.0/debian/patches/series 2018-11-30 15:19:14.000000000 +0900
@@ -5,3 +5,4 @@
08_fix-spelling-in-manpage.patch
09_fix-armhf-detect.patch
10_mpi-pkg-config.patch
+vg401112
diff -Nru valgrind-3.14.0/debian/patches/vg401112 valgrind-3.14.0/debian/patches/vg401112
--- valgrind-3.14.0/debian/patches/vg401112 1970-01-01 09:00:00.000000000 +0900
+++ valgrind-3.14.0/debian/patches/vg401112 2018-11-30 15:19:46.000000000 +0900
@@ -0,0 +1,147 @@
+From f2c03ce3babe51eecbf03735f726c4028a162857 Mon Sep 17 00:00:00 2001
+From: Julian Seward <jseward@acm.org>
+Date: Wed, 28 Nov 2018 14:15:06 +0100
+Subject: [PATCH] Bug 401112 - LLVM 5.0 generates comparison against partially
+ initialized data.
+
+This generalises the existing spec rules for W of 32 bits:
+
+ W <u 0---(N-1)---0 1 0---0 or
+
+(that is, B/NB after SUBL, where dep2 has the above form), to also cover
+
+ W <=u 0---(N-1)---0 0 1---1
+
+(that is, BE/NBE after SUBL, where dept2 has the specified form).
+
+Patch from Nicolas B. Pierron (nicolas.b.pierron@nbp.name).
+---
+ VEX/priv/guest_amd64_helpers.c | 90 ++++++++++++++++++++++++++++++------------
+ 2 files changed, 66 insertions(+), 25 deletions(-)
+
+diff --git a/VEX/priv/guest_amd64_helpers.c b/VEX/priv/guest_amd64_helpers.c
+index a2b0789..30e82db 100644
+--- a/VEX/priv/guest_amd64_helpers.c
++++ b/VEX/priv/guest_amd64_helpers.c
+@@ -1013,13 +1013,10 @@ static inline Bool isU64 ( IRExpr* e, ULong n )
+ && e->Iex.Const.con->Ico.U64 == n;
+ }
+
+-/* Returns N if E is an immediate of the form 1 << N for N in 1 to 31,
++/* Returns N if W64 is a value of the form 1 << N for N in 1 to 31,
+ and zero in any other case. */
+-static Int isU64_1_shl_N ( IRExpr* e )
++static Int isU64_1_shl_N_literal ( ULong w64 )
+ {
+- if (e->tag != Iex_Const || e->Iex.Const.con->tag != Ico_U64)
+- return 0;
+- ULong w64 = e->Iex.Const.con->Ico.U64;
+ if (w64 < (1ULL << 1) || w64 > (1ULL << 31))
+ return 0;
+ if ((w64 & (w64 - 1)) != 0)
+@@ -1036,6 +1033,30 @@ static Int isU64_1_shl_N ( IRExpr* e )
+ return 0;
+ }
+
++/* Returns N if E is an immediate of the form 1 << N for N in 1 to 31,
++ and zero in any other case. */
++static Int isU64_1_shl_N ( IRExpr* e )
++{
++ if (e->tag != Iex_Const || e->Iex.Const.con->tag != Ico_U64)
++ return 0;
++ ULong w64 = e->Iex.Const.con->Ico.U64;
++ return isU64_1_shl_N_literal(w64);
++}
++
++/* Returns N if E is an immediate of the form (1 << N) - 1 for N in 1 to 31,
++ and zero in any other case. */
++static Int isU64_1_shl_N_minus_1 ( IRExpr* e )
++{
++ if (e->tag != Iex_Const || e->Iex.Const.con->tag != Ico_U64)
++ return 0;
++ ULong w64 = e->Iex.Const.con->Ico.U64;
++ // This isn't actually necessary since isU64_1_shl_N_literal will return
++ // zero given a zero argument, but still ..
++ if (w64 == 0xFFFFFFFFFFFFFFFFULL)
++ return 0;
++ return isU64_1_shl_N_literal(w64 + 1);
++}
++
+ IRExpr* guest_amd64_spechelper ( const HChar* function_name,
+ IRExpr** args,
+ IRStmt** precedingStmts,
+@@ -1258,32 +1279,51 @@ IRExpr* guest_amd64_spechelper ( const HChar* function_name,
+ /* It appears that LLVM 5.0 and later have a new way to find out
+ whether the top N bits of a word W are all zero, by computing
+
+- W <u 0---(N-1)---0 1 0---0
++ W <u 0---(N-1)---0 1 0---0 or
++ W <=u 0---(N-1)---0 0 1---1
+
+ In particular, the result will be defined if the top N bits of W
+ are defined, even if the trailing bits -- those corresponding to
+- the 0---0 section -- are undefined. Rather than make Memcheck
+- more complex, we detect this case where we can and shift out the
+- irrelevant and potentially undefined bits. */
++ the rightmost 0---0 / 1---1 section -- are undefined. Rather than
++ make Memcheck more complex, we detect this case where we can and
++ shift out the irrelevant and potentially undefined bits. */
+ Int n = 0;
+- if (isU64(cc_op, AMD64G_CC_OP_SUBL)
+- && (isU64(cond, AMD64CondB) || isU64(cond, AMD64CondNB))
+- && (n = isU64_1_shl_N(cc_dep2)) > 0) {
+- /* long sub/cmp, then B (unsigned less than),
+- where dep2 is a power of 2:
+- -> CmpLT32(dep1, 1 << N)
+- -> CmpEQ32(dep1 >>u N, 0)
+- and
+- long sub/cmp, then NB (unsigned greater than or equal),
+- where dep2 is a power of 2:
+- -> CmpGE32(dep1, 1 << N)
+- -> CmpNE32(dep1 >>u N, 0)
+- This avoids CmpLT32U/CmpGE32U being applied to potentially
+- uninitialised bits in the area being shifted out. */
++ Bool is_NB_or_NBE = False;
++ if (isU64(cc_op, AMD64G_CC_OP_SUBL)) {
++ if (isU64(cond, AMD64CondB) || isU64(cond, AMD64CondNB)) {
++ /* long sub/cmp, then B (unsigned less than),
++ where dep2 is a power of 2:
++ -> CmpLT32U(dep1, 1 << N)
++ -> CmpEQ32(dep1 >>u N, 0)
++ and
++ long sub/cmp, then NB (unsigned greater than or equal),
++ where dep2 is a power of 2:
++ -> CmpGE32U(dep1, 1 << N)
++ -> CmpNE32(dep1 >>u N, 0)
++ This avoids CmpLT32U/CmpGE32U being applied to potentially
++ uninitialised bits in the area being shifted out. */
++ n = isU64_1_shl_N(cc_dep2);
++ is_NB_or_NBE = isU64(cond, AMD64CondNB);
++ } else if (isU64(cond, AMD64CondBE) || isU64(cond, AMD64CondNBE)) {
++ /* long sub/cmp, then BE (unsigned less than or equal),
++ where dep2 is a power of 2 minus 1:
++ -> CmpLE32U(dep1, (1 << N) - 1)
++ -> CmpEQ32(dep1 >>u N, 0)
++ and
++ long sub/cmp, then NBE (unsigned greater than),
++ where dep2 is a power of 2 minus 1:
++ -> CmpGT32U(dep1, (1 << N) - 1)
++ -> CmpNE32(dep1 >>u N, 0)
++ This avoids CmpLE32U/CmpGT32U being applied to potentially
++ uninitialised bits in the area being shifted out. */
++ n = isU64_1_shl_N_minus_1(cc_dep2);
++ is_NB_or_NBE = isU64(cond, AMD64CondNBE);
++ }
++ }
++ if (n > 0) {
+ vassert(n >= 1 && n <= 31);
+- Bool isNB = isU64(cond, AMD64CondNB);
+ return unop(Iop_1Uto64,
+- binop(isNB ? Iop_CmpNE32 : Iop_CmpEQ32,
++ binop(is_NB_or_NBE ? Iop_CmpNE32 : Iop_CmpEQ32,
+ binop(Iop_Shr32, unop(Iop_64to32, cc_dep1),
+ mkU8(n)),
+ mkU32(0)));
+--
+2.9.3
+
diff -Nru valgrind-3.14.0/debian/rules valgrind-3.14.0/debian/rules
--- valgrind-3.14.0/debian/rules 2018-11-15 09:21:43.000000000 +0900
+++ valgrind-3.14.0/debian/rules 2018-11-15 11:45:25.000000000 +0900
diff -Nru valgrind-3.15.0/debian/rules valgrind-3.15.0/debian/rules
--- valgrind-3.15.0/debian/rules 2019-07-14 19:23:29.000000000 +0900
+++ valgrind-3.15.0/debian/rules 2019-07-17 18:19:13.000000000 +0900
@@ -11,7 +11,7 @@
LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
@ -222,9 +56,9 @@ diff -Nru valgrind-3.14.0/debian/rules valgrind-3.14.0/debian/rules
$(MAKE) -C docs FAQ.txt
$(MAKE) -C docs html-docs
$(MAKE) -C docs man-pages
diff -Nru valgrind-3.14.0/debian/valgrind-mpi.install valgrind-3.14.0/debian/valgrind-mpi.install
--- valgrind-3.14.0/debian/valgrind-mpi.install 2018-11-15 09:21:43.000000000 +0900
+++ valgrind-3.14.0/debian/valgrind-mpi.install 2018-11-15 11:45:25.000000000 +0900
diff -Nru valgrind-3.15.0/debian/valgrind-mpi.install valgrind-3.15.0/debian/valgrind-mpi.install
--- valgrind-3.15.0/debian/valgrind-mpi.install 2019-07-14 19:23:29.000000000 +0900
+++ valgrind-3.15.0/debian/valgrind-mpi.install 2019-07-17 18:19:13.000000000 +0900
@@ -1 +1 @@
-usr/lib/*/valgrind/libmpiwrap*
+usr/lib/valgrind/libmpiwrap*

Просмотреть файл

@ -1547,7 +1547,7 @@ def lto(value, pgo, profile_generate, c_compiler):
# MSVC's implementation of PGO implies LTO. Make clang-cl match this.
if c_compiler.type == 'clang-cl' and pgo and not profile_generate and value.origin == 'default':
value = ['thin']
value = ['cross']
if value:
enabled = True

Просмотреть файл

@ -26,7 +26,7 @@ else
if [ -n "$MOZ_PGO" ]; then
if [ -z "$USE_ARTIFACT" ]; then
export MOZ_LTO=1
export MOZ_LTO=cross
if [ -n "$MOZ_PGO_PROFILE_USE" ]; then
ac_add_options --enable-profile-use
ac_add_options --with-pgo-jarlog=/builds/worker/fetches/en-US.log

Просмотреть файл

@ -1839,7 +1839,7 @@ class GFX2D_API Factory {
static already_AddRefed<ScaledFont> CreateScaledFontForDWriteFont(
IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
bool aUseEmbeddedBitmap, bool aForceGDIMode,
bool aUseEmbeddedBitmap, int aRenderingMode,
IDWriteRenderingParams* aParams, Float aGamma, Float aContrast);
static void SetSystemTextQuality(uint8_t aQuality);

Просмотреть файл

@ -959,10 +959,11 @@ void Factory::D2DCleanup() {
already_AddRefed<ScaledFont> Factory::CreateScaledFontForDWriteFont(
IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
const RefPtr<UnscaledFont>& aUnscaledFont, float aSize,
bool aUseEmbeddedBitmap, bool aForceGDIMode,
bool aUseEmbeddedBitmap, int aRenderingMode,
IDWriteRenderingParams* aParams, Float aGamma, Float aContrast) {
return MakeAndAddRef<ScaledFontDWrite>(aFontFace, aUnscaledFont, aSize,
aUseEmbeddedBitmap, aForceGDIMode,
aUseEmbeddedBitmap,
(DWRITE_RENDERING_MODE)aRenderingMode,
aParams, aGamma, aContrast, aStyle);
}

Просмотреть файл

@ -126,14 +126,14 @@ static inline DWRITE_FONT_STRETCH DWriteFontStretchFromStretch(
ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace* aFontFace,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize, bool aUseEmbeddedBitmap,
bool aForceGDIMode,
DWRITE_RENDERING_MODE aRenderingMode,
IDWriteRenderingParams* aParams,
Float aGamma, Float aContrast,
const gfxFontStyle* aStyle)
: ScaledFontBase(aUnscaledFont, aSize),
mFontFace(aFontFace),
mUseEmbeddedBitmap(aUseEmbeddedBitmap),
mForceGDIMode(aForceGDIMode),
mRenderingMode(aRenderingMode),
mParams(aParams),
mGamma(aGamma),
mContrast(aContrast) {
@ -184,7 +184,7 @@ SkTypeface* ScaledFontDWrite::CreateSkTypeface() {
}
return SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle,
mForceGDIMode, gamma, contrast);
mRenderingMode, gamma, contrast);
}
#endif
@ -416,7 +416,7 @@ ScaledFontDWrite::InstanceData::InstanceData(
const wr::FontInstanceOptions* aOptions,
const wr::FontInstancePlatformOptions* aPlatformOptions)
: mUseEmbeddedBitmap(false),
mForceGDIMode(false),
mRenderingMode(DWRITE_RENDERING_MODE_DEFAULT),
mGamma(2.2f),
mContrast(1.0f) {
if (aOptions) {
@ -424,7 +424,11 @@ ScaledFontDWrite::InstanceData::InstanceData(
mUseEmbeddedBitmap = true;
}
if (aOptions->flags & wr::FontInstanceFlags_FORCE_GDI) {
mForceGDIMode = true;
mRenderingMode = DWRITE_RENDERING_MODE_GDI_CLASSIC;
} else if (aOptions->flags & wr::FontInstanceFlags_FORCE_SYMMETRIC) {
mRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
} else if (aOptions->flags & wr::FontInstanceFlags_NO_SYMMETRIC) {
mRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
}
}
if (aPlatformOptions) {
@ -505,6 +509,16 @@ bool ScaledFontDWrite::GetWRFontInstanceOptions(
} else {
options.flags |= wr::FontInstanceFlags_SUBPIXEL_POSITION;
}
switch (GetRenderingMode()) {
case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC:
options.flags |= wr::FontInstanceFlags_FORCE_SYMMETRIC;
break;
case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL:
options.flags |= wr::FontInstanceFlags_NO_SYMMETRIC;
break;
default:
break;
}
if (Factory::GetBGRSubpixelOrder()) {
options.flags |= wr::FontInstanceFlags_SUBPIXEL_BGR;
}
@ -594,7 +608,7 @@ already_AddRefed<ScaledFont> UnscaledFontDWrite::CreateScaledFont(
RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(
face, this, aGlyphSize, instanceData.mUseEmbeddedBitmap,
instanceData.mForceGDIMode, nullptr, instanceData.mGamma,
instanceData.mRenderingMode, nullptr, instanceData.mGamma,
instanceData.mContrast);
if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {

Просмотреть файл

@ -27,15 +27,17 @@ class ScaledFontDWrite final : public ScaledFontBase {
: ScaledFontBase(aUnscaledFont, aSize),
mFontFace(aFont),
mUseEmbeddedBitmap(false),
mForceGDIMode(false),
mRenderingMode(DWRITE_RENDERING_MODE_DEFAULT),
mGamma(2.2f),
mContrast(1.0f) {}
ScaledFontDWrite(IDWriteFontFace* aFontFace,
const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
bool aUseEmbeddedBitmap, bool aForceGDIMode,
IDWriteRenderingParams* aParams, Float aGamma,
Float aContrast, const gfxFontStyle* aStyle = nullptr);
bool aUseEmbeddedBitmap,
DWRITE_RENDERING_MODE aRenderingMode,
IDWriteRenderingParams* aParams,
Float aGamma, Float aContrast,
const gfxFontStyle* aStyle = nullptr);
FontType GetType() const override { return FontType::DWRITE; }
@ -61,8 +63,9 @@ class ScaledFontDWrite final : public ScaledFontBase {
AntialiasMode GetDefaultAAMode() override;
bool UseEmbeddedBitmaps() { return mUseEmbeddedBitmap; }
bool ForceGDIMode() { return mForceGDIMode; }
bool UseEmbeddedBitmaps() const { return mUseEmbeddedBitmap; }
bool ForceGDIMode() const { return mRenderingMode == DWRITE_RENDERING_MODE_GDI_CLASSIC; }
DWRITE_RENDERING_MODE GetRenderingMode() const { return mRenderingMode; }
#ifdef USE_SKIA
SkTypeface* CreateSkTypeface() override;
@ -71,7 +74,7 @@ class ScaledFontDWrite final : public ScaledFontBase {
RefPtr<IDWriteFontFace> mFontFace;
bool mUseEmbeddedBitmap;
bool mForceGDIMode;
DWRITE_RENDERING_MODE mRenderingMode;
// DrawTargetD2D1 requires the IDWriteRenderingParams,
// but we also separately need to store the gamma and contrast
// since Skia needs to be able to access these without having
@ -94,7 +97,7 @@ class ScaledFontDWrite final : public ScaledFontBase {
struct InstanceData {
explicit InstanceData(ScaledFontDWrite* aScaledFont)
: mUseEmbeddedBitmap(aScaledFont->mUseEmbeddedBitmap),
mForceGDIMode(aScaledFont->mForceGDIMode),
mRenderingMode(aScaledFont->mRenderingMode),
mGamma(aScaledFont->mGamma),
mContrast(aScaledFont->mContrast) {}
@ -102,7 +105,7 @@ class ScaledFontDWrite final : public ScaledFontBase {
const wr::FontInstancePlatformOptions* aPlatformOptions);
bool mUseEmbeddedBitmap;
bool mForceGDIMode;
DWRITE_RENDERING_MODE mRenderingMode;
Float mGamma;
Float mContrast;
};

Просмотреть файл

@ -53,7 +53,7 @@ struct IDWriteFontFallback;
SK_API SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
IDWriteFontFace* aFontFace,
SkFontStyle aStyle,
bool aForceGDI,
DWRITE_RENDERING_MODE aRenderingMode,
float aGamma,
float aContrast);

Просмотреть файл

@ -351,11 +351,11 @@ SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
IDWriteFontFace* aFontFace,
SkFontStyle aStyle,
bool aForceGDI,
DWRITE_RENDERING_MODE aRenderingMode,
float aGamma,
float aContrast)
{
return DWriteFontTypeface::Create(aFactory, aFontFace, aStyle, aForceGDI, aGamma, aContrast);
return DWriteFontTypeface::Create(aFactory, aFontFace, aStyle, aRenderingMode, aGamma, aContrast);
}
/**

Просмотреть файл

@ -335,20 +335,23 @@ SkScalerContext_DW::SkScalerContext_DW(sk_sp<DWriteFontTypeface> typefaceRef,
range.fVersion >= 1) ||
realTextSize > SkIntToScalar(20) || !is_hinted(this, typeface)) {
fTextSizeRender = realTextSize;
fRenderingMode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
fTextSizeMeasure = realTextSize;
fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
IDWriteRenderingParams* params = sk_get_dwrite_default_rendering_params();
DWriteFontTypeface* typeface = static_cast<DWriteFontTypeface*>(getTypeface());
if (params &&
!SUCCEEDED(typeface->fDWriteFontFace->GetRecommendedRenderingMode(
fTextSizeRender,
1.0f,
fMeasuringMode,
params,
&fRenderingMode))) {
fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
switch (typeface->GetRenderingMode()) {
case DWRITE_RENDERING_MODE_NATURAL:
case DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC:
fRenderingMode = typeface->GetRenderingMode();
break;
default:
if (IDWriteRenderingParams* params = sk_get_dwrite_default_rendering_params()) {
typeface->fDWriteFontFace->GetRecommendedRenderingMode(
fTextSizeRender, 1.0f, fMeasuringMode, params, &fRenderingMode);
}
break;
}
// We don't support outline mode right now.

Просмотреть файл

@ -57,7 +57,7 @@ private:
, fDWriteFontFamily(SkSafeRefComPtr(fontFamily))
, fDWriteFont(SkSafeRefComPtr(font))
, fDWriteFontFace(SkRefComPtr(fontFace))
, fForceGDI(false)
, fRenderingMode(DWRITE_RENDERING_MODE_DEFAULT)
, fGamma(2.2f)
, fContrast(1.0f)
{
@ -105,20 +105,21 @@ public:
static DWriteFontTypeface* Create(IDWriteFactory* factory,
IDWriteFontFace* fontFace,
SkFontStyle aStyle,
bool aForceGDI,
DWRITE_RENDERING_MODE aRenderingMode,
float aGamma,
float aContrast) {
DWriteFontTypeface* typeface =
new DWriteFontTypeface(aStyle, factory, fontFace,
nullptr, nullptr,
nullptr, nullptr);
typeface->fForceGDI = aForceGDI;
typeface->fRenderingMode = aRenderingMode;
typeface->fGamma = aGamma;
typeface->fContrast = aContrast;
return typeface;
}
bool ForceGDI() const { return fForceGDI; }
bool ForceGDI() const { return fRenderingMode == DWRITE_RENDERING_MODE_GDI_CLASSIC; }
DWRITE_RENDERING_MODE GetRenderingMode() const { return fRenderingMode; }
protected:
void weak_dispose() const override {
@ -156,7 +157,7 @@ protected:
private:
typedef SkTypeface INHERITED;
bool fForceGDI;
DWRITE_RENDERING_MODE fRenderingMode;
float fGamma;
float fContrast;
};

Просмотреть файл

@ -646,11 +646,14 @@ already_AddRefed<ScaledFont> gfxDWriteFont::GetScaledFont(
: gfxWindowsPlatform::TEXT_RENDERING_NORMAL)
: gfxWindowsPlatform::TEXT_RENDERING_NO_CLEARTYPE);
DWRITE_RENDERING_MODE renderingMode =
forceGDI ? DWRITE_RENDERING_MODE_GDI_CLASSIC : params->GetRenderingMode();
const gfxFontStyle* fontStyle = GetStyle();
mAzureScaledFont = Factory::CreateScaledFontForDWriteFont(
mFontFace, fontStyle, GetUnscaledFont(), GetAdjustedSize(),
useEmbeddedBitmap, forceGDI, params, params->GetGamma(),
params->GetEnhancedContrast());
useEmbeddedBitmap, (int)renderingMode,
params, params->GetGamma(), params->GetEnhancedContrast());
if (!mAzureScaledFont) {
return nullptr;
}

Просмотреть файл

@ -1574,20 +1574,26 @@ class GlyphBufferAzure {
}
}
// Ensure the buffer has enough space for aGlyphCount glyphs to be added.
// Ensure the buffer has enough space for aGlyphCount glyphs to be added,
// considering the supplied strike multipler aStrikeCount.
// This MUST be called before OutputGlyph is used to actually store glyph
// records in the buffer. It may be called repeated to add further capacity
// in case we don't know up-front exactly what will be needed.
void AddCapacity(uint32_t aGlyphCount) {
void AddCapacity(uint32_t aGlyphCount, uint32_t aStrikeCount) {
// Calculate the new capacity and ensure it will fit within the maximum
// allowed capacity.
static const uint64_t kMaxCapacity = 64 * 1024;
mCapacity = uint32_t(std::min(
kMaxCapacity,
uint64_t(mCapacity) + uint64_t(aGlyphCount) * uint64_t(aStrikeCount)));
// See if the required capacity fits within the already-allocated space
if (mCapacity + aGlyphCount <= mBufSize) {
mCapacity += aGlyphCount;
if (mCapacity <= mBufSize) {
return;
}
// We need to grow the buffer: determine a new size, allocate, and
// copy the existing data over if we didn't use realloc (which would
// do it automatically).
mBufSize = std::max(mCapacity + aGlyphCount, mBufSize * 2);
mBufSize = std::max(mCapacity, mBufSize * 2);
if (mBuffer == *mAutoBuffer.addr()) {
// switching from autobuffer to malloc, so we need to copy
mBuffer = reinterpret_cast<Glyph*>(moz_xmalloc(mBufSize * sizeof(Glyph)));
@ -1596,12 +1602,15 @@ class GlyphBufferAzure {
mBuffer = reinterpret_cast<Glyph*>(
moz_xrealloc(mBuffer, mBufSize * sizeof(Glyph)));
}
mCapacity += aGlyphCount;
}
void OutputGlyph(uint32_t aGlyphID, const gfx::Point& aPt) {
// If the buffer is full, flush to make room for the new glyph.
if (mNumGlyphs >= mCapacity) {
// Check that AddCapacity has been used appropriately!
MOZ_ASSERT(mNumGlyphs < mCapacity);
MOZ_ASSERT(mCapacity > 0 && mNumGlyphs == mCapacity);
Flush();
}
Glyph* glyph = mBuffer + mNumGlyphs++;
glyph->mIndex = aGlyphID;
glyph->mPosition = aPt;
@ -1805,7 +1814,7 @@ bool gfxFont::DrawGlyphs(const gfxShapedText* aShapedText,
// Allocate buffer space for the run, assuming all simple glyphs.
uint32_t capacityMult = 1 + aBuffer.mFontParams.extraStrikes;
aBuffer.AddCapacity(capacityMult * aCount);
aBuffer.AddCapacity(aCount, capacityMult);
bool emittedGlyphs = false;
@ -1825,7 +1834,7 @@ bool gfxFont::DrawGlyphs(const gfxShapedText* aShapedText,
uint32_t glyphCount = glyphData->GetGlyphCount();
if (glyphCount > 0) {
// Add extra buffer capacity to allow for multiple-glyph entry.
aBuffer.AddCapacity(capacityMult * (glyphCount - 1));
aBuffer.AddCapacity(glyphCount - 1, capacityMult);
const gfxShapedText::DetailedGlyph* details =
aShapedText->GetDetailedGlyphs(aOffset + i);
MOZ_ASSERT(details, "missing DetailedGlyph!");

Просмотреть файл

@ -122,7 +122,15 @@ void brush_vs(
vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
#ifdef WR_FEATURE_ALPHA_PASS
int color_mode = prim_user_data.x & 0xffff;
int blend_mode = prim_user_data.x >> 16;
int raster_space = prim_user_data.y;
if (color_mode == COLOR_MODE_FROM_PASS) {
color_mode = uMode;
}
// Derive the texture coordinates for this image, based on
// whether the source image is a local-space or screen-space
// image.
@ -137,14 +145,6 @@ void brush_vs(
default:
break;
}
#ifdef WR_FEATURE_ALPHA_PASS
int color_mode = prim_user_data.x & 0xffff;
int blend_mode = prim_user_data.x >> 16;
if (color_mode == COLOR_MODE_FROM_PASS) {
color_mode = uMode;
}
#endif
// Offset and scale vUv here to avoid doing it in the fragment shader.

Просмотреть файл

@ -1226,7 +1226,7 @@ impl BatchBuilder {
textures,
[
ShaderColorMode::Image as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
RasterizationSpace::Screen as i32,
RasterizationSpace::Local as i32,
get_shader_opacity(1.0),
0,
],

Просмотреть файл

@ -240,7 +240,7 @@ impl FrameBuilder {
retained_tiles: RetainedTiles,
globals: FrameGlobalResources,
) {
debug_assert!(self.pending_retained_tiles.tiles.is_empty());
assert!(self.pending_retained_tiles.caches.is_empty());
self.pending_retained_tiles = retained_tiles;
self.globals = globals;
}

Просмотреть файл

@ -111,26 +111,35 @@ struct PictureInfo {
_spatial_node_index: SpatialNodeIndex,
}
pub struct PictureCacheState {
/// The tiles retained by this picture cache.
pub tiles: FastHashMap<TileOffset, Tile>,
/// The current fractional offset of the cache transform root.
fract_offset: PictureVector2D,
}
/// Stores a list of cached picture tiles that are retained
/// between new scenes.
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct RetainedTiles {
/// The tiles retained between display lists.
#[cfg_attr(feature = "capture", serde(skip))] //TODO
pub tiles: FastHashMap<TileKey, Tile>,
pub caches: FastHashMap<usize, PictureCacheState>,
}
impl RetainedTiles {
pub fn new() -> Self {
RetainedTiles {
tiles: FastHashMap::default(),
caches: FastHashMap::default(),
}
}
/// Merge items from one retained tiles into another.
pub fn merge(&mut self, other: RetainedTiles) {
assert!(self.tiles.is_empty() || other.tiles.is_empty());
self.tiles.extend(other.tiles);
assert!(self.caches.is_empty() || other.caches.is_empty());
if self.caches.is_empty() {
self.caches = other.caches;
}
}
}
@ -198,14 +207,6 @@ impl From<PropertyBinding<f32>> for OpacityBinding {
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct TileId(usize);
#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq)]
pub struct TileKey {
/// The picture cache slice that this belongs to.
slice: usize,
/// Offset (in tile coords) of the tile within this slice.
offset: TileOffset,
}
/// Information about a cached tile.
#[derive(Debug)]
pub struct Tile {
@ -504,7 +505,7 @@ pub struct TileCacheInstance {
/// The positioning node for this tile cache.
pub spatial_node_index: SpatialNodeIndex,
/// Hash of tiles present in this picture.
pub tiles: FastHashMap<TileKey, Tile>,
pub tiles: FastHashMap<TileOffset, Tile>,
/// A helper struct to map local rects into surface coords.
map_local_to_surface: SpaceMapper<LayoutPixel, PicturePixel>,
/// List of opacity bindings, with some extra information
@ -525,7 +526,7 @@ pub struct TileCacheInstance {
/// Local clip rect for this tile cache.
pub local_clip_rect: PictureRect,
/// A list of tiles that are valid and visible, which should be drawn to the main scene.
pub tiles_to_draw: Vec<TileKey>,
pub tiles_to_draw: Vec<TileOffset>,
/// The world space viewport that this tile cache draws into.
/// Any clips outside this viewport can be ignored (and must be removed so that
/// we can draw outside the bounds of the viewport).
@ -542,6 +543,10 @@ pub struct TileCacheInstance {
/// The allowed subpixel mode for this surface, which depends on the detected
/// opacity of the background.
pub subpixel_mode: SubpixelMode,
/// The current fractional offset of the cache transform root. If this changes,
/// all tiles need to be invalidated and redrawn, since snapping differences are
/// likely to occur.
fract_offset: PictureVector2D,
}
impl TileCacheInstance {
@ -572,6 +577,7 @@ impl TileCacheInstance {
background_color,
opaque_rect: PictureRect::zero(),
subpixel_mode: SubpixelMode::Allow,
fract_offset: PictureVector2D::zero(),
}
}
@ -639,6 +645,53 @@ impl TileCacheInstance {
frame_context.clip_scroll_tree,
);
// If there are pending retained state, retrieve it.
if let Some(prev_state) = frame_state.retained_tiles.caches.remove(&self.slice) {
self.tiles.extend(prev_state.tiles);
self.fract_offset = prev_state.fract_offset;
}
// Map an arbitrary point in picture space to world space, to work out
// what the fractional translation is that's applied by this scroll root.
// TODO(gw): I'm not 100% sure this is right. At least, in future, we should
// make a specific API for this, and/or enforce that the picture
// cache transform only includes scale and/or translation (we
// already ensure it doesn't have perspective).
let world_origin = pic_to_world_mapper
.map(&PictureRect::new(PicturePoint::zero(), PictureSize::new(1.0, 1.0)))
.expect("bug: unable to map origin to world space")
.origin;
// Get the desired integer device coordinate
let device_origin = world_origin * frame_context.global_device_pixel_scale;
let desired_device_origin = device_origin.round();
// Unmap from device space to world space rect
let ref_world_rect = WorldRect::new(
desired_device_origin / frame_context.global_device_pixel_scale,
WorldSize::new(1.0, 1.0),
);
// Unmap from world space to picture space
let ref_point = pic_to_world_mapper
.unmap(&ref_world_rect)
.expect("bug: unable to unmap ref world rect")
.origin;
// Extract the fractional offset required in picture space to align in device space
let fract_offset = PictureVector2D::new(
ref_point.x.fract(),
ref_point.y.fract(),
);
// Determine if the fractional offset of the transform is different this frame
// from the currently cached tile set.
let fract_changed = (self.fract_offset.x - fract_offset.x).abs() > 0.001 ||
(self.fract_offset.y - fract_offset.y).abs() > 0.001;
if fract_changed {
self.fract_offset = fract_offset;
}
let spatial_node = &frame_context
.clip_scroll_tree
.spatial_nodes[self.spatial_node_index.0 as usize];
@ -751,31 +804,16 @@ impl TileCacheInstance {
self.tile_bounds_p0 = TileOffset::new(x0, y0);
self.tile_bounds_p1 = TileOffset::new(x1, y1);
// TODO(gw): Tidy this up as we add better support for retaining
// slices and sub-grid dirty areas.
let mut keys = Vec::new();
for key in frame_state.retained_tiles.tiles.keys() {
if key.slice == self.slice {
keys.push(*key);
}
}
for key in keys {
self.tiles.insert(key, frame_state.retained_tiles.tiles.remove(&key).unwrap());
}
let mut world_culling_rect = WorldRect::zero();
let mut old_tiles = mem::replace(
&mut self.tiles,
FastHashMap::default(),
);
let mut world_culling_rect = WorldRect::zero();
for y in y0 .. y1 {
for x in x0 .. x1 {
let key = TileKey {
offset: TileOffset::new(x, y),
slice: self.slice,
};
let key = TileOffset::new(x, y);
let mut tile = old_tiles
.remove(&key)
@ -784,10 +822,13 @@ impl TileCacheInstance {
Tile::new(next_id)
});
// Ensure each tile is offset by the appropriate amount from the
// origin, such that the content origin will be a whole number and
// the snapping will be consistent.
tile.rect = PictureRect::new(
PicturePoint::new(
x as f32 * self.tile_size.width,
y as f32 * self.tile_size.height,
x as f32 * self.tile_size.width + fract_offset.x,
y as f32 * self.tile_size.height + fract_offset.y,
),
self.tile_size,
);
@ -808,8 +849,9 @@ impl TileCacheInstance {
// Do tile invalidation for any dependencies that we know now.
for (_, tile) in &mut self.tiles {
// Start frame assuming that the tile has the same content.
tile.is_same_content = true;
// Start frame assuming that the tile has the same content,
// unless the fractional offset of the transform root changed.
tile.is_same_content = !fract_changed;
// Content has changed if any opacity bindings changed.
for binding in tile.descriptor.opacity_bindings.items() {
@ -1043,10 +1085,7 @@ impl TileCacheInstance {
for y in p0.y .. p1.y {
for x in p0.x .. p1.x {
// TODO(gw): Convert to 2d array temporarily to avoid hash lookups per-tile?
let key = TileKey {
slice: self.slice,
offset: TileOffset::new(x, y),
};
let key = TileOffset::new(x, y);
let tile = self.tiles.get_mut(&key).expect("bug: no tile");
// Mark if the tile is cacheable at all.
@ -1239,29 +1278,9 @@ impl TileCacheInstance {
TILE_SIZE_WIDTH,
TILE_SIZE_HEIGHT,
);
let content_origin_f = tile.world_rect.origin * frame_context.global_device_pixel_scale;
let content_origin_i = content_origin_f.floor();
// Calculate the UV coords for this tile. These are generally 0-1, but if the
// local rect of the cache has fractional coordinates, then the content origin
// of the tile is floor'ed, and so we need to adjust the UV rect in order to
// ensure a correct 1:1 texel:pixel mapping and correct snapping.
let s0 = (content_origin_f.x - content_origin_i.x) / tile.world_rect.size.width;
let t0 = (content_origin_f.y - content_origin_i.y) / tile.world_rect.size.height;
let s1 = 1.0;
let t1 = 1.0;
let uv_rect_kind = UvRectKind::Quad {
top_left: DeviceHomogeneousVector::new(s0, t0, 0.0, 1.0),
top_right: DeviceHomogeneousVector::new(s1, t0, 0.0, 1.0),
bottom_left: DeviceHomogeneousVector::new(s0, t1, 0.0, 1.0),
bottom_right: DeviceHomogeneousVector::new(s1, t1, 0.0, 1.0),
};
resource_cache.texture_cache.update_picture_cache(
tile_size,
&mut tile.handle,
uv_rect_kind,
gpu_cache,
);
}
@ -2048,7 +2067,15 @@ impl PicturePrimitive {
retained_tiles: &mut RetainedTiles,
) {
if let Some(tile_cache) = self.tile_cache.take() {
retained_tiles.tiles.extend(tile_cache.tiles);
if !tile_cache.tiles.is_empty() {
retained_tiles.caches.insert(
tile_cache.slice,
PictureCacheState {
tiles: tile_cache.tiles,
fract_offset: tile_cache.fract_offset,
},
);
}
}
}
@ -2434,10 +2461,10 @@ impl PicturePrimitive {
continue;
}
// The content origin for surfaces is always an integer value (this preserves
// the same snapping on a surface as would occur if drawn directly to parent).
let content_origin_f = tile.world_rect.origin * device_pixel_scale;
let content_origin = content_origin_f.floor().to_i32();
let content_origin = content_origin_f.round();
debug_assert!((content_origin_f.x - content_origin.x).abs() < 0.01);
debug_assert!((content_origin_f.y - content_origin.y).abs() < 0.01);
let cache_item = frame_state.resource_cache.texture_cache.get(&tile.handle);
@ -2449,7 +2476,7 @@ impl PicturePrimitive {
},
tile_size,
pic_index,
content_origin,
content_origin.to_i32(),
UvRectKind::Rect,
surface_spatial_node_index,
device_pixel_scale,

Просмотреть файл

@ -113,6 +113,10 @@ fn dwrite_render_mode(
FontRenderMode::Alpha | FontRenderMode::Subpixel => {
if bitmaps || font.flags.contains(FontInstanceFlags::FORCE_GDI) {
dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC
} else if font.flags.contains(FontInstanceFlags::FORCE_SYMMETRIC) {
dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC
} else if font.flags.contains(FontInstanceFlags::NO_SYMMETRIC) {
dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL
} else {
font_face.get_recommended_rendering_mode_default_params(em_size, 1.0, measure_mode)
}

Просмотреть файл

@ -1316,7 +1316,6 @@ impl TextureCache {
&mut self,
tile_size: DeviceIntSize,
handle: &mut TextureCacheHandle,
uv_rect_kind: UvRectKind,
gpu_cache: &mut GpuCache,
) {
debug_assert!(self.now.is_valid());
@ -1348,11 +1347,10 @@ impl TextureCache {
}
// Upload the resource rect and texture array layer.
let entry = self.entries
self.entries
.get_opt_mut(handle)
.expect("BUG: handle must be valid now");
entry.uv_rect_kind = uv_rect_kind;
entry.update_gpu_cache(gpu_cache);
.expect("BUG: handle must be valid now")
.update_gpu_cache(gpu_cache);
}
}

Просмотреть файл

@ -182,6 +182,8 @@ bitflags! {
// Windows flags
const FORCE_GDI = 1 << 16;
const FORCE_SYMMETRIC = 1 << 17;
const NO_SYMMETRIC = 1 << 18;
// Mac flags
const FONT_SMOOTHING = 1 << 16;

Просмотреть файл

@ -2,14 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::{WindowWrapper, NotifierEvent};
use crate::blob;
use euclid::{point2, size2, rect};
use std::sync::Arc;
use std::sync::atomic::{AtomicIsize, Ordering};
use std::sync::mpsc::Receiver;
use webrender::api::*;
use webrender::api::units::*;
use crate::{WindowWrapper, NotifierEvent};
use crate::blob;
use crate::reftest::{ReftestImage, ReftestImageComparison};
use crate::wrench::Wrench;
pub struct RawtestHarness<'a> {
@ -53,6 +54,41 @@ impl<'a> RawtestHarness<'a> {
self.wrench.renderer.read_pixels_rgba8(window_rect)
}
fn compare_pixels(&self, reference: Vec<u8>, test: Vec<u8>, size: FramebufferIntSize) {
let size = DeviceIntSize::new(size.width, size.height);
let reference = ReftestImage {
data: reference,
size,
};
let test = ReftestImage {
data: test,
size,
};
match reference.compare(&test) {
ReftestImageComparison::Equal => {}
ReftestImageComparison::NotEqual { max_difference, count_different } => {
let t = "rawtest";
println!(
"{} | {} | {}: {}, {}: {}",
"REFTEST TEST-UNEXPECTED-FAIL",
t,
"image comparison, max difference",
max_difference,
"number of differing pixels",
count_different
);
println!("REFTEST IMAGE 1 (TEST): {}", test.create_data_uri());
println!(
"REFTEST IMAGE 2 (REFERENCE): {}",
reference.create_data_uri()
);
println!("REFTEST TEST-END | {}", t);
panic!();
}
}
}
fn submit_dl(
&mut self,
epoch: &mut Epoch,
@ -574,7 +610,7 @@ impl<'a> RawtestHarness<'a> {
let pixels = self.render_and_get_pixels(window_rect);
assert!(pixels == original_pixels);
self.compare_pixels(original_pixels, pixels, window_rect.size);
// Leaving a tiled blob image in the resource cache
// confuses the `test_capture`. TODO: remove this
@ -667,7 +703,6 @@ impl<'a> RawtestHarness<'a> {
// use png;
// png::save_flipped("out1.png", &pixels_first, window_rect.size);
// png::save_flipped("out2.png", &pixels_second, window_rect.size);
assert!(pixels_first != pixels_second);
// cleanup
@ -905,8 +940,8 @@ impl<'a> RawtestHarness<'a> {
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let pixels_third = self.render_and_get_pixels(window_rect);
assert!(pixels_first == pixels_second);
assert!(pixels_first != pixels_third);
self.compare_pixels(pixels_first, pixels_second, window_rect.size);
}
// Ensures that content doing a save-restore produces the same results as not
@ -1012,7 +1047,7 @@ impl<'a> RawtestHarness<'a> {
let first = do_test(false);
let second = do_test(true);
assert_eq!(first, second);
self.compare_pixels(first, second, window_rect.size);
}
// regression test for #2769
@ -1141,7 +1176,7 @@ impl<'a> RawtestHarness<'a> {
// 5. render the built frame and compare
let pixels1 = self.render_and_get_pixels(window_rect);
assert!(pixels0 == pixels1);
self.compare_pixels(pixels0.clone(), pixels1, window_rect.size);
// 6. rebuild the scene and compare again
let mut txn = Transaction::new();
@ -1149,7 +1184,7 @@ impl<'a> RawtestHarness<'a> {
txn.generate_frame();
self.wrench.api.send_transaction(captured.document_id, txn);
let pixels2 = self.render_and_get_pixels(window_rect);
assert!(pixels0 == pixels2);
self.compare_pixels(pixels0, pixels2, window_rect.size);
}
fn test_zero_height_window(&mut self) {

Просмотреть файл

@ -114,11 +114,11 @@ impl Display for Reftest {
}
}
struct ReftestImage {
data: Vec<u8>,
size: DeviceIntSize,
pub struct ReftestImage {
pub data: Vec<u8>,
pub size: DeviceIntSize,
}
enum ReftestImageComparison {
pub enum ReftestImageComparison {
Equal,
NotEqual {
max_difference: usize,
@ -127,7 +127,7 @@ enum ReftestImageComparison {
}
impl ReftestImage {
fn compare(&self, other: &ReftestImage) -> ReftestImageComparison {
pub fn compare(&self, other: &ReftestImage) -> ReftestImageComparison {
assert_eq!(self.size, other.size);
assert_eq!(self.data.len(), other.data.len());
assert_eq!(self.data.len() % 4, 0);
@ -158,7 +158,7 @@ impl ReftestImage {
}
}
fn create_data_uri(mut self) -> String {
pub fn create_data_uri(mut self) -> String {
let width = self.size.width;
let height = self.size.height;

Просмотреть файл

@ -395,7 +395,7 @@ JS::Result<Ok> BinASTParserPerTokenizer<Tok>::finishLazyFunction(
if (funbox->strict()) {
lazy->setStrict();
}
lazy->setIsBinAST();
MOZ_ASSERT(lazy->isBinAST());
fun->initLazyScript(lazy);
return Ok();

Просмотреть файл

@ -2070,9 +2070,7 @@ class JSScript : public js::gc::TenuredCell {
// Whether the Parser declared 'arguments'.
ShouldDeclareArguments = 1 << 25,
// Whether source is BinAST.
// FIXME: JSScript should also set this flag correctly.
IsBinAST = 1 << 26,
// (1 << 26) is unused.
// Whether this script contains a direct eval statement.
HasDirectEval = 1 << 27,
@ -3522,8 +3520,7 @@ class LazyScript : public gc::TenuredCell {
return frontend::ParseGoal::Script;
}
bool isBinAST() const { return hasFlag(ImmutableFlags::IsBinAST); }
void setIsBinAST() { setFlag(ImmutableFlags::IsBinAST); }
bool isBinAST() const { return scriptSource()->hasBinASTSource(); }
bool strict() const { return hasFlag(ImmutableFlags::Strict); }
void setStrict() { setFlag(ImmutableFlags::Strict); }

Просмотреть файл

@ -10,7 +10,7 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
export MOZ_LTO=1
export MOZ_LTO=cross
export MOZILLA_OFFICIAL=1
export MOZ_ANDROID_POCKET=1

Просмотреть файл

@ -12,7 +12,7 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
export MOZ_LTO=1
export MOZ_LTO=cross
export MOZILLA_OFFICIAL=1
export MOZ_ANDROID_POCKET=1

Просмотреть файл

@ -19,6 +19,6 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
export MOZ_LTO=1
export MOZ_LTO=cross
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"

Просмотреть файл

@ -21,6 +21,6 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
export MOZ_LTO=1
export MOZ_LTO=cross
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"

Просмотреть файл

@ -17,6 +17,6 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
export MOZ_LTO=1
export MOZ_LTO=cross
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"

Просмотреть файл

@ -19,6 +19,6 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
export MOZ_LTO=1
export MOZ_LTO=cross
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"

Просмотреть файл

@ -10,7 +10,7 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
export MOZ_LTO=1
export MOZ_LTO=cross
export MOZILLA_OFFICIAL=1
export MOZ_ANDROID_POCKET=1

Просмотреть файл

@ -12,7 +12,7 @@ export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
export MOZ_LTO=1
export MOZ_LTO=cross
export MOZILLA_OFFICIAL=1
export MOZ_ANDROID_POCKET=1

Просмотреть файл

@ -75,8 +75,6 @@ jobs:
symbol: I(deb7-bb)
parent: debian7-base
definition: debian-build
packages:
- deb7-valgrind
args:
ARCH: amd64
debian7-amd64-build:

Просмотреть файл

@ -192,8 +192,8 @@ jobs:
run:
using: debian-package
dsc:
url: http://snapshot.debian.org/archive/debian/20181115T045552Z/pool/main/v/valgrind/valgrind_3.14.0-1.dsc
sha256: 6709e2fe4e8251ee32f3cfbf2c6ee106a5cfa3e8dc672cf1dd5f2b26e72b64ee
url: http://snapshot.debian.org/archive/debian/20190714T155055Z/pool/main/v/valgrind/valgrind_3.15.0-1.dsc
sha256: 2bd467ec486302060ed4e91452231b2c0a09afcd51c25d4ba8d3028218328681
patch: valgrind-wheezy.diff
deb7-dh-python:

Просмотреть файл

@ -1,10 +1,4 @@
[background-position-x-valid.html]
[e.style['background-position-x'\] = "calc(10px - 0.5em)" should set the property value]
expected: FAIL
[e.style['background-position-x'\] = "calc(10px - 0.5em), left -20%, right 10px" should set the property value]
expected: FAIL
[e.style['background-position-x'\] = "x-end" should set the property value]
expected: FAIL

Просмотреть файл

@ -11,6 +11,3 @@
[e.style['background-position-y'\] = "y-end" should set the property value]
expected: FAIL
[e.style['background-position-y'\] = "calc(10px - 0.5em)" should set the property value]
expected: FAIL

Просмотреть файл

@ -1,4 +0,0 @@
[border-block-valid.html]
[e.style['border-block-end'\] = "calc(10px - 0.5em) dotted red" should set the property value]
expected: FAIL

Просмотреть файл

@ -1,10 +0,0 @@
[border-block-width-valid.html]
[e.style['border-block-end-width'\] = "calc(10px - 0.5em)" should set the property value]
expected: FAIL
[e.style['border-block-start-width'\] = "calc(10px + 0.5em)" should set the property value]
expected: FAIL
[e.style['border-block-width'\] = "medium calc(10px + 0.5em)" should set the property value]
expected: FAIL

Просмотреть файл

@ -1,4 +0,0 @@
[border-inline-valid.html]
[e.style['border-inline-end'\] = "calc(10px - 0.5em) dotted red" should set the property value]
expected: FAIL

Просмотреть файл

@ -1,10 +0,0 @@
[border-inline-width-valid.html]
[e.style['border-inline-width'\] = "medium calc(10px + 0.5em)" should set the property value]
expected: FAIL
[e.style['border-inline-end-width'\] = "calc(10px - 0.5em)" should set the property value]
expected: FAIL
[e.style['border-inline-start-width'\] = "calc(10px + 0.5em)" should set the property value]
expected: FAIL

Просмотреть файл

@ -1,13 +0,0 @@
[inset-block-inline-valid.html]
[e.style['inset-block'\] = "calc(10px - 0.5em) -20%" should set the property value]
expected: FAIL
[e.style['inset-inline'\] = "calc(10px - 0.5em)" should set the property value]
expected: FAIL
[e.style['inset-inline-end'\] = "calc(10px - 0.5em)" should set the property value]
expected: FAIL
[e.style['inset-inline'\] = "auto calc(10px + 0.5em)" should set the property value]
expected: FAIL

Просмотреть файл

@ -1,4 +0,0 @@
[border-spacing-valid.html]
[e.style['border-spacing'\] = "calc(10px + 0.5em) calc(10px - 0.5em)" should set the property value]
expected: FAIL

Просмотреть файл

@ -19,13 +19,13 @@ test_valid_value("background-position-x", "x-end");
test_valid_value("background-position-x", "-20%");
test_valid_value("background-position-x", "10px");
test_valid_value("background-position-x", "0.5em");
test_valid_value("background-position-x", "calc(10px - 0.5em)");
test_valid_value("background-position-x", "calc(10px - 0.5em)", "calc(-0.5em + 10px)");
test_valid_value("background-position-x", "left -20%");
test_valid_value("background-position-x", "right 10px");
test_valid_value("background-position-x", "-20%, 10px");
test_valid_value("background-position-x", "center, left, right");
test_valid_value("background-position-x", "0.5em, x-start, x-end");
test_valid_value("background-position-x", "calc(10px - 0.5em), left -20%, right 10px");
test_valid_value("background-position-x", "calc(10px - 0.5em), left -20%, right 10px", "calc(-0.5em + 10px), left -20%, right 10px");
</script>
</body>
</html>

Просмотреть файл

@ -19,13 +19,13 @@ test_valid_value("background-position-y", "y-end");
test_valid_value("background-position-y", "-20%");
test_valid_value("background-position-y", "10px");
test_valid_value("background-position-y", "0.5em");
test_valid_value("background-position-y", "calc(10px - 0.5em)");
test_valid_value("background-position-y", "calc(10px - 0.5em)", "calc(-0.5em + 10px)");
test_valid_value("background-position-y", "top -20%");
test_valid_value("background-position-y", "bottom 10px");
test_valid_value("background-position-y", "-20%, 10px");
test_valid_value("background-position-y", "center, top, bottom");
test_valid_value("background-position-y", "0.5em, y-start, y-end");
test_valid_value("background-position-y", "calc(10px - 0.5em), top -20%, bottom 10px");
test_valid_value("background-position-y", "calc(10px - 0.5em), top -20%, bottom 10px", "calc(-0.5em + 10px), top -20%, top 10px");
</script>
</body>
</html>

Просмотреть файл

@ -19,7 +19,7 @@ test_valid_value("border-block", "double", ["double", "medium double"]);
test_valid_value("border-block-start", "green double thin", "thin double green");
test_valid_value("border-block-start", "green", ["green", "medium none green"]);
test_valid_value("border-block-end", "thin", ["thin", "thin none"]);
test_valid_value("border-block-end", "calc(10px - 0.5em) dotted red");
test_valid_value("border-block-end", "calc(10px - 0.5em) dotted red", "calc(-0.5em + 10px) dotted red");
</script>
</body>
</html>

Просмотреть файл

@ -13,14 +13,14 @@
<script>
// <length> | thin | medium | thick
test_valid_value("border-block-start-width", "10px");
test_valid_value("border-block-start-width", "calc(10px + 0.5em)");
test_valid_value("border-block-start-width", "calc(10px + 0.5em)", "calc(0.5em + 10px)");
test_valid_value("border-block-start-width", "thick");
test_valid_value("border-block-start-width", "thin");
test_valid_value("border-block-end-width", "0", "0px");
test_valid_value("border-block-end-width", "calc(10px - 0.5em)");
test_valid_value("border-block-end-width", "calc(10px - 0.5em)", "calc(-0.5em + 10px)");
test_valid_value("border-block-end-width", "medium");
test_valid_value("border-block-width", "10px");
test_valid_value("border-block-width", "medium calc(10px + 0.5em)");
test_valid_value("border-block-width", "medium calc(10px + 0.5em)", "medium calc(0.5em + 10px)");
test_valid_value("border-block-width", "10px 10px", "10px");
</script>
</body>

Просмотреть файл

@ -19,7 +19,7 @@ test_valid_value("border-inline", "double", ["double", "medium double"]);
test_valid_value("border-inline-start", "green double thin", "thin double green");
test_valid_value("border-inline-start", "green", ["green", "medium none green"]);
test_valid_value("border-inline-end", "thin", ["thin", "thin none"]);
test_valid_value("border-inline-end", "calc(10px - 0.5em) dotted red");
test_valid_value("border-inline-end", "calc(10px - 0.5em) dotted red", "calc(-0.5em + 10px) dotted red");
</script>
</body>
</html>

Просмотреть файл

@ -13,14 +13,14 @@
<script>
// <length> | thin | medium | thick
test_valid_value("border-inline-start-width", "10px");
test_valid_value("border-inline-start-width", "calc(10px + 0.5em)");
test_valid_value("border-inline-start-width", "calc(10px + 0.5em)", "calc(0.5em + 10px)");
test_valid_value("border-inline-start-width", "thick");
test_valid_value("border-inline-start-width", "thin");
test_valid_value("border-inline-end-width", "0", "0px");
test_valid_value("border-inline-end-width", "calc(10px - 0.5em)");
test_valid_value("border-inline-end-width", "calc(10px - 0.5em)", "calc(-0.5em + 10px)");
test_valid_value("border-inline-end-width", "medium");
test_valid_value("border-inline-width", "10px");
test_valid_value("border-inline-width", "medium calc(10px + 0.5em)");
test_valid_value("border-inline-width", "medium calc(10px + 0.5em)", "medium calc(0.5em + 10px)");
test_valid_value("border-inline-width", "10px 10px", "10px");
</script>
</body>

Просмотреть файл

@ -15,16 +15,16 @@
test_valid_value("inset-block-start", "auto");
test_valid_value("inset-block-end", "-10px");
test_valid_value("inset-inline-start", "-20%");
test_valid_value("inset-inline-end", "calc(10px - 0.5em)");
test_valid_value("inset-inline-end", "calc(10px - 0.5em)", "calc(-0.5em + 10px)");
test_valid_value("inset-block", "auto");
test_valid_value("inset-block", "-10px");
test_valid_value("inset-block", "calc(10px - 0.5em) -20%");
test_valid_value("inset-block", "calc(10px - 0.5em) -20%", "calc(-0.5em + 10px) -20%");
test_valid_value("inset-block", "auto auto", "auto");
test_valid_value("inset-inline", "-20%");
test_valid_value("inset-inline", "calc(10px - 0.5em)");
test_valid_value("inset-inline", "calc(10px - 0.5em)", "calc(-0.5em + 10px)");
test_valid_value("inset-inline", "-10px auto");
test_valid_value("inset-inline", "auto calc(10px + 0.5em)");
test_valid_value("inset-inline", "auto calc(10px + 0.5em)", "auto calc(0.5em + 10px)");
</script>
</body>
</html>

Просмотреть файл

@ -13,7 +13,7 @@
<script>
test_valid_value("border-spacing", "0px");
test_valid_value("border-spacing", "10px 20px");
test_valid_value("border-spacing", "calc(10px + 0.5em) calc(10px - 0.5em)");
test_valid_value("border-spacing", "calc(10px + 0.5em) calc(10px - 0.5em)", "calc(0.5em + 10px) calc(-0.5em + 10px)");
</script>
</body>
</html>