Merge mozilla-central to inbound. a=merge CLOSED TREE
|
@ -496,12 +496,8 @@ pref("browser.tabs.remote.separatePrivilegedContentProcess", true);
|
|||
// Turn on HTTP response process selection.
|
||||
pref("browser.tabs.remote.useHTTPResponseProcessSelection", true);
|
||||
|
||||
// Unload tabs on low-memory on nightly and beta.
|
||||
#ifdef EARLY_BETA_OR_EARLIER
|
||||
// Unload tabs when available memory is running low
|
||||
pref("browser.tabs.unloadOnLowMemory", true);
|
||||
#else
|
||||
pref("browser.tabs.unloadOnLowMemory", false);
|
||||
#endif
|
||||
|
||||
pref("browser.ctrlTab.recentlyUsedOrder", true);
|
||||
|
||||
|
@ -1320,6 +1316,12 @@ pref("browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", tru
|
|||
pref("browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", false);
|
||||
#endif
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("trailhead.firstrun.cohort", 1);
|
||||
#else
|
||||
pref("trailhead.firstrun.cohort", 0);
|
||||
#endif
|
||||
|
||||
// Enable the DOM fullscreen API.
|
||||
pref("full-screen-api.enabled", true);
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
module.exports = {
|
||||
"plugins": [
|
||||
"jsx-a11y" // require("eslint-plugin-jsx-a11y")
|
||||
],
|
||||
"extends": "plugin:jsx-a11y/recommended",
|
||||
"overrides": [{
|
||||
// These files use fluent-dom to insert content
|
||||
"files": [
|
||||
"content-src/asrouter/templates/OnboardingMessage/**",
|
||||
"content-src/asrouter/templates/Trailhead/**",
|
||||
],
|
||||
"rules": {
|
||||
"jsx-a11y/anchor-has-content": 0,
|
||||
"jsx-a11y/heading-has-content": 0,
|
||||
}
|
||||
}],
|
||||
};
|
|
@ -100,8 +100,9 @@ function templateHTML(options, html) {
|
|||
<link rel="stylesheet" href="${options.baseUrl}css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root">${isPrerendered ? html : "<!-- Regular React Rendering -->"}</div>
|
||||
<div id="footer-snippets-container"></div>${options.noscripts ? "" : scriptRender}
|
||||
<div id="footer-asrouter-container"></div>${options.noscripts ? "" : scriptRender}
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
|
|
@ -46,10 +46,12 @@ for (const type of [
|
|||
"DISCOVERY_STREAM_IMPRESSION_STATS",
|
||||
"DISCOVERY_STREAM_LAYOUT_RESET",
|
||||
"DISCOVERY_STREAM_LAYOUT_UPDATE",
|
||||
"DISCOVERY_STREAM_LINK_BLOCKED",
|
||||
"DISCOVERY_STREAM_LOADED_CONTENT",
|
||||
"DISCOVERY_STREAM_OPT_OUT",
|
||||
"DISCOVERY_STREAM_SPOCS_CAPS",
|
||||
"DISCOVERY_STREAM_SPOCS_ENDPOINT",
|
||||
"DISCOVERY_STREAM_SPOCS_FILL",
|
||||
"DISCOVERY_STREAM_SPOCS_UPDATE",
|
||||
"DISCOVERY_STREAM_SPOC_IMPRESSION",
|
||||
"DOWNLOAD_CHANGED",
|
||||
|
@ -291,6 +293,21 @@ function ASRouterUserEvent(data) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* DiscoveryStreamSpocsFill - A telemetry ping indicating a SPOCS Fill event.
|
||||
*
|
||||
* @param {object} data Fields to include in the ping (spocs_fills, etc.)
|
||||
* @param {int} importContext (For testing) Override the import context for testing.
|
||||
* @return {object} An AlsoToMain action
|
||||
*/
|
||||
function DiscoveryStreamSpocsFill(data, importContext = globalImportContext) {
|
||||
const action = {
|
||||
type: actionTypes.DISCOVERY_STREAM_SPOCS_FILL,
|
||||
data,
|
||||
};
|
||||
return importContext === UI_CODE ? AlsoToMain(action) : action;
|
||||
}
|
||||
|
||||
/**
|
||||
* UndesiredEvent - A telemetry ping indicating an undesired state.
|
||||
*
|
||||
|
@ -398,6 +415,7 @@ this.actionCreators = {
|
|||
WebExtEvent,
|
||||
DiscoveryStreamImpressionStats,
|
||||
DiscoveryStreamLoadedContent,
|
||||
DiscoveryStreamSpocsFill,
|
||||
};
|
||||
|
||||
// These are helpers to test for certain kinds of actions
|
||||
|
|
|
@ -533,7 +533,7 @@ function DiscoveryStream(prevState = INITIAL_STATE.DiscoveryStream, action) {
|
|||
};
|
||||
}
|
||||
return prevState;
|
||||
case at.PLACES_LINK_BLOCKED:
|
||||
case at.DISCOVERY_STREAM_LINK_BLOCKED:
|
||||
return isNotReady() ? prevState :
|
||||
nextState(items => items.filter(item => item.url !== action.data.url));
|
||||
|
||||
|
|
|
@ -11,9 +11,11 @@ import ReactDOM from "react-dom";
|
|||
import {ReturnToAMO} from "./templates/ReturnToAMO/ReturnToAMO";
|
||||
import {SnippetsTemplates} from "./templates/template-manifest";
|
||||
import {StartupOverlay} from "./templates/StartupOverlay/StartupOverlay";
|
||||
import {Trailhead} from "./templates/Trailhead/Trailhead";
|
||||
|
||||
const INCOMING_MESSAGE_NAME = "ASRouter:parent-to-child";
|
||||
const OUTGOING_MESSAGE_NAME = "ASRouter:child-to-parent";
|
||||
const TEMPLATES_ABOVE_PAGE = ["trailhead"];
|
||||
const TEMPLATES_BELOW_SEARCH = ["simple_below_search_snippet"];
|
||||
|
||||
export const ASRouterUtils = {
|
||||
|
@ -96,7 +98,8 @@ export class ASRouterUISurface extends React.PureComponent {
|
|||
this.sendUserActionTelemetry = this.sendUserActionTelemetry.bind(this);
|
||||
this.state = {message: {}, bundle: {}};
|
||||
if (props.document) {
|
||||
this.portalContainer = props.document.getElementById("footer-snippets-container");
|
||||
this.headerPortal = props.document.getElementById("header-asrouter-container");
|
||||
this.footerPortal = props.document.getElementById("footer-asrouter-container");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,7 +224,8 @@ export class ASRouterUISurface extends React.PureComponent {
|
|||
renderSnippets() {
|
||||
if (this.state.bundle.template === "onboarding" ||
|
||||
this.state.message.template === "fxa_overlay" ||
|
||||
this.state.message.template === "return_to_amo_overlay") {
|
||||
this.state.message.template === "return_to_amo_overlay" ||
|
||||
this.state.message.template === "trailhead") {
|
||||
return null;
|
||||
}
|
||||
const SnippetComponent = SnippetsTemplates[this.state.message.template];
|
||||
|
@ -288,6 +292,20 @@ export class ASRouterUISurface extends React.PureComponent {
|
|||
return null;
|
||||
}
|
||||
|
||||
renderTrailhead() {
|
||||
const {message} = this.state;
|
||||
if (message.template === "trailhead") {
|
||||
return (<Trailhead
|
||||
message={message}
|
||||
onAction={ASRouterUtils.executeAction}
|
||||
onDoneButton={this.dismissBundle(this.state.bundle.bundle)}
|
||||
sendUserActionTelemetry={this.sendUserActionTelemetry}
|
||||
dispatch={this.props.dispatch}
|
||||
fxaEndpoint={this.props.fxaEndpoint} />);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
renderPreviewBanner() {
|
||||
if (this.state.message.provider !== "preview") {
|
||||
return null;
|
||||
|
@ -305,6 +323,7 @@ export class ASRouterUISurface extends React.PureComponent {
|
|||
const {message, bundle} = this.state;
|
||||
if (!message.id && !bundle.template) { return null; }
|
||||
const shouldRenderBelowSearch = TEMPLATES_BELOW_SEARCH.includes(message.template);
|
||||
const shouldRenderInHeader = TEMPLATES_ABOVE_PAGE.includes(message.template);
|
||||
|
||||
return shouldRenderBelowSearch ?
|
||||
// Render special below search snippets in place;
|
||||
|
@ -314,11 +333,12 @@ export class ASRouterUISurface extends React.PureComponent {
|
|||
ReactDOM.createPortal(
|
||||
<>
|
||||
{this.renderPreviewBanner()}
|
||||
{this.renderTrailhead()}
|
||||
{this.renderFirstRunOverlay()}
|
||||
{this.renderOnboarding()}
|
||||
{this.renderSnippets()}
|
||||
</>,
|
||||
this.portalContainer
|
||||
shouldRenderInHeader ? this.headerPortal : this.footerPortal
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,51 @@
|
|||
import React from "react";
|
||||
|
||||
export class ModalOverlay extends React.PureComponent {
|
||||
export class ModalOverlayWrapper extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
}
|
||||
|
||||
onKeyDown(event) {
|
||||
if (event.key === "Escape") {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({active: true});
|
||||
document.body.classList.add("modal-open");
|
||||
this.props.document.addEventListener("keydown", this.onKeyDown);
|
||||
this.props.document.body.classList.add("modal-open");
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.body.classList.remove("modal-open");
|
||||
this.setState({active: false});
|
||||
this.props.document.removeEventListener("keydown", this.onKeyDown);
|
||||
this.props.document.body.classList.remove("modal-open");
|
||||
}
|
||||
|
||||
render() {
|
||||
const {active} = this.state;
|
||||
const {title, button_label} = this.props;
|
||||
return (
|
||||
<div>
|
||||
<div className={`modalOverlayOuter ${active ? "active" : ""}`} />
|
||||
<div className={`modalOverlayInner ${active ? "active" : ""}`}>
|
||||
<h2> {title} </h2>
|
||||
{this.props.children}
|
||||
<div className="footer">
|
||||
<button tabIndex="2" onClick={this.props.onDoneButton} className="button primary modalButton"> {button_label} </button>
|
||||
</div>
|
||||
</div>
|
||||
const {props} = this;
|
||||
return (<React.Fragment>
|
||||
<div className="modalOverlayOuter active" onClick={props.onClose} role="presentation" />
|
||||
<div className={`modalOverlayInner active ${props.innerClassName || ""}`}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
</React.Fragment>);
|
||||
}
|
||||
}
|
||||
|
||||
ModalOverlayWrapper.defaultProps = {document: global.document};
|
||||
|
||||
export class ModalOverlay extends React.PureComponent {
|
||||
render() {
|
||||
const {title, button_label} = this.props;
|
||||
return (
|
||||
<ModalOverlayWrapper onClose={this.props.onDoneButton}>
|
||||
<h2> {title} </h2>
|
||||
{this.props.children}
|
||||
<div className="footer">
|
||||
<button className="button primary modalButton"
|
||||
onClick={this.props.onDoneButton}> {button_label} </button>
|
||||
</div>
|
||||
</ModalOverlayWrapper>);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
}
|
||||
|
||||
.modalOverlayOuter {
|
||||
background: $white;
|
||||
opacity: 0.93;
|
||||
background: var(--newtab-overlay-color);
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
|
|
@ -28,6 +28,7 @@ Please note that some targeting attributes require stricter controls on the tele
|
|||
* [sync](#sync)
|
||||
* [topFrecentSites](#topfrecentsites)
|
||||
* [totalBookmarksCount](#totalbookmarkscount)
|
||||
* [trailheadCohort](#trailheadcohort)
|
||||
* [usesFirefoxSync](#usesfirefoxsync)
|
||||
* [xpinstallEnabled](#xpinstallEnabled)
|
||||
* [hasPinnedTabs](#haspinnedtabs)
|
||||
|
@ -424,6 +425,11 @@ Total number of bookmarks.
|
|||
declare const totalBookmarksCount: number;
|
||||
```
|
||||
|
||||
### `trailheadCohort`
|
||||
|
||||
(67+ only) Experiment cohort for special trailhead project
|
||||
|
||||
|
||||
### `usesFirefoxSync`
|
||||
|
||||
Does the user use Firefox sync?
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
import {ModalOverlay} from "../../components/ModalOverlay/ModalOverlay";
|
||||
import React from "react";
|
||||
|
||||
class OnboardingCard extends React.PureComponent {
|
||||
const FLUENT_FILES = [
|
||||
"branding/brand.ftl",
|
||||
"browser/branding/sync-brand.ftl",
|
||||
"browser/newtab/onboarding.ftl",
|
||||
];
|
||||
|
||||
export class OnboardingCard extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onClick = this.onClick.bind(this);
|
||||
|
@ -20,16 +26,19 @@ class OnboardingCard extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
const {content} = this.props;
|
||||
const className = this.props.className || "onboardingMessage";
|
||||
return (
|
||||
<div className="onboardingMessage">
|
||||
<div className={className}>
|
||||
<div className={`onboardingMessageImage ${content.icon}`} />
|
||||
<div className="onboardingContent">
|
||||
<span>
|
||||
<h3> {content.title} </h3>
|
||||
<p> {content.text} </p>
|
||||
<h3 className="onboardingTitle" data-l10n-id={content.title.string_id} />
|
||||
<p className="onboardingText" data-l10n-id={content.text.string_id} />
|
||||
</span>
|
||||
<span>
|
||||
<button tabIndex="1" className="button onboardingButton" onClick={this.onClick}> {content.primary_button.label} </button>
|
||||
<span className="onboardingButtonContainer">
|
||||
<button data-l10n-id={content.primary_button.label.string_id}
|
||||
className="button onboardingButton"
|
||||
onClick={this.onClick} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -38,6 +47,14 @@ class OnboardingCard extends React.PureComponent {
|
|||
}
|
||||
|
||||
export class OnboardingMessage extends React.PureComponent {
|
||||
componentWillMount() {
|
||||
FLUENT_FILES.forEach(file => {
|
||||
const link = document.head.appendChild(document.createElement("link"));
|
||||
link.href = file;
|
||||
link.rel = "localization";
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {props} = this;
|
||||
const {button_label, header} = props.extraTemplateStrings;
|
||||
|
|
|
@ -55,43 +55,6 @@
|
|||
height: 250px;
|
||||
}
|
||||
|
||||
.onboardingMessageImage {
|
||||
height: 100px;
|
||||
width: 120px;
|
||||
background-size: 120px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
|
||||
@media(max-width: 850px) {
|
||||
height: 75px;
|
||||
min-width: 80px;
|
||||
background-size: 80px;
|
||||
}
|
||||
|
||||
&.addons {
|
||||
background-image: url('resource://activity-stream/data/content/assets/illustration-addons@2x.png');
|
||||
}
|
||||
|
||||
&.privatebrowsing {
|
||||
background-image: url('resource://activity-stream/data/content/assets/illustration-privatebrowsing@2x.png');
|
||||
}
|
||||
|
||||
&.screenshots {
|
||||
background-image: url('resource://activity-stream/data/content/assets/illustration-screenshots@2x.png');
|
||||
}
|
||||
|
||||
&.gift {
|
||||
background-image: url('resource://activity-stream/data/content/assets/illustration-gift@2x.png');
|
||||
}
|
||||
|
||||
&.sync {
|
||||
background-image: url('resource://activity-stream/data/content/assets/illustration-sync@2x.png');
|
||||
}
|
||||
}
|
||||
|
||||
.onboardingContent {
|
||||
height: 175px;
|
||||
|
||||
|
@ -164,3 +127,84 @@
|
|||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Also used for Trailhead
|
||||
.onboardingMessageImage {
|
||||
height: 100px;
|
||||
width: 120px;
|
||||
background-size: 120px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
@media(max-width: 850px) {
|
||||
height: 75px;
|
||||
min-width: 80px;
|
||||
background-size: 80px;
|
||||
}
|
||||
|
||||
&.addons {
|
||||
background-image: url('#{$image-path}illustration-addons@2x.png');
|
||||
}
|
||||
|
||||
&.privatebrowsing {
|
||||
background-image: url('#{$image-path}illustration-privatebrowsing@2x.png');
|
||||
}
|
||||
|
||||
&.screenshots {
|
||||
background-image: url('#{$image-path}illustration-screenshots@2x.png');
|
||||
}
|
||||
|
||||
&.gift {
|
||||
background-image: url('#{$image-path}illustration-gift@2x.png');
|
||||
}
|
||||
|
||||
&.sync {
|
||||
background-image: url('#{$image-path}illustration-sync@2x.png');
|
||||
}
|
||||
|
||||
&.devices {
|
||||
background-image: url('#{$image-path}trailhead/card-illo-devices.png');
|
||||
}
|
||||
|
||||
&.fbcont {
|
||||
background-image: url('#{$image-path}trailhead/card-illo-fbcont.png');
|
||||
}
|
||||
|
||||
&.ffmonitor {
|
||||
background-image: url('#{$image-path}trailhead/card-illo-ffmonitor.png');
|
||||
}
|
||||
|
||||
&.ffsend {
|
||||
background-image: url('#{$image-path}trailhead/card-illo-ffsend.png');
|
||||
}
|
||||
|
||||
&.lockwise {
|
||||
background-image: url('#{$image-path}trailhead/card-illo-lockwise.png');
|
||||
}
|
||||
|
||||
&.mobile {
|
||||
background-image: url('#{$image-path}trailhead/card-illo-mobile.png');
|
||||
}
|
||||
|
||||
&.pledge {
|
||||
background-image: url('#{$image-path}trailhead/card-illo-pledge.png');
|
||||
}
|
||||
|
||||
&.pocket {
|
||||
background-image: url('#{$image-path}trailhead/card-illo-pocket.png');
|
||||
}
|
||||
|
||||
&.private {
|
||||
background-image: url('#{$image-path}trailhead/card-illo-private.png');
|
||||
}
|
||||
|
||||
&.sendtab {
|
||||
background-image: url('#{$image-path}trailhead/card-illo-sendtab.png');
|
||||
}
|
||||
|
||||
&.tracking {
|
||||
background-image: url('#{$image-path}trailhead/card-illo-tracking.png');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
|
||||
import {ModalOverlayWrapper} from "../../components/ModalOverlay/ModalOverlay";
|
||||
import {OnboardingCard} from "../OnboardingMessage/OnboardingMessage";
|
||||
import React from "react";
|
||||
|
||||
const FLUENT_FILES = [
|
||||
"branding/brand.ftl",
|
||||
"browser/branding/sync-brand.ftl",
|
||||
// These are finalized strings exposed to localizers
|
||||
"browser/newtab/onboarding.ftl",
|
||||
// These are WIP/in-development strings that only get used if the string
|
||||
// doesn't already exist in onboarding.ftl above
|
||||
"trailhead.ftl",
|
||||
];
|
||||
|
||||
export class Trailhead extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.closeModal = this.closeModal.bind(this);
|
||||
this.onInputChange = this.onInputChange.bind(this);
|
||||
this.onSubmit = this.onSubmit.bind(this);
|
||||
this.onInputInvalid = this.onInputInvalid.bind(this);
|
||||
|
||||
this.state = {
|
||||
emailInput: "",
|
||||
isModalOpen: true,
|
||||
flowId: "",
|
||||
flowBeginTime: 0,
|
||||
};
|
||||
this.didFetch = false;
|
||||
}
|
||||
|
||||
async componentWillMount() {
|
||||
FLUENT_FILES.forEach(file => {
|
||||
const link = document.head.appendChild(document.createElement("link"));
|
||||
link.href = file;
|
||||
link.rel = "localization";
|
||||
});
|
||||
|
||||
if (this.props.fxaEndpoint && !this.didFetch) {
|
||||
try {
|
||||
this.didFetch = true;
|
||||
const fxaParams = "entrypoint=activity-stream-firstrun&utm_source=activity-stream&utm_campaign=firstrun&utm_term=trailhead&form_type=email";
|
||||
const response = await fetch(`${this.props.fxaEndpoint}/metrics-flow?${fxaParams}`, {credentials: "omit"});
|
||||
if (response.status === 200) {
|
||||
const {flowId, flowBeginTime} = await response.json();
|
||||
this.setState({flowId, flowBeginTime});
|
||||
} else {
|
||||
this.props.dispatch(ac.OnlyToMain({type: at.TELEMETRY_UNDESIRED_EVENT, data: {event: "FXA_METRICS_FETCH_ERROR", value: response.status}}));
|
||||
}
|
||||
} catch (error) {
|
||||
this.props.dispatch(ac.OnlyToMain({type: at.TELEMETRY_UNDESIRED_EVENT, data: {event: "FXA_METRICS_ERROR"}}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// We need to remove hide-main since we should show it underneath everything that has rendered
|
||||
global.document.body.classList.remove("hide-main");
|
||||
|
||||
// Add inline-onboarding class to disable fixed search header and fixed positioned settings icon
|
||||
global.document.body.classList.add("inline-onboarding");
|
||||
}
|
||||
|
||||
componentDidUnmount() {
|
||||
global.document.body.classList.remove("inline-onboarding");
|
||||
}
|
||||
|
||||
onInputChange(e) {
|
||||
let error = e.target.previousSibling;
|
||||
this.setState({emailInput: e.target.value});
|
||||
error.classList.remove("active");
|
||||
e.target.classList.remove("invalid");
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.props.dispatch(ac.UserEvent({event: "SUBMIT_EMAIL", ...this._getFormInfo()}));
|
||||
|
||||
global.addEventListener("visibilitychange", this.closeModal);
|
||||
}
|
||||
|
||||
closeModal() {
|
||||
global.removeEventListener("visibilitychange", this.closeModal);
|
||||
global.document.body.classList.remove("welcome");
|
||||
this.setState({isModalOpen: false});
|
||||
this.props.dispatch(ac.UserEvent({event: "SKIPPED_SIGNIN", ...this._getFormInfo()}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Report to telemetry additional information about the form submission.
|
||||
*/
|
||||
_getFormInfo() {
|
||||
const value = {has_flow_params: this.state.flowId.length > 0};
|
||||
return {value};
|
||||
}
|
||||
|
||||
onInputInvalid(e) {
|
||||
let error = e.target.previousSibling;
|
||||
error.classList.add("active");
|
||||
e.target.classList.add("invalid");
|
||||
e.preventDefault(); // Override built-in form validation popup
|
||||
e.target.focus();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {props} = this;
|
||||
const {bundle: cards, content} = props.message;
|
||||
return (<>
|
||||
{this.state.isModalOpen && content ? <ModalOverlayWrapper innerClassName={`trailhead ${content.className}`} onClose={this.closeModal}>
|
||||
<div className="trailheadInner">
|
||||
<div className="trailheadContent">
|
||||
<h1 data-l10n-id={content.title.string_id}>{content.title.value}</h1>
|
||||
{content.subtitle &&
|
||||
<p data-l10n-id={content.subtitle.string_id}>{content.subtitle.value}</p>
|
||||
}
|
||||
<ul className="trailheadBenefits">
|
||||
{content.benefits.map(item => (
|
||||
<li key={item.id} className={item.id}>
|
||||
<h3 data-l10n-id={item.title.string_id}>{item.title.value}</h3>
|
||||
<p data-l10n-id={item.text.string_id}>{item.text.value}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<a className="trailheadLearn" data-l10n-id={content.learn.text.string_id} href={content.learn.url}>
|
||||
{content.learn.text.value}
|
||||
</a>
|
||||
</div>
|
||||
<div className="trailheadForm">
|
||||
<h3 data-l10n-id={content.form.title.string_id}>{content.form.title.value}</h3>
|
||||
<p data-l10n-id={content.form.text.string_id}>{content.form.text.value}</p>
|
||||
<form method="get" action={this.props.fxaEndpoint} target="_blank" rel="noopener noreferrer" onSubmit={this.onSubmit}>
|
||||
<input name="service" type="hidden" value="sync" />
|
||||
<input name="action" type="hidden" value="email" />
|
||||
<input name="context" type="hidden" value="fx_desktop_v3" />
|
||||
<input name="entrypoint" type="hidden" value="activity-stream-firstrun" />
|
||||
<input name="utm_source" type="hidden" value="activity-stream" />
|
||||
<input name="utm_campaign" type="hidden" value="firstrun" />
|
||||
<input name="utm_term" type="hidden" value="trailhead" />
|
||||
<input name="flow_id" type="hidden" value={this.state.flowId} />
|
||||
<input name="flow_begin_time" type="hidden" value={this.state.flowBeginTime} />
|
||||
<p data-l10n-id="onboarding-join-form-email-error" className="error" />
|
||||
<input
|
||||
data-l10n-id={content.form.email.string_id}
|
||||
placeholder={content.form.email.placeholder}
|
||||
name="email"
|
||||
type="email"
|
||||
required="true"
|
||||
onInvalid={this.onInputInvalid}
|
||||
onChange={this.onInputChange} />
|
||||
<p className="trailheadTerms" data-l10n-id="onboarding-join-form-legal">
|
||||
<a data-l10n-name="terms"
|
||||
href="https://accounts.firefox.com/legal/terms" />
|
||||
<a data-l10n-name="privacy"
|
||||
href="https://accounts.firefox.com/legal/privacy" />
|
||||
</p>
|
||||
<button data-l10n-id={content.form.button.string_id} type="submit">
|
||||
{content.form.button.value}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button className="trailheadStart"
|
||||
data-l10n-id={content.skipButton.string_id}
|
||||
onClick={this.closeModal}>{content.skipButton.value}</button>
|
||||
</ModalOverlayWrapper> : null}
|
||||
{(cards && cards.length) ? <div className="trailheadCards">
|
||||
<div className="trailheadCardsInner">
|
||||
<h1 data-l10n-id="onboarding-welcome-header" />
|
||||
<div className="trailheadCardGrid">
|
||||
{cards.map(card => (
|
||||
<OnboardingCard key={card.id}
|
||||
className="trailheadCard"
|
||||
sendUserActionTelemetry={props.sendUserActionTelemetry}
|
||||
onAction={props.onAction}
|
||||
UISurface="TRAILHEAD"
|
||||
{...card} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div> : null}
|
||||
</>);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,337 @@
|
|||
.trailhead {
|
||||
$benefit-icon-size: 62px;
|
||||
$benefit-icon-spacing: $benefit-icon-size + 12px;
|
||||
|
||||
background: url('#{$image-path}trailhead/accounts-form-bg.jpg') bottom / cover;
|
||||
color: $white;
|
||||
height: auto;
|
||||
top: 100px;
|
||||
|
||||
@media (max-height: 700px) {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.trailheadInner {
|
||||
$content-spacing: 40px;
|
||||
|
||||
display: grid;
|
||||
grid-column-gap: $content-spacing;
|
||||
grid-template-columns: 5fr 3fr;
|
||||
padding: $content-spacing 60px;
|
||||
}
|
||||
|
||||
.trailheadContent {
|
||||
h1 {
|
||||
font-size: 36px;
|
||||
font-weight: 200;
|
||||
line-height: 46px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.trailheadLearn {
|
||||
display: block;
|
||||
margin-top: 30px;
|
||||
margin-inline-start: $benefit-icon-spacing;
|
||||
}
|
||||
}
|
||||
|
||||
&.syncCohort {
|
||||
left: calc(50% - 430px);
|
||||
width: 860px;
|
||||
|
||||
@media (max-width: 860px) {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.trailheadInner {
|
||||
grid-template-columns: 4fr 3fr;
|
||||
}
|
||||
|
||||
.trailheadContent {
|
||||
.trailheadBenefits {
|
||||
background: url('#{$image-path}sync-devices.svg');
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
height: 200px;
|
||||
margin-inline-end: 60px;
|
||||
}
|
||||
|
||||
.trailheadLearn {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trailheadBenefits {
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
background-position: left 4px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: $benefit-icon-size;
|
||||
-moz-context-properties: fill;
|
||||
fill: $blue-50;
|
||||
list-style: none;
|
||||
padding-inline-start: $benefit-icon-spacing;
|
||||
|
||||
&:dir(rtl) {
|
||||
background-position-x: right;
|
||||
}
|
||||
|
||||
&.knowledge {
|
||||
background-image: url('#{$image-path}trailhead/benefit-knowledge.png');
|
||||
}
|
||||
|
||||
&.privacy {
|
||||
background-image: url('#{$image-path}trailhead/benefit-privacy.png');
|
||||
}
|
||||
|
||||
&.products {
|
||||
background-image: url('#{$image-path}trailhead/benefit-products.png');
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: $violet-20;
|
||||
font-size: 22px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $white;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
margin: 4px 0 15px;
|
||||
margin-inline-end: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.trailheadForm {
|
||||
$logo-size: 100px;
|
||||
|
||||
background: url('#{$image-path}trailhead/firefox-logo.png') top center / $logo-size no-repeat;
|
||||
min-width: 260px;
|
||||
padding-top: $logo-size;
|
||||
text-align: center;
|
||||
|
||||
h3 {
|
||||
font-size: 36px;
|
||||
font-weight: 200;
|
||||
line-height: 46px;
|
||||
margin: 12px 0 4px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $white;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
.trailheadTerms {
|
||||
margin: 4px 30px 20px;
|
||||
|
||||
a,
|
||||
& {
|
||||
color: $white-70;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
position: relative;
|
||||
|
||||
.error.active {
|
||||
inset-inline-start: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: $white;
|
||||
color: $grey-70;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: $blue-60;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
padding: 14px;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: $trailhead-blue-60;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: dotted 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trailheadStart {
|
||||
border: 1px solid $white-50;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
margin: 0 auto 40px;
|
||||
min-width: 300px;
|
||||
padding: 14px;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: $trailhead-blue-60;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: dotted 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trailheadCards {
|
||||
background: var(--trailhead-cards-background-color);
|
||||
text-align: center;
|
||||
|
||||
h1 {
|
||||
font-size: 36px;
|
||||
font-weight: 200;
|
||||
margin: 0 0 40px;
|
||||
color: var(--trailhead-header-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.trailheadCardsInner {
|
||||
margin: auto;
|
||||
padding: 40px $section-horizontal-padding;
|
||||
|
||||
@media (min-width: $break-point-medium) {
|
||||
width: $wrapper-max-width-medium;
|
||||
}
|
||||
|
||||
@media (min-width: $break-point-large) {
|
||||
width: $wrapper-max-width-large;
|
||||
}
|
||||
|
||||
@media (min-width: $break-point-widest) {
|
||||
width: $wrapper-max-width-widest;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.trailheadCardGrid {
|
||||
display: grid;
|
||||
grid-gap: $base-gutter;
|
||||
margin: 0;
|
||||
|
||||
@media (min-width: $break-point-medium) {
|
||||
grid-template-columns: repeat(auto-fit, $card-width);
|
||||
}
|
||||
|
||||
@media (min-width: $break-point-widest) {
|
||||
grid-template-columns: repeat(auto-fit, $card-width-large);
|
||||
}
|
||||
}
|
||||
|
||||
.trailheadCard {
|
||||
position: relative;
|
||||
background: var(--newtab-card-background-color);
|
||||
border-radius: 4px;
|
||||
box-shadow: var(--newtab-card-shadow);
|
||||
|
||||
font-size: 13px;
|
||||
padding: 20px;
|
||||
|
||||
@media (min-width: $break-point-widest) {
|
||||
font-size: 15px;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.onboardingTitle {
|
||||
font-weight: normal;
|
||||
color: var(--newtab-text-primary-color);
|
||||
margin: 10px 0 4px;
|
||||
font-size: 15px;
|
||||
|
||||
@media (min-width: $break-point-widest) {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.onboardingText {
|
||||
margin: 0 0 60px;
|
||||
color: var(--newtab-text-conditional-color);
|
||||
line-height: 1.5;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
.onboardingButton {
|
||||
color: var(--newtab-text-conditional-color);
|
||||
background: var(--trailhead-card-button-background-color);
|
||||
border: 0;
|
||||
height: 30px;
|
||||
min-width: 70%;
|
||||
padding: 0 14px;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
background: var(--trailhead-card-button-background-hover-color);
|
||||
}
|
||||
}
|
||||
|
||||
.onboardingButtonContainer {
|
||||
height: 60px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.inline-onboarding {
|
||||
.outer-wrapper {
|
||||
position: relative;
|
||||
|
||||
.prefs-button {
|
||||
button {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the window is too short, we need to allow scrolling so user can get to Start Browsing button.
|
||||
@media (max-height: 700px) {
|
||||
.activity-stream.welcome.inline-onboarding {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
|
@ -72,6 +72,7 @@ export class _Base extends React.PureComponent {
|
|||
// we don't want to add them back to the Activity Stream view
|
||||
document.body.classList.contains("welcome") ? "welcome" : "",
|
||||
document.body.classList.contains("hide-main") ? "hide-main" : "",
|
||||
document.body.classList.contains("inline-onboarding") ? "inline-onboarding" : "",
|
||||
].filter(v => v).join(" ");
|
||||
global.document.body.className = bodyClassName;
|
||||
}
|
||||
|
@ -158,7 +159,7 @@ export class BaseContent extends React.PureComponent {
|
|||
</ErrorBoundary>
|
||||
</div>
|
||||
}
|
||||
<ASRouterUISurface dispatch={this.props.dispatch} />
|
||||
<ASRouterUISurface fxaEndpoint={this.props.Prefs.values.fxa_endpoint} dispatch={this.props.dispatch} />
|
||||
<div className={`body-wrapper${(initialized ? " on" : "")}`}>
|
||||
{isDiscoveryStream ? (
|
||||
<ErrorBoundary className="borderless-error">
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import {actionCreators as ac} from "common/Actions.jsm";
|
||||
import {CardGrid} from "content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid";
|
||||
import {CollapsibleSection} from "content-src/components/CollapsibleSection/CollapsibleSection";
|
||||
import {connect} from "react-redux";
|
||||
|
@ -168,12 +169,19 @@ export class _DiscoveryStreamBase extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
// Select layout render data by adding spocs and position to recommendations
|
||||
const layoutRender = selectLayoutRender(this.props.DiscoveryStream, this.props.Prefs.values, rickRollCache);
|
||||
const {layoutRender, spocsFill} = selectLayoutRender(this.props.DiscoveryStream, this.props.Prefs.values, rickRollCache);
|
||||
const {config, feeds, spocs} = this.props.DiscoveryStream;
|
||||
if (!spocs.loaded || !feeds.loaded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Send SPOCS Fill if any. Note that it should not send it again if the same
|
||||
// page gets re-rendered by state changes.
|
||||
if (spocsFill.length && !this._spocsFillSent) {
|
||||
this.props.dispatch(ac.DiscoveryStreamSpocsFill({spoc_fills: spocsFill}));
|
||||
this._spocsFillSent = true;
|
||||
}
|
||||
|
||||
// Allow rendering without extracting special components
|
||||
if (!config.collapsible) {
|
||||
return this.renderLayout(layoutRender);
|
||||
|
|
|
@ -15,6 +15,10 @@ $col4-header-font-size: 14;
|
|||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ds-card-link:focus {
|
||||
@include ds-fade-in;
|
||||
}
|
||||
|
||||
&.ds-card-grid-border {
|
||||
.ds-card:not(.placeholder) {
|
||||
@include dark-theme-only {
|
||||
|
|
|
@ -62,6 +62,10 @@ $excerpt-line-height: 20;
|
|||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
|
||||
&:focus {
|
||||
@include ds-fade-in;
|
||||
}
|
||||
}
|
||||
|
||||
.meta {
|
||||
|
|
|
@ -67,6 +67,10 @@ $card-header-in-hero-line-height: 20;
|
|||
padding-top: 16px;
|
||||
height: 100%;
|
||||
|
||||
&:focus {
|
||||
@include ds-fade-in;
|
||||
}
|
||||
|
||||
@at-root .ds-hero-no-border .ds-hero-item .wrapper {
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
|
|
|
@ -86,6 +86,10 @@ $item-line-height: 20;
|
|||
}
|
||||
}
|
||||
|
||||
.ds-list-item-link:focus {
|
||||
@include ds-fade-in;
|
||||
}
|
||||
|
||||
.ds-list-numbers {
|
||||
$counter-whitespace: ($item-line-height - $item-font-size) * 1px;
|
||||
$counter-size: 32px;
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
|
||||
.top-site-outer {
|
||||
padding: 0 12px;
|
||||
|
||||
.top-site-inner > a:-moz-any(.active, :focus) .tile {
|
||||
@include ds-fade-in;
|
||||
}
|
||||
}
|
||||
|
||||
.top-sites-list {
|
||||
|
|
|
@ -246,7 +246,7 @@ $glyph-forward: url('chrome://browser/skin/forward.svg');
|
|||
}
|
||||
|
||||
@media (min-height: 701px) {
|
||||
.fixed-search {
|
||||
body:not(.inline-onboarding) .fixed-search {
|
||||
main {
|
||||
padding-top: 146px;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ export const selectLayoutRender = (state, prefs, rickRollCache) => {
|
|||
const {layout, feeds, spocs} = state;
|
||||
let spocIndex = 0;
|
||||
let bufferRollCache = [];
|
||||
// Records the chosen and unchosen spocs by the probability selection.
|
||||
let chosenSpocs = new Set();
|
||||
let unchosenSpocs = new Set();
|
||||
|
||||
// rickRollCache stores random probability values for each spoc position. This cache is empty
|
||||
// on page refresh and gets filled with random values on first render inside maybeInjectSpocs.
|
||||
|
@ -13,6 +16,11 @@ export const selectLayoutRender = (state, prefs, rickRollCache) => {
|
|||
spocs.data.spocs && spocs.data.spocs.length) {
|
||||
const recommendations = [...data.recommendations];
|
||||
for (let position of spocsConfig.positions) {
|
||||
const spoc = spocs.data.spocs[spocIndex];
|
||||
if (!spoc) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Cache random number for a position
|
||||
let rickRoll;
|
||||
if (isFirstRun) {
|
||||
|
@ -23,8 +31,12 @@ export const selectLayoutRender = (state, prefs, rickRollCache) => {
|
|||
bufferRollCache.push(rickRoll);
|
||||
}
|
||||
|
||||
if (spocs.data.spocs[spocIndex] && rickRoll <= spocsConfig.probability) {
|
||||
recommendations.splice(position.index, 0, spocs.data.spocs[spocIndex++]);
|
||||
if (rickRoll <= spocsConfig.probability) {
|
||||
spocIndex++;
|
||||
recommendations.splice(position.index, 0, spoc);
|
||||
chosenSpocs.add(spoc);
|
||||
} else {
|
||||
unchosenSpocs.add(spoc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +63,7 @@ export const selectLayoutRender = (state, prefs, rickRollCache) => {
|
|||
filterArray.push(...DS_COMPONENTS);
|
||||
}
|
||||
|
||||
return layout.map(row => ({
|
||||
const layoutRender = layout.map(row => ({
|
||||
...row,
|
||||
|
||||
// Loops through desired components and adds a .data property
|
||||
|
@ -94,4 +106,28 @@ export const selectLayoutRender = (state, prefs, rickRollCache) => {
|
|||
return {...component, data};
|
||||
}),
|
||||
})).filter(row => row.components.length);
|
||||
|
||||
// Generate the payload for the SPOCS Fill ping. Note that a SPOC could be rejected
|
||||
// by the `probability_selection` first, then gets chosen for the next position. For
|
||||
// all other SPOCS that never went through the probabilistic selection, its reason will
|
||||
// be "out_of_position".
|
||||
let spocsFill = [];
|
||||
if (spocs.data && spocs.data.spocs) {
|
||||
const chosenSpocsFill = [...chosenSpocs]
|
||||
.map(spoc => ({id: spoc.id, reason: "n/a", displayed: 1, full_recalc: 0}));
|
||||
const unchosenSpocsFill = [...unchosenSpocs]
|
||||
.filter(spoc => !chosenSpocs.has(spoc))
|
||||
.map(spoc => ({id: spoc.id, reason: "probability_selection", displayed: 0, full_recalc: 0}));
|
||||
const outOfPositionSpocsFill = spocs.data.spocs.slice(spocIndex)
|
||||
.filter(spoc => !unchosenSpocs.has(spoc))
|
||||
.map(spoc => ({id: spoc.id, reason: "out_of_position", displayed: 0, full_recalc: 0}));
|
||||
|
||||
spocsFill = [
|
||||
...chosenSpocsFill,
|
||||
...unchosenSpocsFill,
|
||||
...outOfPositionSpocsFill,
|
||||
];
|
||||
}
|
||||
|
||||
return {spocsFill, layoutRender};
|
||||
};
|
||||
|
|
|
@ -170,3 +170,4 @@ input {
|
|||
@import '../asrouter/templates/OnboardingMessage/OnboardingMessage';
|
||||
@import '../asrouter/templates/EOYSnippet/EOYSnippet';
|
||||
@import '../asrouter/templates/StartupOverlay/StartupOverlay';
|
||||
@import '../asrouter/templates/Trailhead/Trailhead';
|
||||
|
|
|
@ -41,3 +41,10 @@
|
|||
|
||||
border-bottom: 1px solid $grey-30;
|
||||
}
|
||||
|
||||
@mixin ds-fade-in {
|
||||
box-shadow: 0 0 0 1px $blue-50 inset, 0 0 0 1px $blue-50, 0 0 0 5px $blue-50-30;
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
}
|
||||
|
|
|
@ -78,6 +78,12 @@ body {
|
|||
--newtab-snippets-background-color: #{$white};
|
||||
--newtab-snippets-hairline-color: transparent;
|
||||
|
||||
// Trailhead
|
||||
--trailhead-header-text-color: #{$trailhead-purple};
|
||||
--trailhead-cards-background-color: #{$grey-20};
|
||||
--trailhead-card-button-background-color: #{$grey-90-10};
|
||||
--trailhead-card-button-background-hover-color: #{$grey-90-20};
|
||||
|
||||
&[lwt-newtab-brighttext] {
|
||||
// General styles
|
||||
--newtab-background-color: #{$grey-80};
|
||||
|
@ -136,5 +142,11 @@ body {
|
|||
// Snippets
|
||||
--newtab-snippets-background-color: #{$grey-70};
|
||||
--newtab-snippets-hairline-color: #{$white-10};
|
||||
|
||||
// Trailhead
|
||||
--trailhead-header-text-color: #{$white-60};
|
||||
--trailhead-cards-background-color: #{$grey-90-10};
|
||||
--trailhead-card-button-background-color: #{$grey-90-30};
|
||||
--trailhead-card-button-background-hover-color: #{$grey-90-40};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ $teal-70: #008EA4;
|
|||
$teal-80: #005A71;
|
||||
$red-60: #D70022;
|
||||
$yellow-50: #FFE900;
|
||||
$violet-20: #CB9EFF;
|
||||
|
||||
// Photon opacity from http://design.firefox.com/photon/visuals/color.html#opacity
|
||||
$grey-10-10: rgba($grey-10, 0.1);
|
||||
|
@ -45,6 +46,8 @@ $grey-90-70: rgba($grey-90, 0.7);
|
|||
$grey-90-80: rgba($grey-90, 0.8);
|
||||
$grey-90-90: rgba($grey-90, 0.9);
|
||||
|
||||
$blue-50-30: rgba($blue-50, 0.3);
|
||||
|
||||
$black: #000;
|
||||
$black-5: rgba($black, 0.05);
|
||||
$black-10: rgba($black, 0.1);
|
||||
|
@ -57,6 +60,9 @@ $black-30: rgba($black, 0.3);
|
|||
// Other colors
|
||||
$white: #FFF;
|
||||
$white-10: rgba($white, 0.1);
|
||||
$white-50: rgba($white, 0.5);
|
||||
$white-60: rgba($white, 0.6);
|
||||
$white-70: rgba($white, 0.7);
|
||||
$pocket-teal: #50BCB6;
|
||||
$pocket-red: #EF4056;
|
||||
$shadow-10: rgba(12, 12, 13, 0.1);
|
||||
|
@ -72,6 +78,8 @@ $about-welcome-gradient: linear-gradient(to bottom, $blue-70 40%, $aw-extra-blue
|
|||
$about-welcome-extra-links: #676F7E;
|
||||
$firefox-wordmark-default-color: #363959;
|
||||
$firefox-wordmark-darktheme-color: $white;
|
||||
$trailhead-purple: #2B2156;
|
||||
$trailhead-blue-60: #0250BB;
|
||||
|
||||
// Photon transitions from http://design.firefox.com/photon/motion/duration-and-easing.html
|
||||
$photon-easing: cubic-bezier(0.07, 0.95, 0, 1);
|
||||
|
|
|
@ -67,7 +67,11 @@ body {
|
|||
--newtab-card-placeholder-color: #D7D7DB;
|
||||
--newtab-card-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
|
||||
--newtab-snippets-background-color: #FFF;
|
||||
--newtab-snippets-hairline-color: transparent; }
|
||||
--newtab-snippets-hairline-color: transparent;
|
||||
--trailhead-header-text-color: #2B2156;
|
||||
--trailhead-cards-background-color: #EDEDF0;
|
||||
--trailhead-card-button-background-color: rgba(12, 12, 13, 0.1);
|
||||
--trailhead-card-button-background-hover-color: rgba(12, 12, 13, 0.2); }
|
||||
body[lwt-newtab-brighttext] {
|
||||
--newtab-background-color: #2A2A2E;
|
||||
--newtab-border-primary-color: rgba(249, 249, 250, 0.8);
|
||||
|
@ -111,7 +115,11 @@ body {
|
|||
--newtab-card-placeholder-color: #4A4A4F;
|
||||
--newtab-card-shadow: 0 1px 8px 0 rgba(12, 12, 13, 0.2);
|
||||
--newtab-snippets-background-color: #38383D;
|
||||
--newtab-snippets-hairline-color: rgba(255, 255, 255, 0.1); }
|
||||
--newtab-snippets-hairline-color: rgba(255, 255, 255, 0.1);
|
||||
--trailhead-header-text-color: rgba(255, 255, 255, 0.6);
|
||||
--trailhead-cards-background-color: rgba(12, 12, 13, 0.1);
|
||||
--trailhead-card-button-background-color: rgba(12, 12, 13, 0.3);
|
||||
--trailhead-card-button-background-hover-color: rgba(12, 12, 13, 0.4); }
|
||||
|
||||
.icon {
|
||||
background-position: center center;
|
||||
|
@ -1139,9 +1147,9 @@ main {
|
|||
visibility: hidden; } }
|
||||
|
||||
@media (min-height: 701px) {
|
||||
.fixed-search main {
|
||||
body:not(.inline-onboarding) .fixed-search main {
|
||||
padding-top: 146px; }
|
||||
.fixed-search .search-wrapper {
|
||||
body:not(.inline-onboarding) .fixed-search .search-wrapper {
|
||||
background-color: var(--newtab-search-header-background-color);
|
||||
border-bottom: solid 1px var(--newtab-border-secondary-color);
|
||||
height: 95px;
|
||||
|
@ -1151,19 +1159,19 @@ main {
|
|||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 9; }
|
||||
.fixed-search .search-wrapper .search-inner-wrapper {
|
||||
body:not(.inline-onboarding) .fixed-search .search-wrapper .search-inner-wrapper {
|
||||
height: 35px; }
|
||||
.fixed-search .search-wrapper input {
|
||||
body:not(.inline-onboarding) .fixed-search .search-wrapper input {
|
||||
background-position-x: 16px;
|
||||
background-size: 16px; }
|
||||
.fixed-search .search-wrapper input:dir(rtl) {
|
||||
body:not(.inline-onboarding) .fixed-search .search-wrapper input:dir(rtl) {
|
||||
background-position-x: right 16px; }
|
||||
.fixed-search .search-handoff-button {
|
||||
body:not(.inline-onboarding) .fixed-search .search-handoff-button {
|
||||
background-position-x: 12px;
|
||||
background-size: 24px; }
|
||||
.fixed-search .search-handoff-button:dir(rtl) {
|
||||
body:not(.inline-onboarding) .fixed-search .search-handoff-button:dir(rtl) {
|
||||
background-position-x: right 12px; }
|
||||
.fixed-search .search-handoff-button .fake-caret {
|
||||
body:not(.inline-onboarding) .fixed-search .search-handoff-button .fake-caret {
|
||||
top: 10px; } }
|
||||
|
||||
.contentSearchSuggestionTable {
|
||||
|
@ -1876,6 +1884,11 @@ main {
|
|||
border-radius: 4px; }
|
||||
[lwt-newtab-brighttext] .ds-card-grid .ds-card {
|
||||
background: none; }
|
||||
.ds-card-grid .ds-card-link:focus {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
.ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
|
||||
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); }
|
||||
[lwt-newtab-brighttext] .ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
|
||||
|
@ -1969,6 +1982,11 @@ main {
|
|||
border-top: 1px solid #4A4A4F; }
|
||||
[lwt-newtab-brighttext] .ds-hero .wrapper {
|
||||
color: #D7D7DB; }
|
||||
.ds-hero .wrapper:focus {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
.ds-hero-no-border .ds-hero-item .wrapper {
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
|
@ -2204,6 +2222,12 @@ main {
|
|||
[lwt-newtab-brighttext] .ds-list a {
|
||||
color: #F9F9FA; }
|
||||
|
||||
.ds-list-item-link:focus {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
|
||||
.ds-list-numbers .ds-list-item {
|
||||
counter-increment: list; }
|
||||
|
||||
|
@ -2403,6 +2427,11 @@ main {
|
|||
margin: 0 -25px; }
|
||||
.ds-top-sites .top-sites .top-site-outer {
|
||||
padding: 0 12px; }
|
||||
.ds-top-sites .top-sites .top-site-outer .top-site-inner > a:-moz-any(.active, :focus) .tile {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
.ds-top-sites .top-sites .top-sites-list {
|
||||
margin: 0 -12px; }
|
||||
|
||||
|
@ -2572,6 +2601,11 @@ main {
|
|||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 100%; }
|
||||
.ds-card .ds-card-link:focus {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
.ds-card .meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -2799,8 +2833,7 @@ main {
|
|||
overflow: hidden; }
|
||||
|
||||
.modalOverlayOuter {
|
||||
background: #FFF;
|
||||
opacity: 0.93;
|
||||
background: var(--newtab-overlay-color);
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
@ -3264,29 +3297,6 @@ main {
|
|||
@media (max-width: 650px) {
|
||||
.onboardingMessage {
|
||||
height: 250px; } }
|
||||
.onboardingMessage .onboardingMessageImage {
|
||||
height: 100px;
|
||||
width: 120px;
|
||||
background-size: 120px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
vertical-align: middle; }
|
||||
@media (max-width: 850px) {
|
||||
.onboardingMessage .onboardingMessageImage {
|
||||
height: 75px;
|
||||
min-width: 80px;
|
||||
background-size: 80px; } }
|
||||
.onboardingMessage .onboardingMessageImage.addons {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-addons@2x.png"); }
|
||||
.onboardingMessage .onboardingMessageImage.privatebrowsing {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-privatebrowsing@2x.png"); }
|
||||
.onboardingMessage .onboardingMessageImage.screenshots {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-screenshots@2x.png"); }
|
||||
.onboardingMessage .onboardingMessageImage.gift {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-gift@2x.png"); }
|
||||
.onboardingMessage .onboardingMessageImage.sync {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-sync@2x.png"); }
|
||||
.onboardingMessage .onboardingContent {
|
||||
height: 175px; }
|
||||
.onboardingMessage .onboardingContent > span > h3 {
|
||||
|
@ -3337,6 +3347,52 @@ main {
|
|||
.onboardingMessage:last-child::before {
|
||||
content: none; }
|
||||
|
||||
.onboardingMessageImage {
|
||||
height: 100px;
|
||||
width: 120px;
|
||||
background-size: 120px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
vertical-align: middle; }
|
||||
@media (max-width: 850px) {
|
||||
.onboardingMessageImage {
|
||||
height: 75px;
|
||||
min-width: 80px;
|
||||
background-size: 80px; } }
|
||||
.onboardingMessageImage.addons {
|
||||
background-image: url("../data/content/assets/illustration-addons@2x.png"); }
|
||||
.onboardingMessageImage.privatebrowsing {
|
||||
background-image: url("../data/content/assets/illustration-privatebrowsing@2x.png"); }
|
||||
.onboardingMessageImage.screenshots {
|
||||
background-image: url("../data/content/assets/illustration-screenshots@2x.png"); }
|
||||
.onboardingMessageImage.gift {
|
||||
background-image: url("../data/content/assets/illustration-gift@2x.png"); }
|
||||
.onboardingMessageImage.sync {
|
||||
background-image: url("../data/content/assets/illustration-sync@2x.png"); }
|
||||
.onboardingMessageImage.devices {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-devices.png"); }
|
||||
.onboardingMessageImage.fbcont {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-fbcont.png"); }
|
||||
.onboardingMessageImage.ffmonitor {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-ffmonitor.png"); }
|
||||
.onboardingMessageImage.ffsend {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-ffsend.png"); }
|
||||
.onboardingMessageImage.lockwise {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-lockwise.png"); }
|
||||
.onboardingMessageImage.mobile {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-mobile.png"); }
|
||||
.onboardingMessageImage.pledge {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-pledge.png"); }
|
||||
.onboardingMessageImage.pocket {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-pocket.png"); }
|
||||
.onboardingMessageImage.private {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-private.png"); }
|
||||
.onboardingMessageImage.sendtab {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-sendtab.png"); }
|
||||
.onboardingMessageImage.tracking {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-tracking.png"); }
|
||||
|
||||
.EOYSnippetForm {
|
||||
margin: 10px 0 8px;
|
||||
align-self: start;
|
||||
|
@ -3631,3 +3687,224 @@ a.firstrun-link {
|
|||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0); } }
|
||||
|
||||
.trailhead {
|
||||
background: url("../data/content/assets/trailhead/accounts-form-bg.jpg") bottom/cover;
|
||||
color: #FFF;
|
||||
height: auto;
|
||||
top: 100px; }
|
||||
@media (max-height: 700px) {
|
||||
.trailhead {
|
||||
position: absolute;
|
||||
top: 20px; } }
|
||||
.trailhead a {
|
||||
color: #FFF;
|
||||
text-decoration: underline; }
|
||||
.trailhead input,
|
||||
.trailhead button {
|
||||
border-radius: 4px;
|
||||
padding: 10px; }
|
||||
.trailhead .trailheadInner {
|
||||
display: grid;
|
||||
grid-column-gap: 40px;
|
||||
grid-template-columns: 5fr 3fr;
|
||||
padding: 40px 60px; }
|
||||
.trailhead .trailheadContent h1 {
|
||||
font-size: 36px;
|
||||
font-weight: 200;
|
||||
line-height: 46px;
|
||||
margin: 0; }
|
||||
.trailhead .trailheadContent .trailheadLearn {
|
||||
display: block;
|
||||
margin-top: 30px;
|
||||
margin-inline-start: 74px; }
|
||||
.trailhead.syncCohort {
|
||||
left: calc(50% - 430px);
|
||||
width: 860px; }
|
||||
@media (max-width: 860px) {
|
||||
.trailhead.syncCohort {
|
||||
left: 0;
|
||||
width: 100%; } }
|
||||
.trailhead.syncCohort .trailheadInner {
|
||||
grid-template-columns: 4fr 3fr; }
|
||||
.trailhead.syncCohort .trailheadContent .trailheadBenefits {
|
||||
background: url("../data/content/assets/sync-devices.svg");
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
height: 200px;
|
||||
margin-inline-end: 60px; }
|
||||
.trailhead.syncCohort .trailheadContent .trailheadLearn {
|
||||
margin-inline-start: 0; }
|
||||
.trailhead .trailheadBenefits {
|
||||
padding: 0; }
|
||||
.trailhead .trailheadBenefits li {
|
||||
background-position: left 4px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 62px;
|
||||
-moz-context-properties: fill;
|
||||
fill: #0A84FF;
|
||||
list-style: none;
|
||||
padding-inline-start: 74px; }
|
||||
.trailhead .trailheadBenefits li:dir(rtl) {
|
||||
background-position-x: right; }
|
||||
.trailhead .trailheadBenefits li.knowledge {
|
||||
background-image: url("../data/content/assets/trailhead/benefit-knowledge.png"); }
|
||||
.trailhead .trailheadBenefits li.privacy {
|
||||
background-image: url("../data/content/assets/trailhead/benefit-privacy.png"); }
|
||||
.trailhead .trailheadBenefits li.products {
|
||||
background-image: url("../data/content/assets/trailhead/benefit-products.png"); }
|
||||
.trailhead .trailheadBenefits h3 {
|
||||
color: #CB9EFF;
|
||||
font-size: 22px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 4px; }
|
||||
.trailhead .trailheadBenefits p {
|
||||
color: #FFF;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
margin: 4px 0 15px;
|
||||
margin-inline-end: 60px; }
|
||||
.trailhead .trailheadForm {
|
||||
background: url("../data/content/assets/trailhead/firefox-logo.png") top center/100px no-repeat;
|
||||
min-width: 260px;
|
||||
padding-top: 100px;
|
||||
text-align: center; }
|
||||
.trailhead .trailheadForm h3 {
|
||||
font-size: 36px;
|
||||
font-weight: 200;
|
||||
line-height: 46px;
|
||||
margin: 12px 0 4px; }
|
||||
.trailhead .trailheadForm p {
|
||||
color: #FFF;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
margin: 0 0 20px; }
|
||||
.trailhead .trailheadForm .trailheadTerms {
|
||||
margin: 4px 30px 20px; }
|
||||
.trailhead .trailheadForm .trailheadTerms a, .trailhead .trailheadForm .trailheadTerms {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 12px;
|
||||
line-height: 20px; }
|
||||
.trailhead .trailheadForm form {
|
||||
position: relative; }
|
||||
.trailhead .trailheadForm form .error.active {
|
||||
inset-inline-start: 0;
|
||||
z-index: 0; }
|
||||
.trailhead .trailheadForm button,
|
||||
.trailhead .trailheadForm input {
|
||||
border: 0;
|
||||
width: 100%; }
|
||||
.trailhead .trailheadForm input {
|
||||
background-color: #FFF;
|
||||
color: #38383D;
|
||||
font-size: 15px; }
|
||||
.trailhead .trailheadForm button {
|
||||
background-color: #0060DF;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
padding: 14px; }
|
||||
.trailhead .trailheadForm button:hover, .trailhead .trailheadForm button:focus {
|
||||
background-color: #0250BB; }
|
||||
.trailhead .trailheadForm button:focus {
|
||||
outline: dotted 1px; }
|
||||
.trailhead .trailheadStart {
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
margin: 0 auto 40px;
|
||||
min-width: 300px;
|
||||
padding: 14px; }
|
||||
.trailhead .trailheadStart:hover, .trailhead .trailheadStart:focus {
|
||||
background-color: #0250BB;
|
||||
border-color: transparent; }
|
||||
.trailhead .trailheadStart:focus {
|
||||
outline: dotted 1px; }
|
||||
|
||||
.trailheadCards {
|
||||
background: var(--trailhead-cards-background-color);
|
||||
text-align: center; }
|
||||
.trailheadCards h1 {
|
||||
font-size: 36px;
|
||||
font-weight: 200;
|
||||
margin: 0 0 40px;
|
||||
color: var(--trailhead-header-text-color); }
|
||||
|
||||
.trailheadCardsInner {
|
||||
margin: auto;
|
||||
padding: 40px 25px; }
|
||||
@media (min-width: 610px) {
|
||||
.trailheadCardsInner {
|
||||
width: 530px; } }
|
||||
@media (min-width: 866px) {
|
||||
.trailheadCardsInner {
|
||||
width: 786px; } }
|
||||
@media (min-width: 1122px) {
|
||||
.trailheadCardsInner {
|
||||
width: 1042px; } }
|
||||
|
||||
.trailheadCardGrid {
|
||||
display: grid;
|
||||
grid-gap: 32px;
|
||||
margin: 0; }
|
||||
@media (min-width: 610px) {
|
||||
.trailheadCardGrid {
|
||||
grid-template-columns: repeat(auto-fit, 224px); } }
|
||||
@media (min-width: 1122px) {
|
||||
.trailheadCardGrid {
|
||||
grid-template-columns: repeat(auto-fit, 309px); } }
|
||||
|
||||
.trailheadCard {
|
||||
position: relative;
|
||||
background: var(--newtab-card-background-color);
|
||||
border-radius: 4px;
|
||||
box-shadow: var(--newtab-card-shadow);
|
||||
font-size: 13px;
|
||||
padding: 20px; }
|
||||
@media (min-width: 1122px) {
|
||||
.trailheadCard {
|
||||
font-size: 15px;
|
||||
padding: 40px; } }
|
||||
.trailheadCard .onboardingTitle {
|
||||
font-weight: normal;
|
||||
color: var(--newtab-text-primary-color);
|
||||
margin: 10px 0 4px;
|
||||
font-size: 15px; }
|
||||
@media (min-width: 1122px) {
|
||||
.trailheadCard .onboardingTitle {
|
||||
font-size: 18px; } }
|
||||
.trailheadCard .onboardingText {
|
||||
margin: 0 0 60px;
|
||||
color: var(--newtab-text-conditional-color);
|
||||
line-height: 1.5;
|
||||
font-weight: 200; }
|
||||
.trailheadCard .onboardingButton {
|
||||
color: var(--newtab-text-conditional-color);
|
||||
background: var(--trailhead-card-button-background-color);
|
||||
border: 0;
|
||||
height: 30px;
|
||||
min-width: 70%;
|
||||
padding: 0 14px; }
|
||||
.trailheadCard .onboardingButton:focus, .trailheadCard .onboardingButton:hover {
|
||||
box-shadow: none;
|
||||
background: var(--trailhead-card-button-background-hover-color); }
|
||||
.trailheadCard .onboardingButtonContainer {
|
||||
height: 60px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center; }
|
||||
|
||||
.inline-onboarding .outer-wrapper {
|
||||
position: relative; }
|
||||
.inline-onboarding .outer-wrapper .prefs-button button {
|
||||
position: absolute; }
|
||||
|
||||
@media (max-height: 700px) {
|
||||
.activity-stream.welcome.inline-onboarding {
|
||||
overflow: auto; } }
|
||||
|
|
|
@ -70,7 +70,11 @@ body {
|
|||
--newtab-card-placeholder-color: #D7D7DB;
|
||||
--newtab-card-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
|
||||
--newtab-snippets-background-color: #FFF;
|
||||
--newtab-snippets-hairline-color: transparent; }
|
||||
--newtab-snippets-hairline-color: transparent;
|
||||
--trailhead-header-text-color: #2B2156;
|
||||
--trailhead-cards-background-color: #EDEDF0;
|
||||
--trailhead-card-button-background-color: rgba(12, 12, 13, 0.1);
|
||||
--trailhead-card-button-background-hover-color: rgba(12, 12, 13, 0.2); }
|
||||
body[lwt-newtab-brighttext] {
|
||||
--newtab-background-color: #2A2A2E;
|
||||
--newtab-border-primary-color: rgba(249, 249, 250, 0.8);
|
||||
|
@ -114,7 +118,11 @@ body {
|
|||
--newtab-card-placeholder-color: #4A4A4F;
|
||||
--newtab-card-shadow: 0 1px 8px 0 rgba(12, 12, 13, 0.2);
|
||||
--newtab-snippets-background-color: #38383D;
|
||||
--newtab-snippets-hairline-color: rgba(255, 255, 255, 0.1); }
|
||||
--newtab-snippets-hairline-color: rgba(255, 255, 255, 0.1);
|
||||
--trailhead-header-text-color: rgba(255, 255, 255, 0.6);
|
||||
--trailhead-cards-background-color: rgba(12, 12, 13, 0.1);
|
||||
--trailhead-card-button-background-color: rgba(12, 12, 13, 0.3);
|
||||
--trailhead-card-button-background-hover-color: rgba(12, 12, 13, 0.4); }
|
||||
|
||||
.icon {
|
||||
background-position: center center;
|
||||
|
@ -1142,9 +1150,9 @@ main {
|
|||
visibility: hidden; } }
|
||||
|
||||
@media (min-height: 701px) {
|
||||
.fixed-search main {
|
||||
body:not(.inline-onboarding) .fixed-search main {
|
||||
padding-top: 146px; }
|
||||
.fixed-search .search-wrapper {
|
||||
body:not(.inline-onboarding) .fixed-search .search-wrapper {
|
||||
background-color: var(--newtab-search-header-background-color);
|
||||
border-bottom: solid 1px var(--newtab-border-secondary-color);
|
||||
height: 95px;
|
||||
|
@ -1154,19 +1162,19 @@ main {
|
|||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 9; }
|
||||
.fixed-search .search-wrapper .search-inner-wrapper {
|
||||
body:not(.inline-onboarding) .fixed-search .search-wrapper .search-inner-wrapper {
|
||||
height: 35px; }
|
||||
.fixed-search .search-wrapper input {
|
||||
body:not(.inline-onboarding) .fixed-search .search-wrapper input {
|
||||
background-position-x: 16px;
|
||||
background-size: 16px; }
|
||||
.fixed-search .search-wrapper input:dir(rtl) {
|
||||
body:not(.inline-onboarding) .fixed-search .search-wrapper input:dir(rtl) {
|
||||
background-position-x: right 16px; }
|
||||
.fixed-search .search-handoff-button {
|
||||
body:not(.inline-onboarding) .fixed-search .search-handoff-button {
|
||||
background-position-x: 12px;
|
||||
background-size: 24px; }
|
||||
.fixed-search .search-handoff-button:dir(rtl) {
|
||||
body:not(.inline-onboarding) .fixed-search .search-handoff-button:dir(rtl) {
|
||||
background-position-x: right 12px; }
|
||||
.fixed-search .search-handoff-button .fake-caret {
|
||||
body:not(.inline-onboarding) .fixed-search .search-handoff-button .fake-caret {
|
||||
top: 10px; } }
|
||||
|
||||
.contentSearchSuggestionTable {
|
||||
|
@ -1879,6 +1887,11 @@ main {
|
|||
border-radius: 4px; }
|
||||
[lwt-newtab-brighttext] .ds-card-grid .ds-card {
|
||||
background: none; }
|
||||
.ds-card-grid .ds-card-link:focus {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
.ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
|
||||
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); }
|
||||
[lwt-newtab-brighttext] .ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
|
||||
|
@ -1972,6 +1985,11 @@ main {
|
|||
border-top: 1px solid #4A4A4F; }
|
||||
[lwt-newtab-brighttext] .ds-hero .wrapper {
|
||||
color: #D7D7DB; }
|
||||
.ds-hero .wrapper:focus {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
.ds-hero-no-border .ds-hero-item .wrapper {
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
|
@ -2207,6 +2225,12 @@ main {
|
|||
[lwt-newtab-brighttext] .ds-list a {
|
||||
color: #F9F9FA; }
|
||||
|
||||
.ds-list-item-link:focus {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
|
||||
.ds-list-numbers .ds-list-item {
|
||||
counter-increment: list; }
|
||||
|
||||
|
@ -2406,6 +2430,11 @@ main {
|
|||
margin: 0 -25px; }
|
||||
.ds-top-sites .top-sites .top-site-outer {
|
||||
padding: 0 12px; }
|
||||
.ds-top-sites .top-sites .top-site-outer .top-site-inner > a:-moz-any(.active, :focus) .tile {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
.ds-top-sites .top-sites .top-sites-list {
|
||||
margin: 0 -12px; }
|
||||
|
||||
|
@ -2575,6 +2604,11 @@ main {
|
|||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 100%; }
|
||||
.ds-card .ds-card-link:focus {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
.ds-card .meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -2802,8 +2836,7 @@ main {
|
|||
overflow: hidden; }
|
||||
|
||||
.modalOverlayOuter {
|
||||
background: #FFF;
|
||||
opacity: 0.93;
|
||||
background: var(--newtab-overlay-color);
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
@ -3267,29 +3300,6 @@ main {
|
|||
@media (max-width: 650px) {
|
||||
.onboardingMessage {
|
||||
height: 250px; } }
|
||||
.onboardingMessage .onboardingMessageImage {
|
||||
height: 100px;
|
||||
width: 120px;
|
||||
background-size: 120px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
vertical-align: middle; }
|
||||
@media (max-width: 850px) {
|
||||
.onboardingMessage .onboardingMessageImage {
|
||||
height: 75px;
|
||||
min-width: 80px;
|
||||
background-size: 80px; } }
|
||||
.onboardingMessage .onboardingMessageImage.addons {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-addons@2x.png"); }
|
||||
.onboardingMessage .onboardingMessageImage.privatebrowsing {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-privatebrowsing@2x.png"); }
|
||||
.onboardingMessage .onboardingMessageImage.screenshots {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-screenshots@2x.png"); }
|
||||
.onboardingMessage .onboardingMessageImage.gift {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-gift@2x.png"); }
|
||||
.onboardingMessage .onboardingMessageImage.sync {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-sync@2x.png"); }
|
||||
.onboardingMessage .onboardingContent {
|
||||
height: 175px; }
|
||||
.onboardingMessage .onboardingContent > span > h3 {
|
||||
|
@ -3340,6 +3350,52 @@ main {
|
|||
.onboardingMessage:last-child::before {
|
||||
content: none; }
|
||||
|
||||
.onboardingMessageImage {
|
||||
height: 100px;
|
||||
width: 120px;
|
||||
background-size: 120px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
vertical-align: middle; }
|
||||
@media (max-width: 850px) {
|
||||
.onboardingMessageImage {
|
||||
height: 75px;
|
||||
min-width: 80px;
|
||||
background-size: 80px; } }
|
||||
.onboardingMessageImage.addons {
|
||||
background-image: url("../data/content/assets/illustration-addons@2x.png"); }
|
||||
.onboardingMessageImage.privatebrowsing {
|
||||
background-image: url("../data/content/assets/illustration-privatebrowsing@2x.png"); }
|
||||
.onboardingMessageImage.screenshots {
|
||||
background-image: url("../data/content/assets/illustration-screenshots@2x.png"); }
|
||||
.onboardingMessageImage.gift {
|
||||
background-image: url("../data/content/assets/illustration-gift@2x.png"); }
|
||||
.onboardingMessageImage.sync {
|
||||
background-image: url("../data/content/assets/illustration-sync@2x.png"); }
|
||||
.onboardingMessageImage.devices {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-devices.png"); }
|
||||
.onboardingMessageImage.fbcont {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-fbcont.png"); }
|
||||
.onboardingMessageImage.ffmonitor {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-ffmonitor.png"); }
|
||||
.onboardingMessageImage.ffsend {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-ffsend.png"); }
|
||||
.onboardingMessageImage.lockwise {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-lockwise.png"); }
|
||||
.onboardingMessageImage.mobile {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-mobile.png"); }
|
||||
.onboardingMessageImage.pledge {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-pledge.png"); }
|
||||
.onboardingMessageImage.pocket {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-pocket.png"); }
|
||||
.onboardingMessageImage.private {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-private.png"); }
|
||||
.onboardingMessageImage.sendtab {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-sendtab.png"); }
|
||||
.onboardingMessageImage.tracking {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-tracking.png"); }
|
||||
|
||||
.EOYSnippetForm {
|
||||
margin: 10px 0 8px;
|
||||
align-self: start;
|
||||
|
@ -3634,3 +3690,224 @@ a.firstrun-link {
|
|||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0); } }
|
||||
|
||||
.trailhead {
|
||||
background: url("../data/content/assets/trailhead/accounts-form-bg.jpg") bottom/cover;
|
||||
color: #FFF;
|
||||
height: auto;
|
||||
top: 100px; }
|
||||
@media (max-height: 700px) {
|
||||
.trailhead {
|
||||
position: absolute;
|
||||
top: 20px; } }
|
||||
.trailhead a {
|
||||
color: #FFF;
|
||||
text-decoration: underline; }
|
||||
.trailhead input,
|
||||
.trailhead button {
|
||||
border-radius: 4px;
|
||||
padding: 10px; }
|
||||
.trailhead .trailheadInner {
|
||||
display: grid;
|
||||
grid-column-gap: 40px;
|
||||
grid-template-columns: 5fr 3fr;
|
||||
padding: 40px 60px; }
|
||||
.trailhead .trailheadContent h1 {
|
||||
font-size: 36px;
|
||||
font-weight: 200;
|
||||
line-height: 46px;
|
||||
margin: 0; }
|
||||
.trailhead .trailheadContent .trailheadLearn {
|
||||
display: block;
|
||||
margin-top: 30px;
|
||||
margin-inline-start: 74px; }
|
||||
.trailhead.syncCohort {
|
||||
left: calc(50% - 430px);
|
||||
width: 860px; }
|
||||
@media (max-width: 860px) {
|
||||
.trailhead.syncCohort {
|
||||
left: 0;
|
||||
width: 100%; } }
|
||||
.trailhead.syncCohort .trailheadInner {
|
||||
grid-template-columns: 4fr 3fr; }
|
||||
.trailhead.syncCohort .trailheadContent .trailheadBenefits {
|
||||
background: url("../data/content/assets/sync-devices.svg");
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
height: 200px;
|
||||
margin-inline-end: 60px; }
|
||||
.trailhead.syncCohort .trailheadContent .trailheadLearn {
|
||||
margin-inline-start: 0; }
|
||||
.trailhead .trailheadBenefits {
|
||||
padding: 0; }
|
||||
.trailhead .trailheadBenefits li {
|
||||
background-position: left 4px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 62px;
|
||||
-moz-context-properties: fill;
|
||||
fill: #0A84FF;
|
||||
list-style: none;
|
||||
padding-inline-start: 74px; }
|
||||
.trailhead .trailheadBenefits li:dir(rtl) {
|
||||
background-position-x: right; }
|
||||
.trailhead .trailheadBenefits li.knowledge {
|
||||
background-image: url("../data/content/assets/trailhead/benefit-knowledge.png"); }
|
||||
.trailhead .trailheadBenefits li.privacy {
|
||||
background-image: url("../data/content/assets/trailhead/benefit-privacy.png"); }
|
||||
.trailhead .trailheadBenefits li.products {
|
||||
background-image: url("../data/content/assets/trailhead/benefit-products.png"); }
|
||||
.trailhead .trailheadBenefits h3 {
|
||||
color: #CB9EFF;
|
||||
font-size: 22px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 4px; }
|
||||
.trailhead .trailheadBenefits p {
|
||||
color: #FFF;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
margin: 4px 0 15px;
|
||||
margin-inline-end: 60px; }
|
||||
.trailhead .trailheadForm {
|
||||
background: url("../data/content/assets/trailhead/firefox-logo.png") top center/100px no-repeat;
|
||||
min-width: 260px;
|
||||
padding-top: 100px;
|
||||
text-align: center; }
|
||||
.trailhead .trailheadForm h3 {
|
||||
font-size: 36px;
|
||||
font-weight: 200;
|
||||
line-height: 46px;
|
||||
margin: 12px 0 4px; }
|
||||
.trailhead .trailheadForm p {
|
||||
color: #FFF;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
margin: 0 0 20px; }
|
||||
.trailhead .trailheadForm .trailheadTerms {
|
||||
margin: 4px 30px 20px; }
|
||||
.trailhead .trailheadForm .trailheadTerms a, .trailhead .trailheadForm .trailheadTerms {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 12px;
|
||||
line-height: 20px; }
|
||||
.trailhead .trailheadForm form {
|
||||
position: relative; }
|
||||
.trailhead .trailheadForm form .error.active {
|
||||
inset-inline-start: 0;
|
||||
z-index: 0; }
|
||||
.trailhead .trailheadForm button,
|
||||
.trailhead .trailheadForm input {
|
||||
border: 0;
|
||||
width: 100%; }
|
||||
.trailhead .trailheadForm input {
|
||||
background-color: #FFF;
|
||||
color: #38383D;
|
||||
font-size: 15px; }
|
||||
.trailhead .trailheadForm button {
|
||||
background-color: #0060DF;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
padding: 14px; }
|
||||
.trailhead .trailheadForm button:hover, .trailhead .trailheadForm button:focus {
|
||||
background-color: #0250BB; }
|
||||
.trailhead .trailheadForm button:focus {
|
||||
outline: dotted 1px; }
|
||||
.trailhead .trailheadStart {
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
margin: 0 auto 40px;
|
||||
min-width: 300px;
|
||||
padding: 14px; }
|
||||
.trailhead .trailheadStart:hover, .trailhead .trailheadStart:focus {
|
||||
background-color: #0250BB;
|
||||
border-color: transparent; }
|
||||
.trailhead .trailheadStart:focus {
|
||||
outline: dotted 1px; }
|
||||
|
||||
.trailheadCards {
|
||||
background: var(--trailhead-cards-background-color);
|
||||
text-align: center; }
|
||||
.trailheadCards h1 {
|
||||
font-size: 36px;
|
||||
font-weight: 200;
|
||||
margin: 0 0 40px;
|
||||
color: var(--trailhead-header-text-color); }
|
||||
|
||||
.trailheadCardsInner {
|
||||
margin: auto;
|
||||
padding: 40px 25px; }
|
||||
@media (min-width: 610px) {
|
||||
.trailheadCardsInner {
|
||||
width: 530px; } }
|
||||
@media (min-width: 866px) {
|
||||
.trailheadCardsInner {
|
||||
width: 786px; } }
|
||||
@media (min-width: 1122px) {
|
||||
.trailheadCardsInner {
|
||||
width: 1042px; } }
|
||||
|
||||
.trailheadCardGrid {
|
||||
display: grid;
|
||||
grid-gap: 32px;
|
||||
margin: 0; }
|
||||
@media (min-width: 610px) {
|
||||
.trailheadCardGrid {
|
||||
grid-template-columns: repeat(auto-fit, 224px); } }
|
||||
@media (min-width: 1122px) {
|
||||
.trailheadCardGrid {
|
||||
grid-template-columns: repeat(auto-fit, 309px); } }
|
||||
|
||||
.trailheadCard {
|
||||
position: relative;
|
||||
background: var(--newtab-card-background-color);
|
||||
border-radius: 4px;
|
||||
box-shadow: var(--newtab-card-shadow);
|
||||
font-size: 13px;
|
||||
padding: 20px; }
|
||||
@media (min-width: 1122px) {
|
||||
.trailheadCard {
|
||||
font-size: 15px;
|
||||
padding: 40px; } }
|
||||
.trailheadCard .onboardingTitle {
|
||||
font-weight: normal;
|
||||
color: var(--newtab-text-primary-color);
|
||||
margin: 10px 0 4px;
|
||||
font-size: 15px; }
|
||||
@media (min-width: 1122px) {
|
||||
.trailheadCard .onboardingTitle {
|
||||
font-size: 18px; } }
|
||||
.trailheadCard .onboardingText {
|
||||
margin: 0 0 60px;
|
||||
color: var(--newtab-text-conditional-color);
|
||||
line-height: 1.5;
|
||||
font-weight: 200; }
|
||||
.trailheadCard .onboardingButton {
|
||||
color: var(--newtab-text-conditional-color);
|
||||
background: var(--trailhead-card-button-background-color);
|
||||
border: 0;
|
||||
height: 30px;
|
||||
min-width: 70%;
|
||||
padding: 0 14px; }
|
||||
.trailheadCard .onboardingButton:focus, .trailheadCard .onboardingButton:hover {
|
||||
box-shadow: none;
|
||||
background: var(--trailhead-card-button-background-hover-color); }
|
||||
.trailheadCard .onboardingButtonContainer {
|
||||
height: 60px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center; }
|
||||
|
||||
.inline-onboarding .outer-wrapper {
|
||||
position: relative; }
|
||||
.inline-onboarding .outer-wrapper .prefs-button button {
|
||||
position: absolute; }
|
||||
|
||||
@media (max-height: 700px) {
|
||||
.activity-stream.welcome.inline-onboarding {
|
||||
overflow: auto; } }
|
||||
|
|
|
@ -67,7 +67,11 @@ body {
|
|||
--newtab-card-placeholder-color: #D7D7DB;
|
||||
--newtab-card-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
|
||||
--newtab-snippets-background-color: #FFF;
|
||||
--newtab-snippets-hairline-color: transparent; }
|
||||
--newtab-snippets-hairline-color: transparent;
|
||||
--trailhead-header-text-color: #2B2156;
|
||||
--trailhead-cards-background-color: #EDEDF0;
|
||||
--trailhead-card-button-background-color: rgba(12, 12, 13, 0.1);
|
||||
--trailhead-card-button-background-hover-color: rgba(12, 12, 13, 0.2); }
|
||||
body[lwt-newtab-brighttext] {
|
||||
--newtab-background-color: #2A2A2E;
|
||||
--newtab-border-primary-color: rgba(249, 249, 250, 0.8);
|
||||
|
@ -111,7 +115,11 @@ body {
|
|||
--newtab-card-placeholder-color: #4A4A4F;
|
||||
--newtab-card-shadow: 0 1px 8px 0 rgba(12, 12, 13, 0.2);
|
||||
--newtab-snippets-background-color: #38383D;
|
||||
--newtab-snippets-hairline-color: rgba(255, 255, 255, 0.1); }
|
||||
--newtab-snippets-hairline-color: rgba(255, 255, 255, 0.1);
|
||||
--trailhead-header-text-color: rgba(255, 255, 255, 0.6);
|
||||
--trailhead-cards-background-color: rgba(12, 12, 13, 0.1);
|
||||
--trailhead-card-button-background-color: rgba(12, 12, 13, 0.3);
|
||||
--trailhead-card-button-background-hover-color: rgba(12, 12, 13, 0.4); }
|
||||
|
||||
.icon {
|
||||
background-position: center center;
|
||||
|
@ -1139,9 +1147,9 @@ main {
|
|||
visibility: hidden; } }
|
||||
|
||||
@media (min-height: 701px) {
|
||||
.fixed-search main {
|
||||
body:not(.inline-onboarding) .fixed-search main {
|
||||
padding-top: 146px; }
|
||||
.fixed-search .search-wrapper {
|
||||
body:not(.inline-onboarding) .fixed-search .search-wrapper {
|
||||
background-color: var(--newtab-search-header-background-color);
|
||||
border-bottom: solid 1px var(--newtab-border-secondary-color);
|
||||
height: 95px;
|
||||
|
@ -1151,19 +1159,19 @@ main {
|
|||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 9; }
|
||||
.fixed-search .search-wrapper .search-inner-wrapper {
|
||||
body:not(.inline-onboarding) .fixed-search .search-wrapper .search-inner-wrapper {
|
||||
height: 35px; }
|
||||
.fixed-search .search-wrapper input {
|
||||
body:not(.inline-onboarding) .fixed-search .search-wrapper input {
|
||||
background-position-x: 16px;
|
||||
background-size: 16px; }
|
||||
.fixed-search .search-wrapper input:dir(rtl) {
|
||||
body:not(.inline-onboarding) .fixed-search .search-wrapper input:dir(rtl) {
|
||||
background-position-x: right 16px; }
|
||||
.fixed-search .search-handoff-button {
|
||||
body:not(.inline-onboarding) .fixed-search .search-handoff-button {
|
||||
background-position-x: 12px;
|
||||
background-size: 24px; }
|
||||
.fixed-search .search-handoff-button:dir(rtl) {
|
||||
body:not(.inline-onboarding) .fixed-search .search-handoff-button:dir(rtl) {
|
||||
background-position-x: right 12px; }
|
||||
.fixed-search .search-handoff-button .fake-caret {
|
||||
body:not(.inline-onboarding) .fixed-search .search-handoff-button .fake-caret {
|
||||
top: 10px; } }
|
||||
|
||||
.contentSearchSuggestionTable {
|
||||
|
@ -1876,6 +1884,11 @@ main {
|
|||
border-radius: 4px; }
|
||||
[lwt-newtab-brighttext] .ds-card-grid .ds-card {
|
||||
background: none; }
|
||||
.ds-card-grid .ds-card-link:focus {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
.ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
|
||||
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); }
|
||||
[lwt-newtab-brighttext] .ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
|
||||
|
@ -1969,6 +1982,11 @@ main {
|
|||
border-top: 1px solid #4A4A4F; }
|
||||
[lwt-newtab-brighttext] .ds-hero .wrapper {
|
||||
color: #D7D7DB; }
|
||||
.ds-hero .wrapper:focus {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
.ds-hero-no-border .ds-hero-item .wrapper {
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
|
@ -2204,6 +2222,12 @@ main {
|
|||
[lwt-newtab-brighttext] .ds-list a {
|
||||
color: #F9F9FA; }
|
||||
|
||||
.ds-list-item-link:focus {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
|
||||
.ds-list-numbers .ds-list-item {
|
||||
counter-increment: list; }
|
||||
|
||||
|
@ -2403,6 +2427,11 @@ main {
|
|||
margin: 0 -25px; }
|
||||
.ds-top-sites .top-sites .top-site-outer {
|
||||
padding: 0 12px; }
|
||||
.ds-top-sites .top-sites .top-site-outer .top-site-inner > a:-moz-any(.active, :focus) .tile {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
.ds-top-sites .top-sites .top-sites-list {
|
||||
margin: 0 -12px; }
|
||||
|
||||
|
@ -2572,6 +2601,11 @@ main {
|
|||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 100%; }
|
||||
.ds-card .ds-card-link:focus {
|
||||
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 5px rgba(10, 132, 255, 0.3);
|
||||
transition: box-shadow 150ms;
|
||||
border-radius: 4px;
|
||||
outline: none; }
|
||||
.ds-card .meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -2799,8 +2833,7 @@ main {
|
|||
overflow: hidden; }
|
||||
|
||||
.modalOverlayOuter {
|
||||
background: #FFF;
|
||||
opacity: 0.93;
|
||||
background: var(--newtab-overlay-color);
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
@ -3264,29 +3297,6 @@ main {
|
|||
@media (max-width: 650px) {
|
||||
.onboardingMessage {
|
||||
height: 250px; } }
|
||||
.onboardingMessage .onboardingMessageImage {
|
||||
height: 100px;
|
||||
width: 120px;
|
||||
background-size: 120px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
vertical-align: middle; }
|
||||
@media (max-width: 850px) {
|
||||
.onboardingMessage .onboardingMessageImage {
|
||||
height: 75px;
|
||||
min-width: 80px;
|
||||
background-size: 80px; } }
|
||||
.onboardingMessage .onboardingMessageImage.addons {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-addons@2x.png"); }
|
||||
.onboardingMessage .onboardingMessageImage.privatebrowsing {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-privatebrowsing@2x.png"); }
|
||||
.onboardingMessage .onboardingMessageImage.screenshots {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-screenshots@2x.png"); }
|
||||
.onboardingMessage .onboardingMessageImage.gift {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-gift@2x.png"); }
|
||||
.onboardingMessage .onboardingMessageImage.sync {
|
||||
background-image: url("resource://activity-stream/data/content/assets/illustration-sync@2x.png"); }
|
||||
.onboardingMessage .onboardingContent {
|
||||
height: 175px; }
|
||||
.onboardingMessage .onboardingContent > span > h3 {
|
||||
|
@ -3337,6 +3347,52 @@ main {
|
|||
.onboardingMessage:last-child::before {
|
||||
content: none; }
|
||||
|
||||
.onboardingMessageImage {
|
||||
height: 100px;
|
||||
width: 120px;
|
||||
background-size: 120px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
vertical-align: middle; }
|
||||
@media (max-width: 850px) {
|
||||
.onboardingMessageImage {
|
||||
height: 75px;
|
||||
min-width: 80px;
|
||||
background-size: 80px; } }
|
||||
.onboardingMessageImage.addons {
|
||||
background-image: url("../data/content/assets/illustration-addons@2x.png"); }
|
||||
.onboardingMessageImage.privatebrowsing {
|
||||
background-image: url("../data/content/assets/illustration-privatebrowsing@2x.png"); }
|
||||
.onboardingMessageImage.screenshots {
|
||||
background-image: url("../data/content/assets/illustration-screenshots@2x.png"); }
|
||||
.onboardingMessageImage.gift {
|
||||
background-image: url("../data/content/assets/illustration-gift@2x.png"); }
|
||||
.onboardingMessageImage.sync {
|
||||
background-image: url("../data/content/assets/illustration-sync@2x.png"); }
|
||||
.onboardingMessageImage.devices {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-devices.png"); }
|
||||
.onboardingMessageImage.fbcont {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-fbcont.png"); }
|
||||
.onboardingMessageImage.ffmonitor {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-ffmonitor.png"); }
|
||||
.onboardingMessageImage.ffsend {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-ffsend.png"); }
|
||||
.onboardingMessageImage.lockwise {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-lockwise.png"); }
|
||||
.onboardingMessageImage.mobile {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-mobile.png"); }
|
||||
.onboardingMessageImage.pledge {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-pledge.png"); }
|
||||
.onboardingMessageImage.pocket {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-pocket.png"); }
|
||||
.onboardingMessageImage.private {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-private.png"); }
|
||||
.onboardingMessageImage.sendtab {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-sendtab.png"); }
|
||||
.onboardingMessageImage.tracking {
|
||||
background-image: url("../data/content/assets/trailhead/card-illo-tracking.png"); }
|
||||
|
||||
.EOYSnippetForm {
|
||||
margin: 10px 0 8px;
|
||||
align-self: start;
|
||||
|
@ -3631,3 +3687,224 @@ a.firstrun-link {
|
|||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0); } }
|
||||
|
||||
.trailhead {
|
||||
background: url("../data/content/assets/trailhead/accounts-form-bg.jpg") bottom/cover;
|
||||
color: #FFF;
|
||||
height: auto;
|
||||
top: 100px; }
|
||||
@media (max-height: 700px) {
|
||||
.trailhead {
|
||||
position: absolute;
|
||||
top: 20px; } }
|
||||
.trailhead a {
|
||||
color: #FFF;
|
||||
text-decoration: underline; }
|
||||
.trailhead input,
|
||||
.trailhead button {
|
||||
border-radius: 4px;
|
||||
padding: 10px; }
|
||||
.trailhead .trailheadInner {
|
||||
display: grid;
|
||||
grid-column-gap: 40px;
|
||||
grid-template-columns: 5fr 3fr;
|
||||
padding: 40px 60px; }
|
||||
.trailhead .trailheadContent h1 {
|
||||
font-size: 36px;
|
||||
font-weight: 200;
|
||||
line-height: 46px;
|
||||
margin: 0; }
|
||||
.trailhead .trailheadContent .trailheadLearn {
|
||||
display: block;
|
||||
margin-top: 30px;
|
||||
margin-inline-start: 74px; }
|
||||
.trailhead.syncCohort {
|
||||
left: calc(50% - 430px);
|
||||
width: 860px; }
|
||||
@media (max-width: 860px) {
|
||||
.trailhead.syncCohort {
|
||||
left: 0;
|
||||
width: 100%; } }
|
||||
.trailhead.syncCohort .trailheadInner {
|
||||
grid-template-columns: 4fr 3fr; }
|
||||
.trailhead.syncCohort .trailheadContent .trailheadBenefits {
|
||||
background: url("../data/content/assets/sync-devices.svg");
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
height: 200px;
|
||||
margin-inline-end: 60px; }
|
||||
.trailhead.syncCohort .trailheadContent .trailheadLearn {
|
||||
margin-inline-start: 0; }
|
||||
.trailhead .trailheadBenefits {
|
||||
padding: 0; }
|
||||
.trailhead .trailheadBenefits li {
|
||||
background-position: left 4px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 62px;
|
||||
-moz-context-properties: fill;
|
||||
fill: #0A84FF;
|
||||
list-style: none;
|
||||
padding-inline-start: 74px; }
|
||||
.trailhead .trailheadBenefits li:dir(rtl) {
|
||||
background-position-x: right; }
|
||||
.trailhead .trailheadBenefits li.knowledge {
|
||||
background-image: url("../data/content/assets/trailhead/benefit-knowledge.png"); }
|
||||
.trailhead .trailheadBenefits li.privacy {
|
||||
background-image: url("../data/content/assets/trailhead/benefit-privacy.png"); }
|
||||
.trailhead .trailheadBenefits li.products {
|
||||
background-image: url("../data/content/assets/trailhead/benefit-products.png"); }
|
||||
.trailhead .trailheadBenefits h3 {
|
||||
color: #CB9EFF;
|
||||
font-size: 22px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 4px; }
|
||||
.trailhead .trailheadBenefits p {
|
||||
color: #FFF;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
margin: 4px 0 15px;
|
||||
margin-inline-end: 60px; }
|
||||
.trailhead .trailheadForm {
|
||||
background: url("../data/content/assets/trailhead/firefox-logo.png") top center/100px no-repeat;
|
||||
min-width: 260px;
|
||||
padding-top: 100px;
|
||||
text-align: center; }
|
||||
.trailhead .trailheadForm h3 {
|
||||
font-size: 36px;
|
||||
font-weight: 200;
|
||||
line-height: 46px;
|
||||
margin: 12px 0 4px; }
|
||||
.trailhead .trailheadForm p {
|
||||
color: #FFF;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
margin: 0 0 20px; }
|
||||
.trailhead .trailheadForm .trailheadTerms {
|
||||
margin: 4px 30px 20px; }
|
||||
.trailhead .trailheadForm .trailheadTerms a, .trailhead .trailheadForm .trailheadTerms {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 12px;
|
||||
line-height: 20px; }
|
||||
.trailhead .trailheadForm form {
|
||||
position: relative; }
|
||||
.trailhead .trailheadForm form .error.active {
|
||||
inset-inline-start: 0;
|
||||
z-index: 0; }
|
||||
.trailhead .trailheadForm button,
|
||||
.trailhead .trailheadForm input {
|
||||
border: 0;
|
||||
width: 100%; }
|
||||
.trailhead .trailheadForm input {
|
||||
background-color: #FFF;
|
||||
color: #38383D;
|
||||
font-size: 15px; }
|
||||
.trailhead .trailheadForm button {
|
||||
background-color: #0060DF;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
padding: 14px; }
|
||||
.trailhead .trailheadForm button:hover, .trailhead .trailheadForm button:focus {
|
||||
background-color: #0250BB; }
|
||||
.trailhead .trailheadForm button:focus {
|
||||
outline: dotted 1px; }
|
||||
.trailhead .trailheadStart {
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
margin: 0 auto 40px;
|
||||
min-width: 300px;
|
||||
padding: 14px; }
|
||||
.trailhead .trailheadStart:hover, .trailhead .trailheadStart:focus {
|
||||
background-color: #0250BB;
|
||||
border-color: transparent; }
|
||||
.trailhead .trailheadStart:focus {
|
||||
outline: dotted 1px; }
|
||||
|
||||
.trailheadCards {
|
||||
background: var(--trailhead-cards-background-color);
|
||||
text-align: center; }
|
||||
.trailheadCards h1 {
|
||||
font-size: 36px;
|
||||
font-weight: 200;
|
||||
margin: 0 0 40px;
|
||||
color: var(--trailhead-header-text-color); }
|
||||
|
||||
.trailheadCardsInner {
|
||||
margin: auto;
|
||||
padding: 40px 25px; }
|
||||
@media (min-width: 610px) {
|
||||
.trailheadCardsInner {
|
||||
width: 530px; } }
|
||||
@media (min-width: 866px) {
|
||||
.trailheadCardsInner {
|
||||
width: 786px; } }
|
||||
@media (min-width: 1122px) {
|
||||
.trailheadCardsInner {
|
||||
width: 1042px; } }
|
||||
|
||||
.trailheadCardGrid {
|
||||
display: grid;
|
||||
grid-gap: 32px;
|
||||
margin: 0; }
|
||||
@media (min-width: 610px) {
|
||||
.trailheadCardGrid {
|
||||
grid-template-columns: repeat(auto-fit, 224px); } }
|
||||
@media (min-width: 1122px) {
|
||||
.trailheadCardGrid {
|
||||
grid-template-columns: repeat(auto-fit, 309px); } }
|
||||
|
||||
.trailheadCard {
|
||||
position: relative;
|
||||
background: var(--newtab-card-background-color);
|
||||
border-radius: 4px;
|
||||
box-shadow: var(--newtab-card-shadow);
|
||||
font-size: 13px;
|
||||
padding: 20px; }
|
||||
@media (min-width: 1122px) {
|
||||
.trailheadCard {
|
||||
font-size: 15px;
|
||||
padding: 40px; } }
|
||||
.trailheadCard .onboardingTitle {
|
||||
font-weight: normal;
|
||||
color: var(--newtab-text-primary-color);
|
||||
margin: 10px 0 4px;
|
||||
font-size: 15px; }
|
||||
@media (min-width: 1122px) {
|
||||
.trailheadCard .onboardingTitle {
|
||||
font-size: 18px; } }
|
||||
.trailheadCard .onboardingText {
|
||||
margin: 0 0 60px;
|
||||
color: var(--newtab-text-conditional-color);
|
||||
line-height: 1.5;
|
||||
font-weight: 200; }
|
||||
.trailheadCard .onboardingButton {
|
||||
color: var(--newtab-text-conditional-color);
|
||||
background: var(--trailhead-card-button-background-color);
|
||||
border: 0;
|
||||
height: 30px;
|
||||
min-width: 70%;
|
||||
padding: 0 14px; }
|
||||
.trailheadCard .onboardingButton:focus, .trailheadCard .onboardingButton:hover {
|
||||
box-shadow: none;
|
||||
background: var(--trailhead-card-button-background-hover-color); }
|
||||
.trailheadCard .onboardingButtonContainer {
|
||||
height: 60px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center; }
|
||||
|
||||
.inline-onboarding .outer-wrapper {
|
||||
position: relative; }
|
||||
.inline-onboarding .outer-wrapper .prefs-button button {
|
||||
position: absolute; }
|
||||
|
||||
@media (max-height: 700px) {
|
||||
.activity-stream.welcome.inline-onboarding {
|
||||
overflow: auto; } }
|
||||
|
|
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/accounts-form-bg.jpg
Executable file
После Ширина: | Высота: | Размер: 23 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/benefit-knowledge.png
Executable file
После Ширина: | Высота: | Размер: 4.2 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/benefit-privacy.png
Executable file
После Ширина: | Высота: | Размер: 8.0 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/benefit-products.png
Executable file
После Ширина: | Высота: | Размер: 4.7 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/card-illo-devices.png
Executable file
После Ширина: | Высота: | Размер: 26 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/card-illo-fbcont.png
Executable file
После Ширина: | Высота: | Размер: 22 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/card-illo-ffmonitor.png
Executable file
После Ширина: | Высота: | Размер: 34 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/card-illo-ffsend.png
Executable file
После Ширина: | Высота: | Размер: 46 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/card-illo-lockwise.png
Normal file
После Ширина: | Высота: | Размер: 34 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/card-illo-mobile.png
Normal file
После Ширина: | Высота: | Размер: 61 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/card-illo-pledge.png
Executable file
После Ширина: | Высота: | Размер: 27 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/card-illo-pocket.png
Executable file
После Ширина: | Высота: | Размер: 27 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/card-illo-private.png
Executable file
После Ширина: | Высота: | Размер: 31 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/card-illo-sendtab.png
Executable file
После Ширина: | Высота: | Размер: 24 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/card-illo-tracking.png
Executable file
После Ширина: | Высота: | Размер: 34 KiB |
Двоичные данные
browser/components/newtab/data/content/assets/trailhead/firefox-logo.png
Executable file
После Ширина: | Высота: | Размер: 30 KiB |
|
@ -0,0 +1,139 @@
|
|||
# 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/.
|
||||
|
||||
## The following feature names must be treated as a brand, and kept in English.
|
||||
## They cannot be:
|
||||
## - Declined to adapt to grammatical case.
|
||||
## - Transliterated.
|
||||
## - Translated.
|
||||
|
||||
-facebook-container-brand-name = Facebook Container
|
||||
-lockwise-brand-name = Firefox Lockwise
|
||||
-monitor-brand-name = Firefox Monitor
|
||||
-pocket-brand-name = Pocket
|
||||
-send-brand-name = Firefox Send
|
||||
# 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/.
|
||||
|
||||
## UI strings for the simplified onboarding modal
|
||||
|
||||
onboarding-button-label-learn-more = Learn More
|
||||
onboarding-button-label-try-now = Try It Now
|
||||
onboarding-button-label-get-started = Get Started
|
||||
|
||||
onboarding-welcome-header = Welcome to { -brand-short-name }
|
||||
onboarding-welcome-body = You’ve got the browser.<br/>Meet the rest of { -brand-product-name }.
|
||||
onboarding-welcome-learn-more = Learn more about the benefits.
|
||||
|
||||
onboarding-join-form-header = Join { -brand-product-name }
|
||||
onboarding-join-form-body = Enter your email address to get started.
|
||||
onboarding-join-form-email =
|
||||
.placeholder = Enter email
|
||||
onboarding-join-form-email-error = Valid email required
|
||||
onboarding-join-form-legal = By proceeding, you agree to the <a data-l10n-name="terms">Terms of Service</a> and <a data-l10n-name="privacy">Privacy Notice</a>.
|
||||
onboarding-join-form-continue = Continue
|
||||
|
||||
onboarding-start-browsing-button-label = Start Browsing
|
||||
|
||||
## These are individual benefit messages shown with an image, title and
|
||||
## description.
|
||||
|
||||
onboarding-benefit-products-title = Useful Products
|
||||
onboarding-benefit-products-text = Get things done with a family of tools that respects your privacy across your devices.
|
||||
|
||||
onboarding-benefit-knowledge-title = Practical Knowledge
|
||||
onboarding-benefit-knowledge-text = Learn everything you need to know to stay smarter and safer online.
|
||||
|
||||
onboarding-benefit-privacy-title = True Privacy
|
||||
# "Personal Data Promise" should be treated as a brand and refers to a concept
|
||||
# shown elsewhere to the user: "The Firefox Personal Data Promise is the way we
|
||||
# honor your data in everything we make and do. We take less data. We keep it
|
||||
# safe. And we make sure that we are transparent about how we use it."
|
||||
onboarding-benefit-privacy-text = Everything we do honors our Personal Data Promise: Take less. Keep it safe. No secrets.
|
||||
|
||||
|
||||
## These strings belong to the individual onboarding messages.
|
||||
|
||||
## Each message has a title and a description of what the browser feature is.
|
||||
## Each message also has an associated button for the user to try the feature.
|
||||
## The string for the button is found above, in the UI strings section
|
||||
onboarding-private-browsing-title = Private Browsing
|
||||
onboarding-private-browsing-text = Browse by yourself. Private Browsing with Content Blocking blocks online trackers that follow you around the web.
|
||||
|
||||
onboarding-screenshots-title = Screenshots
|
||||
onboarding-screenshots-text = Take, save and share screenshots - without leaving { -brand-short-name }. Capture a region or an entire page as you browse. Then save to the web for easy access and sharing.
|
||||
|
||||
onboarding-addons-title = Add-ons
|
||||
onboarding-addons-text = Add even more features that make { -brand-short-name } work harder for you. Compare prices, check the weather or express your personality with a custom theme.
|
||||
|
||||
onboarding-ghostery-title = Ghostery
|
||||
onboarding-ghostery-text = Browse faster, smarter, or safer with extensions like Ghostery, which lets you block annoying ads.
|
||||
|
||||
# Note: "Sync" in this case is a generic verb, as in "to synchronize"
|
||||
onboarding-fxa-title = Sync
|
||||
onboarding-fxa-text = Sign up for a { -fxaccount-brand-name } and sync your bookmarks, passwords, and open tabs everywhere you use { -brand-short-name }.
|
||||
|
||||
onboarding-tracking-protection-title = Control How You’re Tracked
|
||||
onboarding-tracking-protection-text = Don’t like when ads follow you around? { -brand-short-name } helps you control how advertisers track your activity online.
|
||||
onboarding-tracking-protection-button = Learn More
|
||||
|
||||
onboarding-data-sync-title = Take Your Settings with You
|
||||
# "Sync" is short for synchronize.
|
||||
onboarding-data-sync-text = Sync your bookmarks and passwords everywhere you use { -brand-product-name }.
|
||||
onboarding-data-sync-button = Turn on { -sync-brand-short-name }
|
||||
|
||||
onboarding-firefox-monitor-title = Stay Alert to Data Breaches
|
||||
onboarding-firefox-monitor-text = { -monitor-brand-name } monitors if your email has appeared in a data breach and alerts you if it appears in a new breach.
|
||||
onboarding-firefox-monitor-button = Sign up for Alerts
|
||||
|
||||
onboarding-private-browsing-title = Browse Privately
|
||||
onboarding-private-browsing-text = Private Browsing clears your search and browsing history to keep it secret from anyone who uses your computer.
|
||||
onboarding-private-browsing-button = Open a Private Window
|
||||
|
||||
onboarding-firefox-send-title = Keep Your Shared Files Private
|
||||
onboarding-firefox-send-text = { -send-brand-name } protects the files you share with end-to-end encryption and a link that automatically expires.
|
||||
onboarding-firefox-send-button = Try { -send-brand-name }
|
||||
|
||||
onboarding-mobile-phone-title = Get { -brand-product-name } on Your Phone
|
||||
onboarding-mobile-phone-text = Download { -brand-product-name } for iOS or Android and sync your data across devices.
|
||||
# "Mobile" is short for mobile/cellular phone, "Browser" is short for web
|
||||
# browser.
|
||||
onboarding-mobile-phone-button = Download Mobile Browser
|
||||
|
||||
onboarding-privacy-right-title = Privacy is Your Right
|
||||
onboarding-privacy-right-text = { -brand-short-name } treats your data with respect by taking less, protecting it, and being clear about how we use it.
|
||||
onboarding-privacy-right-button = Learn More
|
||||
|
||||
onboarding-send-tabs-title = Instantly Send Yourself Tabs
|
||||
# "Send Tabs" refers to "Send Tab to Device" feature that appears when opening a
|
||||
# tab's context menu.
|
||||
onboarding-send-tabs-text = Send Tabs instantly shares pages between your devices without having to copy, paste, or leave the browser.
|
||||
onboarding-send-tabs-button = Start Using Send Tabs
|
||||
|
||||
onboarding-pocket-anywhere-title = Read and Listen Anywhere
|
||||
# "downtime" refers to the user's free/spare time.
|
||||
onboarding-pocket-anywhere-text = { -pocket-brand-name } saves your favorite stories so you can read, listen, and watch during your downtime, even if you’re offline.
|
||||
onboarding-pocket-anywhere-button = Try { -pocket-brand-name }
|
||||
|
||||
onboarding-lockwise-passwords-title = Take Your Passwords Everywhere
|
||||
# "many places" conveys that Lockwise is available outside of Firefox.
|
||||
onboarding-lockwise-passwords-text = { -lockwise-brand-name } saves your passwords in a secure place so you can easily log into your accounts.
|
||||
onboarding-lockwise-passwords-button = Get { -lockwise-brand-name }
|
||||
|
||||
onboarding-facebook-container-title = Set Boundaries with Facebook
|
||||
onboarding-facebook-container-text = { -facebook-container-brand-name } keeps your Facebook identity separate from everything else, making it harder to track you across the web.
|
||||
onboarding-facebook-container-button = Add the Extension
|
||||
|
||||
|
||||
## Message strings belonging to the Return to AMO flow
|
||||
return-to-amo-sub-header = Great, you’ve got { -brand-short-name }
|
||||
|
||||
# <icon></icon> will be replaced with the icon belonging to the extension
|
||||
#
|
||||
# Variables:
|
||||
# $addon-name (String) - Name of the add-on
|
||||
return-to-amo-addon-header = Now let’s get you <icon></icon><b>{ $addon-name }.</b>
|
||||
return-to-amo-extension-button = Add the Extension
|
||||
return-to-amo-get-started-button = Get Started with { -brand-short-name }
|
|
@ -161,6 +161,30 @@ Schema definitions/validations that can be used for tests can be found in `syste
|
|||
}
|
||||
```
|
||||
|
||||
# Example Discovery Stream `SPOCS Fill` log
|
||||
|
||||
```js
|
||||
{
|
||||
// both "client_id" and "session_id" are set to "n/a" in this ping.
|
||||
"client_id": "n/a",
|
||||
"session_id": "n/a",
|
||||
"impression_id": "{005deed0-e3e4-4c02-a041-17405fd703f6}",
|
||||
"addon_version": "20180710100040",
|
||||
"locale": "en-US",
|
||||
"version": "68",
|
||||
"release_channel": "release",
|
||||
"spoc_fills": [
|
||||
{"id": 10000, displayed: 0, reason: "frequency_cap", full_recalc: 1},
|
||||
{"id": 10001, displayed: 0, reason: "blocked_by_user", full_recalc: 1},
|
||||
{"id": 10002, displayed: 0, reason: "below_min_score", full_recalc: 1},
|
||||
{"id": 10003, displayed: 0, reason: "campaign_duplicate", full_recalc: 1},
|
||||
{"id": 10004, displayed: 0, reason: "probability_selection", full_recalc: 0},
|
||||
{"id": 10004, displayed: 0, reason: "out_of_position", full_recalc: 0},
|
||||
{"id": 10005, displayed: 1, reason: "n/a", full_recalc: 0}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# Example Activity Stream `Router` Pings
|
||||
|
||||
```js
|
||||
|
@ -237,6 +261,9 @@ and losing focus. | :one:
|
|||
| `profile_creation_date` | [Optional] An integer to record the age of the Firefox profile as the total number of days since the UNIX epoch. | :one:
|
||||
| `message_id` | [required] A string identifier of the message in Activity Stream Router. | :one:
|
||||
| `has_flow_params` | [required] One of [true, false]. A boolean identifier that indicates if Firefox Accounts flow parameters are set or unset. | :one:
|
||||
| `displayed` | [required] 1: a SPOC is displayed; 0: non-displayed | :one:
|
||||
| `reason` | [required] The reason if a SPOC is not displayed, "n/a" for the displayed, one of ("frequency_cap", "blocked_by_user", "campaign_duplicate", "probability_selection", "below_min_score", "out_of_position", "n/a") | :one:
|
||||
| `full_recalc` | [required] Is it a full SPOCS recalculation: 0: false; 1: true. Recalculation case: 1). fetch SPOCS from Pocket endpoint. Non-recalculation cases: 1). An impression updates the SPOCS; 2). Any action that triggers the `selectLayoutRender ` | :one:
|
||||
|
||||
**Where:**
|
||||
|
||||
|
|
|
@ -855,6 +855,32 @@ This reports all the loaded content (a list of `id`s and positions) when the use
|
|||
}
|
||||
```
|
||||
|
||||
### Discovery Stream SPOCS Fill ping
|
||||
|
||||
This reports the internal status of Pocket SPOCS (Sponsored Contents).
|
||||
|
||||
```js
|
||||
{
|
||||
// both "client_id" and "session_id" are set to "n/a" in this ping.
|
||||
"client_id": "n/a",
|
||||
"session_id": "n/a",
|
||||
"impression_id": "{005deed0-e3e4-4c02-a041-17405fd703f6}",
|
||||
"addon_version": "20180710100040",
|
||||
"locale": "en-US",
|
||||
"version": "68",
|
||||
"release_channel": "release",
|
||||
"spoc_fills": [
|
||||
{"id": 10000, displayed: 0, reason: "frequency_cap", full_recalc: 1},
|
||||
{"id": 10001, displayed: 0, reason: "blocked_by_user", full_recalc: 1},
|
||||
{"id": 10002, displayed: 0, reason: "below_min_score", full_recalc: 1},
|
||||
{"id": 10003, displayed: 0, reason: "campaign_duplicate", full_recalc: 1},
|
||||
{"id": 10004, displayed: 0, reason: "probability_selection", full_recalc: 0},
|
||||
{"id": 10004, displayed: 0, reason: "out_of_position", full_recalc: 0},
|
||||
{"id": 10005, displayed: 1, reason: "n/a", full_recalc: 0}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Undesired event pings
|
||||
|
||||
These pings record the undesired events happen in the addon for further investigation.
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
# 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/.
|
||||
|
||||
[localization] en-US.jar:
|
||||
trailhead.ftl (./data/trailhead.wip)
|
||||
|
||||
browser.jar:
|
||||
% resource activity-stream %res/activity-stream/ contentaccessible=yes
|
||||
res/activity-stream/lib/ (./lib/*)
|
||||
|
|
|
@ -695,18 +695,34 @@ class _ASRouter {
|
|||
}
|
||||
|
||||
async _getBundledMessages(originalMessage, target, trigger, force = false) {
|
||||
let result = [{content: originalMessage.content, id: originalMessage.id, order: originalMessage.order || 0}];
|
||||
let result = [];
|
||||
let bundleLength;
|
||||
let bundleTemplate;
|
||||
let originalId;
|
||||
|
||||
if (originalMessage.includeBundle) {
|
||||
// The original message is not part of the bundle, so don't include it
|
||||
bundleLength = originalMessage.includeBundle.length;
|
||||
bundleTemplate = originalMessage.includeBundle.template;
|
||||
} else {
|
||||
// The original message is part of the bundle
|
||||
bundleLength = originalMessage.bundled;
|
||||
bundleTemplate = originalMessage.template;
|
||||
originalId = originalMessage.id;
|
||||
// Add in a copy of the first message
|
||||
result.push({content: originalMessage.content, id: originalMessage.id, order: originalMessage.order || 0});
|
||||
}
|
||||
|
||||
// First, find all messages of same template. These are potential matching targeting candidates
|
||||
let bundledMessagesOfSameTemplate = this._getUnblockedMessages()
|
||||
.filter(msg => msg.bundled && msg.template === originalMessage.template && msg.id !== originalMessage.id);
|
||||
.filter(msg => msg.bundled && msg.template === bundleTemplate && msg.id !== originalId);
|
||||
|
||||
if (force) {
|
||||
// Forcefully show the messages without targeting matching - this is for about:newtab#asrouter to show the messages
|
||||
for (const message of bundledMessagesOfSameTemplate) {
|
||||
result.push({content: message.content, id: message.id});
|
||||
// Stop once we have enough messages to fill a bundle
|
||||
if (result.length === originalMessage.bundled) {
|
||||
if (result.length === bundleLength) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -723,14 +739,14 @@ class _ASRouter {
|
|||
result.push({content: message.content, id: message.id, order: message.order || 0});
|
||||
bundledMessagesOfSameTemplate.splice(bundledMessagesOfSameTemplate.findIndex(msg => msg.id === message.id), 1);
|
||||
// Stop once we have enough messages to fill a bundle
|
||||
if (result.length === originalMessage.bundled) {
|
||||
if (result.length === bundleLength) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we did not find enough messages to fill the bundle, do not send the bundle down
|
||||
if (result.length < originalMessage.bundled) {
|
||||
if (result.length < bundleLength) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -739,7 +755,12 @@ class _ASRouter {
|
|||
// handle finding these strings on its own. See bug 1488973
|
||||
const extraTemplateStrings = await this._extraTemplateStrings(originalMessage);
|
||||
|
||||
return {bundle: this._orderBundle(result), ...(extraTemplateStrings && {extraTemplateStrings}), provider: originalMessage.provider, template: originalMessage.template};
|
||||
return {
|
||||
bundle: this._orderBundle(result),
|
||||
...(extraTemplateStrings && {extraTemplateStrings}),
|
||||
provider: originalMessage.provider,
|
||||
template: originalMessage.template,
|
||||
};
|
||||
}
|
||||
|
||||
async _extraTemplateStrings(originalMessage) {
|
||||
|
@ -776,7 +797,16 @@ class _ASRouter {
|
|||
} else if (message.bundled) {
|
||||
const bundledMessages = await this._getBundledMessages(message, target, trigger, force);
|
||||
const action = bundledMessages ? {type: "SET_BUNDLED_MESSAGES", data: bundledMessages} : {type: "CLEAR_ALL"};
|
||||
target.sendAsyncMessage(OUTGOING_MESSAGE_NAME, action);
|
||||
try {
|
||||
target.sendAsyncMessage(OUTGOING_MESSAGE_NAME, action);
|
||||
} catch (e) {}
|
||||
|
||||
// For nested bundled messages, look for the desired bundle
|
||||
} else if (message.includeBundle) {
|
||||
const bundledMessages = await this._getBundledMessages(message, target, message.includeBundle.trigger, force);
|
||||
try {
|
||||
target.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "SET_MESSAGE", data: {...message, bundle: bundledMessages && bundledMessages.bundle}});
|
||||
} catch (e) {}
|
||||
|
||||
// CFR doorhanger
|
||||
} else if (message.template === "cfr_doorhanger") {
|
||||
|
@ -788,7 +818,9 @@ class _ASRouter {
|
|||
|
||||
// New tab single messages
|
||||
} else {
|
||||
target.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "SET_MESSAGE", data: message});
|
||||
try {
|
||||
target.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "SET_MESSAGE", data: message});
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -172,6 +172,9 @@ function sortMessagesByTargeting(messages) {
|
|||
}
|
||||
|
||||
const TargetingGetters = {
|
||||
get trailheadCohort() {
|
||||
return Services.prefs.getIntPref("trailhead.firstrun.cohort", 0);
|
||||
},
|
||||
get locale() {
|
||||
return Services.locale.appLocaleAsLangTag;
|
||||
},
|
||||
|
|
|
@ -42,6 +42,32 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
this._prefCache = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Send SPOCS Fill telemetry.
|
||||
* @param {object} filteredItems An object keyed on filter reasons, and the value
|
||||
* is a list of SPOCS.
|
||||
* @param {boolean} fullRecalc A boolean indicating if it's a full recalculation.
|
||||
* Calling `loadSpocs` will be treated as a full recalculation.
|
||||
* Whereas responding the action "DISCOVERY_STREAM_SPOC_IMPRESSION"
|
||||
* is not a full recalculation.
|
||||
*/
|
||||
_sendSpocsFill(filteredItems, fullRecalc) {
|
||||
const full_recalc = fullRecalc ? 1 : 0;
|
||||
const spocsFill = [];
|
||||
for (const [reason, items] of Object.entries(filteredItems)) {
|
||||
items.forEach(item => {
|
||||
// Only send SPOCS (i.e. it has a campaign_id)
|
||||
if (item.campaign_id) {
|
||||
spocsFill.push({reason, full_recalc, id: item.id, displayed: 0});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (spocsFill.length) {
|
||||
this.store.dispatch(ac.DiscoveryStreamSpocsFill({spoc_fills: spocsFill}));
|
||||
}
|
||||
}
|
||||
|
||||
finalLayoutEndpoint(url, apiKey) {
|
||||
if (url.includes("$apiKey") && !apiKey) {
|
||||
throw new Error(`Layout Endpoint - An API key was specified but none configured: ${url}`);
|
||||
|
@ -266,9 +292,10 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
|
||||
filterRecommendations(feed) {
|
||||
if (feed && feed.data && feed.data.recommendations && feed.data.recommendations.length) {
|
||||
const {data} = this.filterBlocked(feed.data, "recommendations");
|
||||
return {
|
||||
...feed,
|
||||
data: this.filterBlocked(feed.data, "recommendations"),
|
||||
data,
|
||||
};
|
||||
}
|
||||
return feed;
|
||||
|
@ -366,13 +393,17 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
data: {},
|
||||
};
|
||||
|
||||
let {data, filtered: frequencyCapped} = this.frequencyCapSpocs(spocs.data);
|
||||
let {data: newSpocs, filtered} = this.transform(data);
|
||||
|
||||
sendUpdate({
|
||||
type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
|
||||
data: {
|
||||
lastUpdated: spocs.lastUpdated,
|
||||
spocs: this.transform(this.frequencyCapSpocs(spocs.data)),
|
||||
spocs: newSpocs,
|
||||
},
|
||||
});
|
||||
this._sendSpocsFill({...filtered, frequency_cap: frequencyCapped}, true);
|
||||
}
|
||||
|
||||
async loadAffinityScoresCache() {
|
||||
|
@ -418,11 +449,19 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
}
|
||||
|
||||
scoreItems(items) {
|
||||
return items.map(item => this.scoreItem(item))
|
||||
const filtered = [];
|
||||
const data = items.map(item => this.scoreItem(item))
|
||||
// Remove spocs that are scored too low.
|
||||
.filter(s => s.score >= s.min_score)
|
||||
.filter(s => {
|
||||
if (s.score >= s.min_score) {
|
||||
return true;
|
||||
}
|
||||
filtered.push(s);
|
||||
return false;
|
||||
})
|
||||
// Sort by highest scores.
|
||||
.sort((a, b) => b.score - a.score);
|
||||
return {data, filtered};
|
||||
}
|
||||
|
||||
scoreItem(item) {
|
||||
|
@ -441,45 +480,67 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
}
|
||||
|
||||
filterBlocked(data, type) {
|
||||
const filtered = [];
|
||||
if (data && data[type] && data[type].length) {
|
||||
const filteredItems = data[type].filter(item => !NewTabUtils.blockedLinks.isBlocked({"url": item.url}));
|
||||
const filteredItems = data[type].filter(item => {
|
||||
const blocked = NewTabUtils.blockedLinks.isBlocked({"url": item.url});
|
||||
if (blocked) {
|
||||
filtered.push(item);
|
||||
}
|
||||
return !blocked;
|
||||
});
|
||||
return {
|
||||
...data,
|
||||
[type]: filteredItems,
|
||||
data: {
|
||||
...data,
|
||||
[type]: filteredItems,
|
||||
},
|
||||
filtered,
|
||||
};
|
||||
}
|
||||
return data;
|
||||
return {data, filtered};
|
||||
}
|
||||
|
||||
transform(spocs) {
|
||||
const data = this.filterBlocked(spocs, "spocs");
|
||||
const {data, filtered: blockedItems} = this.filterBlocked(spocs, "spocs");
|
||||
if (data && data.spocs && data.spocs.length) {
|
||||
const spocsPerDomain = this.store.getState().DiscoveryStream.spocs.spocs_per_domain || 1;
|
||||
const campaignMap = {};
|
||||
const campaignDuplicates = [];
|
||||
|
||||
// This order of operations is intended.
|
||||
// scoreItems must be first because it creates this.score.
|
||||
const {data: items, filtered: belowMinScoreItems} = this.scoreItems(data.spocs);
|
||||
// This removes campaign dupes.
|
||||
// We do this only after scoring and sorting because that way
|
||||
// we can keep the first item we see, and end up keeping the highest scored.
|
||||
const newSpocs = items.filter(s => {
|
||||
if (!campaignMap[s.campaign_id]) {
|
||||
campaignMap[s.campaign_id] = 1;
|
||||
return true;
|
||||
} else if (campaignMap[s.campaign_id] < spocsPerDomain) {
|
||||
campaignMap[s.campaign_id]++;
|
||||
return true;
|
||||
}
|
||||
campaignDuplicates.push(s);
|
||||
return false;
|
||||
});
|
||||
return {
|
||||
...data,
|
||||
// This order of operations is intended.
|
||||
// scoreItems must be first because it creates this.score.
|
||||
spocs: this.scoreItems(data.spocs)
|
||||
// This removes campaign dupes.
|
||||
// We do this only after scoring and sorting because that way
|
||||
// we can keep the first item we see, and end up keeping the highest scored.
|
||||
.filter(s => {
|
||||
if (!campaignMap[s.campaign_id]) {
|
||||
campaignMap[s.campaign_id] = 1;
|
||||
return true;
|
||||
} else if (campaignMap[s.campaign_id] < spocsPerDomain) {
|
||||
campaignMap[s.campaign_id]++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
data: {...data, spocs: newSpocs},
|
||||
filtered: {
|
||||
blocked_by_user: blockedItems,
|
||||
below_min_score: belowMinScoreItems,
|
||||
campaign_duplicate: campaignDuplicates,
|
||||
},
|
||||
};
|
||||
}
|
||||
return data;
|
||||
return {data, filtered: {blocked: blockedItems}};
|
||||
}
|
||||
|
||||
// Filter spocs based on frequency caps
|
||||
//
|
||||
// @param {Object} data An object that might have a SPOCS array.
|
||||
// @returns {Object} An object with a property `data` as the result, and a property
|
||||
// `filterItems` as the frequency capped items.
|
||||
frequencyCapSpocs(data) {
|
||||
if (data && data.spocs && data.spocs.length) {
|
||||
const {spocs} = data;
|
||||
|
@ -499,9 +560,9 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
if (caps.length) {
|
||||
this.store.dispatch({type: at.DISCOVERY_STREAM_SPOCS_CAPS, data: caps});
|
||||
}
|
||||
return result;
|
||||
return {data: result, filtered: caps};
|
||||
}
|
||||
return data;
|
||||
return {data, filtered: []};
|
||||
}
|
||||
|
||||
// Frequency caps are based on campaigns, which may include multiple spocs.
|
||||
|
@ -549,7 +610,7 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
if (this.isExpired({cachedData, key: "feed", url: feedUrl, isStartup})) {
|
||||
const feedResponse = await this.fetchFromEndpoint(feedUrl);
|
||||
if (feedResponse) {
|
||||
const scoredItems = this.scoreItems(feedResponse.recommendations);
|
||||
const {data: scoredItems} = this.scoreItems(feedResponse.recommendations);
|
||||
const {recsExpireTime} = feedResponse.settings;
|
||||
const recommendations = this.rotate(scoredItems, recsExpireTime);
|
||||
this.componentFeedFetched = true;
|
||||
|
@ -867,10 +928,8 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
// Apply frequency capping to SPOCs in the redux store, only update the
|
||||
// store if the SPOCs are changed.
|
||||
const {spocs} = this.store.getState().DiscoveryStream;
|
||||
const newSpocs = this.frequencyCapSpocs(spocs.data);
|
||||
const prevSpocs = spocs.data.spocs || [];
|
||||
const currentSpocs = newSpocs.spocs || [];
|
||||
if (prevSpocs.length !== currentSpocs.length) {
|
||||
const {data: newSpocs, filtered} = this.frequencyCapSpocs(spocs.data);
|
||||
if (filtered.length) {
|
||||
this.store.dispatch(ac.AlsoToPreloaded({
|
||||
type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
|
||||
data: {
|
||||
|
@ -878,9 +937,24 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
spocs: newSpocs,
|
||||
},
|
||||
}));
|
||||
this._sendSpocsFill({frequency_cap: filtered}, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case at.PLACES_LINK_BLOCKED:
|
||||
if (this.showSpocs) {
|
||||
const {spocs} = this.store.getState().DiscoveryStream;
|
||||
const spocsList = spocs.data.spocs || [];
|
||||
const filtered = spocsList.filter(s => s.url === action.data.url);
|
||||
if (filtered.length) {
|
||||
this._sendSpocsFill({blocked_by_user: filtered}, false);
|
||||
}
|
||||
}
|
||||
this.store.dispatch(ac.BroadcastToContent({
|
||||
type: at.DISCOVERY_STREAM_LINK_BLOCKED,
|
||||
data: action.data,
|
||||
}));
|
||||
break;
|
||||
case at.UNINIT:
|
||||
// When this feed is shutting down:
|
||||
this.uninitPrefs();
|
||||
|
|
|
@ -99,7 +99,7 @@ const ONBOARDING_MESSAGES = async () => ([
|
|||
},
|
||||
},
|
||||
},
|
||||
targeting: "attributionData.campaign != 'non-fx-button' && attributionData.source != 'addons.mozilla.org'",
|
||||
targeting: "trailheadCohort == 0 && attributionData.campaign != 'non-fx-button' && attributionData.source != 'addons.mozilla.org'",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
|
@ -119,7 +119,7 @@ const ONBOARDING_MESSAGES = async () => ([
|
|||
},
|
||||
},
|
||||
},
|
||||
targeting: "providerCohorts.onboarding == 'ghostery'",
|
||||
targeting: "trailheadCohort == 0 && providerCohorts.onboarding == 'ghostery'",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
|
@ -139,7 +139,279 @@ const ONBOARDING_MESSAGES = async () => ([
|
|||
},
|
||||
},
|
||||
},
|
||||
targeting: "attributionData.campaign == 'non-fx-button' && attributionData.source == 'addons.mozilla.org'",
|
||||
targeting: "trailheadCohort == 0 && attributionData.campaign == 'non-fx-button' && attributionData.source == 'addons.mozilla.org'",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_1",
|
||||
template: "trailhead",
|
||||
targeting: "trailheadCohort == 1",
|
||||
trigger: {id: "firstRun"},
|
||||
includeBundle: {length: 3, template: "onboarding", trigger: {id: "showOnboarding"}},
|
||||
content: {
|
||||
className: "joinCohort",
|
||||
title: {string_id: "onboarding-welcome-body"},
|
||||
benefits: ["products", "knowledge", "privacy"].map(id => (
|
||||
{
|
||||
id,
|
||||
title: {string_id: `onboarding-benefit-${id}-title`},
|
||||
text: {string_id: `onboarding-benefit-${id}-text`},
|
||||
}
|
||||
)),
|
||||
learn: {
|
||||
text: {string_id: "onboarding-welcome-learn-more"},
|
||||
url: "https://www.mozilla.org/firefox/accounts/",
|
||||
},
|
||||
form: {
|
||||
title: {string_id: "onboarding-join-form-header"},
|
||||
text: {string_id: "onboarding-join-form-body"},
|
||||
email: {string_id: "onboarding-join-form-email"},
|
||||
button: {string_id: "onboarding-join-form-continue"},
|
||||
},
|
||||
skipButton: {string_id: "onboarding-start-browsing-button-label"},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_2",
|
||||
template: "trailhead",
|
||||
targeting: "trailheadCohort == 2",
|
||||
trigger: {id: "firstRun"},
|
||||
includeBundle: {length: 3, template: "onboarding", trigger: {id: "showOnboarding"}},
|
||||
content: {
|
||||
className: "syncCohort",
|
||||
title: {value: "Take Firefox with You"},
|
||||
subtitle: {value: "Get your bookmarks, history, passwords and other settings on all your devices."},
|
||||
benefits: [],
|
||||
learn: {
|
||||
text: {string_id: "onboarding-welcome-learn-more"},
|
||||
url: "https://www.mozilla.org/firefox/accounts/",
|
||||
},
|
||||
form: {
|
||||
title: {value: "Enter your email"},
|
||||
text: {value: "to continue to Firefox Sync"},
|
||||
email: {placeholder: "Email"},
|
||||
button: {string_id: "onboarding-join-form-continue"},
|
||||
},
|
||||
skipButton: {value: "Skip this step"},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_3",
|
||||
template: "trailhead",
|
||||
targeting: "trailheadCohort == 3",
|
||||
trigger: {id: "firstRun"},
|
||||
includeBundle: {length: 3, template: "onboarding", trigger: {id: "showOnboarding"}},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_4",
|
||||
template: "trailhead",
|
||||
targeting: "trailheadCohort == 4",
|
||||
trigger: {id: "firstRun"},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_CARD_1",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
content: {
|
||||
title: {string_id: "onboarding-tracking-protection-title"},
|
||||
text: {string_id: "onboarding-tracking-protection-text"},
|
||||
icon: "tracking",
|
||||
primary_button: {
|
||||
label: {string_id: "onboarding-tracking-protection-button"},
|
||||
action: {
|
||||
type: "OPEN_PREFERENCES_PAGE",
|
||||
data: {category: "privacy-trackingprotection"},
|
||||
},
|
||||
},
|
||||
},
|
||||
targeting: "trailheadCohort > 0",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_CARD_2",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
content: {
|
||||
title: {string_id: "onboarding-data-sync-title"},
|
||||
text: {string_id: "onboarding-data-sync-text"},
|
||||
icon: "devices",
|
||||
primary_button: {
|
||||
label: {string_id: "onboarding-data-sync-button"},
|
||||
action: {
|
||||
type: "OPEN_URL",
|
||||
data: {args: "https://accounts.firefox.com/?service=sync&action=email&context=fx_desktop_v3&entrypoint=activity-stream-firstrun&utm_source=activity-stream&utm_campaign=firstrun", where: "tabshifted"},
|
||||
},
|
||||
},
|
||||
},
|
||||
targeting: "trailheadCohort > 0",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_CARD_3",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
content: {
|
||||
title: {string_id: "onboarding-firefox-monitor-title"},
|
||||
text: {string_id: "onboarding-firefox-monitor-text"},
|
||||
icon: "ffmonitor",
|
||||
primary_button: {
|
||||
label: {string_id: "onboarding-firefox-monitor-button"},
|
||||
action: {
|
||||
type: "OPEN_URL",
|
||||
data: {args: "https://monitor.firefox.com/", where: "tabshifted"},
|
||||
},
|
||||
},
|
||||
},
|
||||
targeting: "trailheadCohort > 0",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_CARD_4",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
content: {
|
||||
title: {string_id: "onboarding-private-browsing-title"},
|
||||
text: {string_id: "onboarding-private-browsing-text"},
|
||||
icon: "private",
|
||||
primary_button: {
|
||||
label: {string_id: "onboarding-private-browsing-button"},
|
||||
action: {type: "OPEN_PRIVATE_BROWSER_WINDOW"},
|
||||
},
|
||||
},
|
||||
targeting: "trailheadCohort > 0",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_CARD_5",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
content: {
|
||||
title: {string_id: "onboarding-firefox-send-title"},
|
||||
text: {string_id: "onboarding-firefox-send-text"},
|
||||
icon: "ffsend",
|
||||
primary_button: {
|
||||
label: {string_id: "onboarding-firefox-send-button"},
|
||||
action: {
|
||||
type: "OPEN_URL",
|
||||
data: {args: "https://send.firefox.com/?utm_source=activity-stream?utm_medium=referral?utm_campaign=firstrun", where: "tabshifted"},
|
||||
},
|
||||
},
|
||||
},
|
||||
targeting: "trailheadCohort > 0",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_CARD_6",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
content: {
|
||||
title: {string_id: "onboarding-mobile-phone-title"},
|
||||
text: {string_id: "onboarding-mobile-phone-text"},
|
||||
icon: "mobile",
|
||||
primary_button: {
|
||||
label: {string_id: "onboarding-mobile-phone-button"},
|
||||
action: {
|
||||
type: "OPEN_URL",
|
||||
data: {args: "https://www.mozilla.org/firefox/mobile/", where: "tabshifted"},
|
||||
},
|
||||
},
|
||||
},
|
||||
targeting: "trailheadCohort > 0",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_CARD_7",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
content: {
|
||||
title: {string_id: "onboarding-privacy-right-title"},
|
||||
text: {string_id: "onboarding-privacy-right-text"},
|
||||
icon: "pledge",
|
||||
primary_button: {
|
||||
label: {string_id: "onboarding-privacy-right-button"},
|
||||
action: {
|
||||
type: "OPEN_URL",
|
||||
data: {args: "https://www.mozilla.org/?privacy-right", where: "tabshifted"},
|
||||
},
|
||||
},
|
||||
},
|
||||
targeting: "trailheadCohort > 0",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_CARD_8",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
content: {
|
||||
title: {string_id: "onboarding-send-tabs-title"},
|
||||
text: {string_id: "onboarding-send-tabs-text"},
|
||||
icon: "sendtab",
|
||||
primary_button: {
|
||||
label: {string_id: "onboarding-send-tabs-button"},
|
||||
action: {
|
||||
type: "OPEN_URL",
|
||||
data: {args: "https://blog.mozilla.org/firefox/send-tabs-a-better-way/", where: "tabshifted"},
|
||||
},
|
||||
},
|
||||
},
|
||||
targeting: "trailheadCohort > 0",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_CARD_9",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
content: {
|
||||
title: {string_id: "onboarding-pocket-anywhere-title"},
|
||||
text: {string_id: "onboarding-pocket-anywhere-text"},
|
||||
icon: "pocket",
|
||||
primary_button: {
|
||||
label: {string_id: "onboarding-pocket-anywhere-button"},
|
||||
action: {
|
||||
type: "OPEN_URL",
|
||||
data: {args: "https://getpocket.com/firefox_learnmore", where: "tabshifted"},
|
||||
},
|
||||
},
|
||||
},
|
||||
targeting: "trailheadCohort > 0",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_CARD_10",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
content: {
|
||||
title: {string_id: "onboarding-lockwise-passwords-title"},
|
||||
text: {string_id: "onboarding-lockwise-passwords-text"},
|
||||
icon: "lockwise",
|
||||
primary_button: {
|
||||
label: {string_id: "onboarding-lockwise-passwords-button"},
|
||||
action: {
|
||||
type: "OPEN_URL",
|
||||
data: {args: "https://lockwise.firefox.com/", where: "tabshifted"},
|
||||
},
|
||||
},
|
||||
},
|
||||
targeting: "trailheadCohort > 0",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
id: "TRAILHEAD_CARD_11",
|
||||
template: "onboarding",
|
||||
bundled: 3,
|
||||
content: {
|
||||
title: {string_id: "onboarding-facebook-container-title"},
|
||||
text: {string_id: "onboarding-facebook-container-text"},
|
||||
icon: "fbcont",
|
||||
primary_button: {
|
||||
label: {string_id: "onboarding-facebook-container-button"},
|
||||
action: {
|
||||
type: "OPEN_URL",
|
||||
data: {args: "https://addons.mozilla.org/firefox/addon/facebook-container/", where: "tabshifted"},
|
||||
},
|
||||
},
|
||||
},
|
||||
targeting: "trailheadCohort > 0",
|
||||
trigger: {id: "showOnboarding"},
|
||||
},
|
||||
{
|
||||
|
@ -219,15 +491,6 @@ const OnboardingMessageProvider = {
|
|||
}
|
||||
}
|
||||
|
||||
const [primary_button_string, title_string, text_string] = await L10N.formatMessages([
|
||||
{id: msg.content.primary_button.label.string_id},
|
||||
{id: msg.content.title.string_id},
|
||||
{id: msg.content.text.string_id, args: msg.content.text.args},
|
||||
]);
|
||||
translatedMessage.content.primary_button.label = primary_button_string.value;
|
||||
translatedMessage.content.title = title_string.value;
|
||||
translatedMessage.content.text = text_string.value;
|
||||
|
||||
// Translate any secondary buttons separately
|
||||
if (msg.content.secondary_button) {
|
||||
const [secondary_button_string] = await L10N.formatMessages([{id: msg.content.secondary_button.label.string_id}]);
|
||||
|
|
|
@ -477,6 +477,18 @@ this.TelemetryFeed = class TelemetryFeed {
|
|||
);
|
||||
}
|
||||
|
||||
createSpocsFillPing(data) {
|
||||
return Object.assign(
|
||||
this.createPing(null),
|
||||
data,
|
||||
{
|
||||
impression_id: this._impressionId,
|
||||
client_id: "n/a",
|
||||
session_id: "n/a",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
createUserEvent(action) {
|
||||
return Object.assign(
|
||||
this.createPing(au.getPortIdOfSender(action)),
|
||||
|
@ -733,6 +745,9 @@ this.TelemetryFeed = class TelemetryFeed {
|
|||
case at.DISCOVERY_STREAM_LOADED_CONTENT:
|
||||
this.handleDiscoveryStreamLoadedContent(au.getPortIdOfSender(action), action.data);
|
||||
break;
|
||||
case at.DISCOVERY_STREAM_SPOCS_FILL:
|
||||
this.handleDiscoveryStreamSpocsFill(action.data);
|
||||
break;
|
||||
case at.TELEMETRY_UNDESIRED_EVENT:
|
||||
this.handleUndesiredEvent(action);
|
||||
break;
|
||||
|
@ -805,6 +820,33 @@ this.TelemetryFeed = class TelemetryFeed {
|
|||
session.loadedContentSets = loadedContentSets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handl SPOCS Fill actions from Discovery Stream.
|
||||
*
|
||||
* @param {Object} data
|
||||
* The SPOCS Fill event structured as:
|
||||
* {
|
||||
* spoc_fills: [
|
||||
* {
|
||||
* id: 123,
|
||||
* displayed: 0,
|
||||
* reason: "frequency_cap",
|
||||
* full_recalc: 1
|
||||
* },
|
||||
* {
|
||||
* id: 124,
|
||||
* displayed: 1,
|
||||
* reason: "n/a",
|
||||
* full_recalc: 1
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
handleDiscoveryStreamSpocsFill(data) {
|
||||
const payload = this.createSpocsFillPing(data);
|
||||
this.sendStructuredIngestionEvent(payload, "spocs-fills", "1");
|
||||
}
|
||||
|
||||
/**
|
||||
* Take all enumerable members of the data object and merge them into
|
||||
* the session.perf object for the given port, so that it is sent to the
|
||||
|
|
|
@ -93,6 +93,7 @@ prefs_home_header=Firefoxen hasiera-orriko edukia
|
|||
prefs_home_description=Aukeratu zein eduki nahi duzun Firefoxen hasiera-orriko pantailan.
|
||||
|
||||
prefs_content_discovery_header=Firefoxen hasiera
|
||||
|
||||
prefs_content_discovery_description=Firefoxen hasierako edukien aurkikuntzaren bidez kalitate altuko artikulu esanguratsuak aurki ditzakezu webean.
|
||||
prefs_content_discovery_button=Desgaitu edukien aurkikuntza
|
||||
|
||||
|
@ -190,7 +191,7 @@ section_menu_action_privacy_notice=Pribatutasun-oharra
|
|||
# LOCALIZATION NOTE (firstrun_*). These strings are displayed only once, on the
|
||||
# firstrun of the browser, they give an introduction to Firefox and Sync.
|
||||
firstrun_title=Eraman Firefox aldean
|
||||
firstrun_content=Izan laster-markak, historia, pasahitzak eta beste ezarpenak eskura zure gailu guztietatik.
|
||||
firstrun_content=Izan laster-markak, historia, pasahitzak eta beste ezarpenak eskura zure gailu guztietan.
|
||||
firstrun_learn_more_link=Firefox kontuei buruzko argibide gehiago
|
||||
|
||||
# LOCALIZATION NOTE (firstrun_form_header and firstrun_form_sub_header):
|
||||
|
|
|
@ -93,6 +93,8 @@ prefs_home_description=ਉਹ ਸਮੱਗਰੀ ਚੁਣੋ ਜੋ ਤੁਸ
|
|||
|
||||
prefs_content_discovery_header=ਫਾਇਰਫਾਕਸ ਮੁੱਖ ਸਫ਼ਾ
|
||||
|
||||
prefs_content_discovery_button=ਸਮੱਗਰੀ ਖੋਜ ਬੰਦ ਕਰੋ
|
||||
|
||||
# LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
|
||||
# plural forms used in a drop down of multiple row options (1 row, 2 rows).
|
||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
|
|
|
@ -20,6 +20,7 @@ cd /mozilla-central && ./mach build \
|
|||
&& ./mach test --log-tbpl test_run_log \
|
||||
browser/base/content/test/about/browser_aboutHome_search_telemetry.js \
|
||||
browser/base/content/test/static/browser_parsable_css.js \
|
||||
browser/base/content/test/tabs/browser_new_tab_in_privileged_process_pref.js \
|
||||
browser/components/enterprisepolicies/tests/browser/browser_policy_set_homepage.js \
|
||||
browser/components/preferences/in-content/tests/browser_hometab_restore_defaults.js \
|
||||
browser/components/preferences/in-content/tests/browser_newtab_menu.js \
|
||||
|
|
|
@ -708,6 +708,16 @@
|
|||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"aria-query": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
|
||||
"integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ast-types-flow": "0.0.7",
|
||||
"commander": "^2.11.0"
|
||||
}
|
||||
},
|
||||
"arr-diff": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
|
||||
|
@ -856,6 +866,12 @@
|
|||
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
|
||||
"dev": true
|
||||
},
|
||||
"ast-types-flow": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
|
||||
"integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
|
||||
"dev": true
|
||||
},
|
||||
"astral-regex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
|
||||
|
@ -913,6 +929,15 @@
|
|||
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
|
||||
"dev": true
|
||||
},
|
||||
"axobject-query": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz",
|
||||
"integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ast-types-flow": "0.0.7"
|
||||
}
|
||||
},
|
||||
"babel-code-frame": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
||||
|
@ -2222,6 +2247,12 @@
|
|||
"es5-ext": "^0.10.9"
|
||||
}
|
||||
},
|
||||
"damerau-levenshtein": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz",
|
||||
"integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=",
|
||||
"dev": true
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
|
@ -3085,6 +3116,22 @@
|
|||
"vscode-json-languageservice": "^3.2.1"
|
||||
}
|
||||
},
|
||||
"eslint-plugin-jsx-a11y": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.1.tgz",
|
||||
"integrity": "sha512-cjN2ObWrRz0TTw7vEcGQrx+YltMvZoOEx4hWU8eEERDnBIU00OTq7Vr+jA7DFKxiwLNv4tTh5Pq2GUNEa8b6+w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"aria-query": "^3.0.0",
|
||||
"array-includes": "^3.0.3",
|
||||
"ast-types-flow": "^0.0.7",
|
||||
"axobject-query": "^2.0.2",
|
||||
"damerau-levenshtein": "^1.0.4",
|
||||
"emoji-regex": "^7.0.2",
|
||||
"has": "^1.0.3",
|
||||
"jsx-ast-utils": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"eslint-plugin-mozilla": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-mozilla/-/eslint-plugin-mozilla-1.1.1.tgz",
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"eslint-plugin-fetch-options": "0.0.4",
|
||||
"eslint-plugin-import": "2.16.0",
|
||||
"eslint-plugin-json": "1.4.0",
|
||||
"eslint-plugin-jsx-a11y": "6.2.1",
|
||||
"eslint-plugin-mozilla": "1.1.1",
|
||||
"eslint-plugin-no-unsanitized": "3.0.2",
|
||||
"eslint-plugin-promise": "4.0.1",
|
||||
|
@ -135,6 +136,7 @@
|
|||
"debugcoverage": "open logs/coverage/index.html",
|
||||
"lint": "npm-run-all lint:*",
|
||||
"lint:eslint": "esw --ext=.js,.jsm,.json,.jsx .",
|
||||
"lint:jsx-a11y": "esw --config=.eslintrc.jsx-a11y.js --ext=.jsx content-src/asrouter/components/ModalOverlay content-src/asrouter/templates/OnboardingMessage content-src/asrouter/templates/Trailhead",
|
||||
"lint:sasslint": "sass-lint -v -q",
|
||||
"strings-import": "node ./bin/strings-import.js",
|
||||
"test": "npm run testmc",
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
<script src="resource://activity-stream/vendor/react.js"></script>
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
<script src="resource://activity-stream/vendor/react.js"></script>
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
<script src="resource://activity-stream/vendor/react.js"></script>
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
<script src="resource://activity-stream/vendor/react.js"></script>
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
<script src="resource://activity-stream/vendor/react.js"></script>
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
<script src="resource://activity-stream/vendor/react.js"></script>
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
<script src="resource://activity-stream/vendor/react.js"></script>
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
<script src="resource://activity-stream/vendor/react.js"></script>
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
<script src="resource://activity-stream/vendor/react.js"></script>
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
|
||||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="footer-snippets-container"></div>
|
||||
<div id="footer-asrouter-container"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
<script src="resource://activity-stream/vendor/react.js"></script>
|
||||
|
|