Bug 1866802 - Move ASRouterAdmin tool to about:asrouter and its own component folder. r=pdahiya,Gijs,desktop-theme-reviewers,dao

This tries to maintain stylistic continuity, while also trying to decouple from
newtab as much as possible. This is a first foray, and future patches will
further this decoupling.

This also modifies about:asrouter to show an error message if the ASRouter devtools
pref is not set to true.

Differential Revision: https://phabricator.services.mozilla.com/D194811
This commit is contained in:
Mike Conley 2023-12-14 18:46:55 +00:00
parent 19fe553001
commit 9415a9f13d
29 changed files with 12268 additions and 0 deletions

View File

@ -233,6 +233,7 @@ module.exports = {
"browser/components/Browser*",
"browser/components/aboutlogins/**",
"browser/components/aboutwelcome/**",
"browser/components/asrouter/**",
"browser/components/attribution/**",
"browser/components/customizableui/**",
"browser/components/downloads/**",
@ -498,6 +499,7 @@ module.exports = {
extends: ["plugin:react-hooks/recommended"],
files: [
"browser/components/aboutwelcome/**",
"browser/components/asrouter/**",
"browser/components/newtab/**",
"browser/components/pocket/**",
"devtools/**",

8
.gitignore vendored
View File

@ -56,12 +56,20 @@ security/manager/.nss.checkout
# gecko.log is generated by various test harnesses
/gecko.log
# Ignore all node_modules directories
node_modules/
# ...but allow ones under third_party
!/third_party/**/node_modules/
# Ignore newtab component build assets
browser/components/newtab/logs/
# Ignore about:welcome component build assets
browser/components/aboutwelcome/logs/
# Ignore ASRouter component build assets
browser/components/asrouter/logs/
# Ignore ASRouter generated test files
browser/components/newtab/content-src/asrouter/schemas/corpus/CFRMessageProvider.messages.json
browser/components/newtab/content-src/asrouter/schemas/corpus/OnboardingMessageProvider.messages.json

View File

@ -60,6 +60,9 @@ compile_commands\.json
# Ignore about:welcome build assets
^browser/components/aboutwelcome/logs/
# Ignore ASRouter component build assets
^browser/components/asrouter/logs/
# Ignore ASRouter generated test files
^browser/components/newtab/content-src/asrouter/schemas/corpus/CFRMessageProvider.messages.json
^browser/components/newtab/content-src/asrouter/schemas/corpus/OnboardingMessageProvider.messages.json
@ -198,6 +201,7 @@ _OPT\.OBJ/
^node_modules/
^tools/browsertime/node_modules/
^tools/lint/eslint/eslint-plugin-mozilla/node_modules/
^browser/components/asrouter/node_modules/
^browser/components/newtab/node_modules/
^browser/components/aboutwelcome/node_modules/
^tools/esmify/node_modules/

View File

@ -1449,6 +1449,9 @@ xpcom/io/crc32c.c
.gradle/
browser/components/aboutwelcome/content/aboutwelcome.bundle.js
browser/components/asrouter/node_modules/
browser/components/asrouter/content/asrouter-admin.bundle.js
browser/components/asrouter/logs/
browser/components/newtab/content-src/asrouter/schemas/BackgroundTaskMessagingExperiment.schema.json
browser/components/newtab/content-src/asrouter/schemas/MessagingExperiment.schema.json
browser/components/newtab/logs/

View File

@ -26,6 +26,7 @@ obj*/
browser/components/pocket/content/panels/css/main.compiled.css
browser/components/newtab/**/*.css
browser/components/aboutwelcome/**/*.css
browser/components/asrouter/**/*.css
# Note that the debugger has its own stylelint setup, but that currently
# produces errors. Bug 1831302 tracks making this better

View File

@ -260,6 +260,7 @@ module.exports = {
{
files: [
"browser/components/aboutwelcome/**",
"browser/components/asrouter/**",
"browser/components/newtab/**",
],
customSyntax: "postcss-scss",

View File

@ -581,6 +581,7 @@ let JSWINDOWACTORS = {
includeChrome: true,
allFrames: true,
matches: [
"about:asrouter",
"about:home",
"about:newtab",
"about:welcome",
@ -796,6 +797,7 @@ let JSWINDOWACTORS = {
},
},
matches: [
"about:asrouter*",
"about:home*",
"about:newtab*",
"about:welcome*",

View File

@ -44,6 +44,11 @@ struct RedirEntry {
browser/components/about/components.conf
*/
static const RedirEntry kRedirMap[] = {
{"asrouter", "chrome://browser/content/asrouter/asrouter-admin.html",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
nsIAboutModule::URI_MUST_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT},
{"blocked", "chrome://browser/content/blockedSite.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |

View File

@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
pages = [
'asrouter',
'blocked',
'certerror',
'downloads',

View File

@ -0,0 +1,175 @@
/* 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/. */
module.exports = {
// When adding items to this file please check for effects on sub-directories.
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
plugins: ["import", "react", "jsx-a11y"],
settings: {
react: {
version: "16.2.0",
},
},
extends: ["plugin:jsx-a11y/recommended"],
overrides: [
{
// Only mark the files as modules which are actually modules.
// TODO: Add "tests/unit/**" to this list once we get our tests built.
files: ["content-src/**"],
parserOptions: {
sourceType: "module",
},
},
{
// TODO: Add ./*.js and tests/unit/** to this list if necessary
files: ["./*.js", "content-src/**"],
env: {
node: true,
},
},
/* TODO: Turn this rule on when I move the tests over.
{
// Use a configuration that's appropriate for modules, workers and
// non-production files.
files: ["modules/*.jsm", "tests/**"],
rules: {
"no-implicit-globals": "off",
},
},
*/
{
// TODO: Add "tests/unit/**" to this list once we get our tests built.
files: ["content-src/**"],
rules: {
// Disallow commonjs in these directories.
"import/no-commonjs": 2,
// Allow JSX with arrow functions.
"react/jsx-no-bind": 0,
},
},
/* TODO: Turn this rule on when I move the tests over.
{
// These tests simulate the browser environment.
files: "tests/unit/**",
env: {
browser: true,
mocha: true,
},
globals: {
assert: true,
chai: true,
sinon: true,
},
},
{
files: "tests/**",
rules: {
"func-name-matching": 0,
"lines-between-class-members": 0,
"require-await": 0,
},
},*/
],
rules: {
"fetch-options/no-fetch-credentials": "error",
"react/jsx-boolean-value": ["error", "always"],
"react/jsx-key": "error",
"react/jsx-no-bind": "error",
"react/jsx-no-comment-textnodes": "error",
"react/jsx-no-duplicate-props": "error",
"react/jsx-no-target-blank": "error",
"react/jsx-no-undef": "error",
"react/jsx-pascal-case": "error",
"react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error",
"react/no-access-state-in-setstate": "error",
"react/no-danger": "error",
"react/no-deprecated": "error",
"react/no-did-mount-set-state": "error",
"react/no-did-update-set-state": "error",
"react/no-direct-mutation-state": "error",
"react/no-is-mounted": "error",
"react/no-unknown-property": "error",
"react/require-render-return": "error",
"accessor-pairs": ["error", { setWithoutGet: true, getWithoutSet: false }],
"array-callback-return": "error",
"block-scoped-var": "error",
"consistent-this": ["error", "use-bind"],
eqeqeq: "error",
"for-direction": "error",
"func-name-matching": "error",
"getter-return": "error",
"guard-for-in": "error",
"handle-callback-err": "error",
"lines-between-class-members": "error",
"max-depth": ["error", 4],
"max-nested-callbacks": ["error", 4],
"max-params": ["error", 6],
"max-statements": ["error", 50],
"max-statements-per-line": ["error", { max: 2 }],
"new-cap": ["error", { newIsCap: true, capIsNew: false }],
"no-alert": "error",
"no-buffer-constructor": "error",
"no-console": ["error", { allow: ["error"] }],
"no-div-regex": "error",
"no-duplicate-imports": "error",
"no-eq-null": "error",
"no-extend-native": "error",
"no-extra-label": "error",
"no-implicit-coercion": ["error", { allow: ["!!"] }],
"no-implicit-globals": "error",
"no-loop-func": "error",
"no-mixed-requires": "error",
"no-multi-assign": "error",
"no-multi-str": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-require": "error",
"no-octal-escape": "error",
"no-param-reassign": "error",
"no-path-concat": "error",
"no-process-exit": "error",
"no-proto": "error",
"no-prototype-builtins": "error",
"no-return-assign": ["error", "except-parens"],
"no-script-url": "error",
"no-shadow": "error",
"no-template-curly-in-string": "error",
"no-undef-init": "error",
"no-unmodified-loop-condition": "error",
"no-unused-expressions": "error",
"no-use-before-define": "error",
"no-useless-computed-key": "error",
"no-useless-constructor": "error",
"no-useless-rename": "error",
"no-var": "error",
"no-void": ["error", { allowAsStatement: true }],
"one-var": ["error", "never"],
"operator-assignment": ["error", "always"],
"prefer-destructuring": [
"error",
{
AssignmentExpression: { array: true },
VariableDeclarator: { array: true, object: true },
},
],
"prefer-numeric-literals": "error",
"prefer-promise-reject-errors": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "error",
radix: ["error", "always"],
"require-await": "error",
"sort-vars": "error",
"symbol-description": "error",
"vars-on-top": "error",
yoda: ["error", "never"],
},
};

View File

@ -0,0 +1,353 @@
/* 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/. */
/* stylelint-disable max-nesting-depth */
@import '../../../../newtab/content-src/styles/variables';
@import '../../../../newtab/content-src/styles/theme';
@import '../../../../newtab/content-src/styles/icons';
@import '../../../../newtab/content-src/asrouter/components/Button/Button';
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Ubuntu, 'Helvetica Neue', sans-serif;
}
/**
* These styles are copied verbatim from _activity-stream.scss in order to maintain
* a continuity of styling while also decoupling from the newtab code. This should
* be removed when about:asrouter starts using the default in-content style sheets.
*/
.button,
.actions button {
background-color: var(--newtab-button-secondary-color);
border: $border-primary;
border-radius: 4px;
color: inherit;
cursor: pointer;
margin-bottom: 15px;
padding: 10px 30px;
white-space: nowrap;
&:hover:not(.dismiss),
&:focus:not(.dismiss) {
box-shadow: $shadow-primary;
transition: box-shadow 150ms;
}
&.dismiss {
background-color: transparent;
border: 0;
padding: 0;
text-decoration: underline;
}
// Blue button
&.primary,
&.done {
background-color: var(--newtab-primary-action-background);
border: solid 1px var(--newtab-primary-action-background);
color: var(--newtab-primary-element-text-color);
margin-inline-start: auto;
}
}
.asrouter-admin {
max-width: 1300px;
$border-color: var(--newtab-border-color);
$monospace: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Mono', 'Droid Sans Mono',
'Source Code Pro', monospace;
$sidebar-width: 240px;
font-size: 14px;
padding-inline-start: $sidebar-width;
color: var(--newtab-text-primary-color);
&.collapsed {
display: none;
}
.sidebar {
inset-inline-start: 0;
position: fixed;
width: $sidebar-width;
ul {
margin: 0;
padding: 0;
list-style: none;
}
li a {
padding: 10px 34px;
display: block;
color: var(--lwt-sidebar-text-color);
&:hover {
background: var(--newtab-background-color-secondary);
}
}
}
h1 {
font-weight: 200;
font-size: 32px;
}
h2 .button,
p .button {
font-size: 14px;
padding: 6px 12px;
margin-inline-start: 5px;
margin-bottom: 0;
}
.general-textarea {
direction: ltr;
width: 740px;
height: 500px;
overflow: auto;
resize: none;
border-radius: 4px;
display: flex;
}
.wnp-textarea {
direction: ltr;
width: 740px;
height: 500px;
overflow: auto;
resize: none;
border-radius: 4px;
display: flex;
}
.json-button {
display: inline-flex;
font-size: 10px;
padding: 4px 10px;
margin-bottom: 6px;
margin-inline-end: 4px;
&:hover {
background-color: var(--newtab-element-hover-color);
box-shadow: none;
}
}
table {
border-collapse: collapse;
&.minimal-table {
border-collapse: collapse;
border: 1px solid $border-color;
td {
padding: 8px;
}
td:first-child {
width: 1%;
white-space: nowrap;
}
td:not(:first-child) {
font-family: $monospace;
}
}
&.errorReporting {
tr {
border: 1px solid var(--newtab-background-color-secondary);
}
td {
padding: 4px;
&[rowspan] {
border: 1px solid var(--newtab-background-color-secondary);
}
}
}
}
.sourceLabel {
background: var(--newtab-background-color-secondary);
padding: 2px 5px;
border-radius: 3px;
&.isDisabled {
background: $email-input-invalid;
color: var(--newtab-status-error);
}
}
.message-item {
&:first-child td {
border-top: 1px solid $border-color;
}
td {
vertical-align: top;
padding: 8px;
border-bottom: 1px solid $border-color;
&.min {
width: 1%;
white-space: nowrap;
}
&.message-summary {
width: 60%;
}
&.button-column {
width: 15%;
}
&:first-child {
border-inline-start: 1px solid $border-color;
}
&:last-child {
border-inline-end: 1px solid $border-color;
}
}
&.blocked {
.message-id,
.message-summary {
opacity: 0.5;
}
.message-id {
opacity: 0.5;
}
}
.message-id {
font-family: $monospace;
font-size: 12px;
}
}
.providerUrl {
font-size: 12px;
}
pre {
background: var(--newtab-background-color-secondary);
margin: 0;
padding: 8px;
font-size: 12px;
max-width: 750px;
overflow: auto;
font-family: $monospace;
}
.errorState {
border: $input-error-border;
}
.helpLink {
padding: 10px;
display: flex;
background: $black-10;
border-radius: 3px;
align-items: center;
a {
text-decoration: underline;
}
.icon {
min-width: 18px;
min-height: 18px;
}
}
.ds-component {
margin-bottom: 20px;
}
.modalOverlayInner {
height: 80%;
}
.clearButton {
border: 0;
padding: 4px;
border-radius: 4px;
display: flex;
&:hover {
background: var(--newtab-element-hover-color);
}
}
.collapsed {
display: none;
}
.icon {
display: inline-table;
cursor: pointer;
width: 18px;
height: 18px;
}
.button {
&:disabled,
&:disabled:active {
opacity: 0.5;
cursor: unset;
box-shadow: none;
}
}
.impressions-section {
display: flex;
flex-direction: column;
gap: 16px;
.impressions-item {
display: flex;
flex-flow: column nowrap;
padding: 8px;
border: 1px solid $border-color;
border-radius: 5px;
.impressions-inner-box {
display: flex;
flex-flow: row nowrap;
gap: 8px;
}
.impressions-category {
font-size: 1.15em;
white-space: nowrap;
flex-grow: 0.1;
}
.impressions-buttons {
display: flex;
flex-direction: column;
gap: 8px;
button {
margin: 0;
}
}
.impressions-editor {
display: flex;
flex-grow: 1.5;
.general-textarea {
width: auto;
flex-grow: 1;
}
}
}
}
}

View File

@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import React, { useState, useRef, useCallback } from "react";
export const CopyButton = ({
className,
label,
copiedLabel,
inputSelector,
transformer,
...props
}) => {
const [copied, setCopied] = useState(false);
const timeout = useRef(null);
const onClick = useCallback(() => {
let text = document.querySelector(inputSelector).value;
if (transformer) {
text = transformer(text);
}
navigator.clipboard.writeText(text);
clearTimeout(timeout.current);
setCopied(true);
timeout.current = setTimeout(() => setCopied(false), 1500);
}, [inputSelector, transformer]);
return (
<button className={className} onClick={e => onClick()} {...props}>
{(copied && copiedLabel) || label}
</button>
);
};

View File

@ -0,0 +1,146 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { ASRouterUtils } from "newtab/content-src/asrouter/asrouter-utils";
import React, {
useState,
useMemo,
useCallback,
useEffect,
useRef,
} from "react";
const stringify = json => JSON.stringify(json, null, 2);
export const ImpressionsSection = ({
messageImpressions,
groupImpressions,
screenImpressions,
}) => {
const handleSaveMessageImpressions = useCallback(newImpressions => {
ASRouterUtils.editState("messageImpressions", newImpressions);
}, []);
const handleSaveGroupImpressions = useCallback(newImpressions => {
ASRouterUtils.editState("groupImpressions", newImpressions);
}, []);
const handleSaveScreenImpressions = useCallback(newImpressions => {
ASRouterUtils.editState("screenImpressions", newImpressions);
}, []);
const handleResetMessageImpressions = useCallback(() => {
ASRouterUtils.sendMessage({ type: "RESET_MESSAGE_STATE" });
}, []);
const handleResetGroupImpressions = useCallback(() => {
ASRouterUtils.sendMessage({ type: "RESET_GROUPS_STATE" });
}, []);
const handleResetScreenImpressions = useCallback(() => {
ASRouterUtils.sendMessage({ type: "RESET_SCREEN_IMPRESSIONS" });
}, []);
return (
<div className="impressions-section">
<ImpressionsItem
impressions={messageImpressions}
label="Message Impressions"
description="Message impressions are stored in an object, where each key is a message ID and each value is an array of timestamps. They are cleaned up when a message with that ID stops existing in ASRouter state (such as at the end of an experiment)."
onSave={handleSaveMessageImpressions}
onReset={handleResetMessageImpressions}
/>
<ImpressionsItem
impressions={groupImpressions}
label="Group Impressions"
description="Group impressions are stored in an object, where each key is a group ID and each value is an array of timestamps. They are never cleaned up."
onSave={handleSaveGroupImpressions}
onReset={handleResetGroupImpressions}
/>
<ImpressionsItem
impressions={screenImpressions}
label="Screen Impressions"
description="Screen impressions are stored in an object, where each key is a screen ID and each value is the most recent timestamp that screen was shown. They are never cleaned up."
onSave={handleSaveScreenImpressions}
onReset={handleResetScreenImpressions}
/>
</div>
);
};
const ImpressionsItem = ({
impressions,
label,
description,
validator,
onSave,
onReset,
}) => {
const [json, setJson] = useState(stringify(impressions));
const modified = useRef(false);
const isValidJson = useCallback(
text => {
try {
JSON.parse(text);
return validator ? validator(text) : true;
} catch (e) {
return false;
}
},
[validator]
);
const jsonIsInvalid = useMemo(() => !isValidJson(json), [json, isValidJson]);
const handleChange = useCallback(e => {
setJson(e.target.value);
modified.current = true;
}, []);
const handleSave = useCallback(() => {
if (jsonIsInvalid) {
return;
}
const newImpressions = JSON.parse(json);
modified.current = false;
onSave(newImpressions);
}, [json, jsonIsInvalid, onSave]);
const handleReset = useCallback(() => {
modified.current = false;
onReset();
}, [onReset]);
useEffect(() => {
if (!modified.current) {
setJson(stringify(impressions));
}
}, [impressions]);
return (
<div className="impressions-item">
<span className="impressions-category">{label}</span>
{description ? (
<p className="impressions-description">{description}</p>
) : null}
<div className="impressions-inner-box">
<div className="impressions-buttons">
<button
className="button primary"
disabled={jsonIsInvalid}
onClick={handleSave}
>
Save
</button>
<button className="button reset" onClick={handleReset}>
Reset
</button>
</div>
<div className="impressions-editor">
<textarea
className="general-textarea"
value={json}
onChange={handleChange}
/>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,35 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from "react";
export class SimpleHashRouter extends React.PureComponent {
constructor(props) {
super(props);
this.onHashChange = this.onHashChange.bind(this);
this.state = { hash: global.location.hash };
}
onHashChange() {
this.setState({ hash: global.location.hash });
}
componentWillMount() {
global.addEventListener("hashchange", this.onHashChange);
}
componentWillUnmount() {
global.removeEventListener("hashchange", this.onHashChange);
}
render() {
const [, ...routes] = this.state.hash.split("-");
return React.cloneElement(this.props.children, {
location: {
hash: this.state.hash,
routes,
},
});
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
<!-- 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/. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; object-src 'none'; script-src resource: chrome:;"
/>
<meta name="color-scheme" content="light dark" />
<title>ASRouter Admin</title>
<link
rel="icon"
type="image/png"
href="chrome://branding/content/icon32.png"
/>
<link rel="localization" href="branding/brand.ftl" />
<link rel="localization" href="toolkit/branding/brandings.ftl" />
<link
rel="stylesheet"
href="chrome://browser/content/asrouter/components/ASRouterAdmin/ASRouterAdmin.css"
/>
</head>
<body>
<div id="root"></div>
<script src="chrome://browser/content/contentTheme.js"></script>
<script src="resource://activity-stream/vendor/react.js"></script>
<script src="resource://activity-stream/vendor/react-dom.js"></script>
<script src="resource://activity-stream/vendor/prop-types.js"></script>
<script src="chrome://browser/content/asrouter/asrouter-admin.bundle.js"></script>
<!-- The render.js script is the main entrypoint for the page. -->
<script src="chrome://browser/content/asrouter/render.js"></script>
</body>
</html>

View File

@ -0,0 +1,546 @@
/* 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/. */
/* stylelint-disable max-nesting-depth */
:root {
--newtab-background-color: #F9F9FB;
--newtab-background-color-secondary: #FFF;
--newtab-text-primary-color: #15141a;
--newtab-primary-action-background: #0061e0;
--newtab-primary-action-background-pocket: #008078;
--newtab-text-secondary-color: color-mix(in srgb, var(--newtab-text-primary-color) 70%, transparent);
--newtab-element-hover-color: color-mix(in srgb, var(--newtab-background-color) 90%, #000);
--newtab-element-active-color: color-mix(in srgb, var(--newtab-background-color) 80%, #000);
--newtab-element-secondary-color: color-mix(in srgb, currentColor 5%, transparent);
--newtab-element-secondary-hover-color: color-mix(in srgb, currentColor 12%, transparent);
--newtab-element-secondary-active-color: color-mix(in srgb, currentColor 25%, transparent);
--newtab-primary-element-hover-color: color-mix(in srgb, var(--newtab-primary-action-background) 90%, #000);
--newtab-primary-element-hover-pocket-color: color-mix(in srgb, var(--newtab-primary-action-background-pocket) 90%, #000);
--newtab-primary-element-active-color: color-mix(in srgb, var(--newtab-primary-action-background) 80%, #000);
--newtab-primary-element-text-color: #FFF;
--newtab-primary-action-background-dimmed: color-mix(in srgb, var(--newtab-primary-action-background) 25%, transparent);
--newtab-primary-action-background-pocket-dimmed: color-mix(in srgb, var(--newtab-primary-action-background-pocket) 25%, transparent);
--newtab-border-color: color-mix(in srgb, var(--newtab-background-color) 75%, #000);
--newtab-wordmark-color: #20123A;
--newtab-status-success: #058B00;
--newtab-status-error: #D70022;
--newtab-inner-box-shadow-color: rgba(0, 0, 0, 0.1);
--newtab-overlay-color: color-mix(in srgb, var(--newtab-background-color) 85%, transparent);
--newtab-textbox-focus-color: var(--newtab-primary-action-background);
--newtab-textbox-focus-boxshadow: 0 0 0 1px var(--newtab-primary-action-background), 0 0 0 4px rgba(var(--newtab-primary-action-background), 0.3);
--newtab-button-secondary-color: inherit;
}
:root[lwt-newtab-brighttext] {
--newtab-background-color: #2B2A33;
--newtab-background-color-secondary: #42414d;
--newtab-text-primary-color: #fbfbfe;
--newtab-primary-action-background: #00ddff;
--newtab-primary-action-background-pocket: #00ddff;
--newtab-primary-action-background-pocket-dimmed: color-mix(in srgb, var(--newtab-primary-action-background) 25%, transparent);
--newtab-primary-element-hover-color: color-mix(in srgb, var(--newtab-primary-action-background) 55%, #FFF);
--newtab-primary-element-hover-pocket-color: color-mix(in srgb, var(--newtab-primary-action-background-pocket) 55%, #FFF);
--newtab-element-hover-color: color-mix(in srgb, var(--newtab-background-color) 80%, #FFF);
--newtab-element-active-color: color-mix(in srgb, var(--newtab-background-color) 60%, #FFF);
--newtab-element-secondary-color: color-mix(in srgb, currentColor 10%, transparent);
--newtab-element-secondary-hover-color: color-mix(in srgb, currentColor 17%, transparent);
--newtab-element-secondary-active-color: color-mix(in srgb, currentColor 30%, transparent);
--newtab-border-color: color-mix(in srgb, var(--newtab-background-color) 75%, #FFF);
--newtab-primary-element-text-color: #2b2a33;
--newtab-wordmark-color: #fbfbfe;
--newtab-status-success: #7C6;
}
@media (prefers-contrast) {
:root {
--newtab-text-secondary-color: var(--newtab-text-primary-color);
}
}
.icon {
background-position: center center;
background-repeat: no-repeat;
background-size: 16px;
-moz-context-properties: fill;
display: inline-block;
color: var(--newtab-text-primary-color);
fill: currentColor;
height: 16px;
vertical-align: middle;
width: 16px;
}
.icon.icon-spacer {
margin-inline-end: 8px;
}
.icon.icon-small-spacer {
margin-inline-end: 6px;
}
.icon.icon-button-style {
fill: var(--newtab-text-secondary-color);
border: 0;
}
.icon.icon-button-style:focus, .icon.icon-button-style:hover {
fill: var(--newtab-text-primary-color);
}
.icon.icon-bookmark-added {
background-image: url("chrome://browser/skin/bookmark.svg");
}
.icon.icon-bookmark-hollow {
background-image: url("chrome://browser/skin/bookmark-hollow.svg");
}
.icon.icon-clear-input {
background-image: url("chrome://global/skin/icons/close-fill.svg");
}
.icon.icon-delete {
background-image: url("chrome://global/skin/icons/delete.svg");
}
.icon.icon-search {
background-image: url("chrome://global/skin/icons/search-glass.svg");
}
.icon.icon-modal-delete {
flex-shrink: 0;
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-modal-delete-20.svg");
background-size: 32px;
height: 32px;
width: 32px;
}
.icon.icon-mail {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-mail-16.svg");
}
.icon.icon-dismiss {
background-image: url("chrome://global/skin/icons/close.svg");
}
.icon.icon-info {
background-image: url("chrome://global/skin/icons/info.svg");
}
.icon.icon-new-window {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-newWindow-16.svg");
}
.icon.icon-new-window:dir(rtl) {
transform: scaleX(-1);
}
.icon.icon-new-window-private {
background-image: url("chrome://browser/skin/privateBrowsing.svg");
}
.icon.icon-settings {
background-image: url("chrome://global/skin/icons/settings.svg");
}
.icon.icon-pin {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-pin-16.svg");
}
.icon.icon-pin:dir(rtl) {
transform: scaleX(-1);
}
.icon.icon-unpin {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-unpin-16.svg");
}
.icon.icon-unpin:dir(rtl) {
transform: scaleX(-1);
}
.icon.icon-edit {
background-image: url("chrome://global/skin/icons/edit.svg");
}
.icon.icon-pocket {
background-image: url("chrome://global/skin/icons/pocket.svg");
}
.icon.icon-pocket-save {
background-image: url("chrome://global/skin/icons/pocket.svg");
fill: #FFF;
}
.icon.icon-pocket-delete {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-pocket-delete-16.svg");
}
.icon.icon-pocket-archive {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-pocket-archive-16.svg");
}
.icon.icon-history-item {
background-image: url("chrome://browser/skin/history.svg");
}
.icon.icon-trending {
background-image: url("chrome://browser/skin/trending.svg");
transform: translateY(2px);
}
.icon.icon-now {
background-image: url("chrome://browser/skin/history.svg");
}
.icon.icon-topsites {
background-image: url("chrome://browser/skin/topsites.svg");
}
.icon.icon-pin-small {
background-image: url("chrome://browser/skin/pin-12.svg");
background-size: 12px;
height: 12px;
width: 12px;
}
.icon.icon-pin-small:dir(rtl) {
transform: scaleX(-1);
}
.icon.icon-check {
background-image: url("chrome://global/skin/icons/check.svg");
}
.icon.icon-download {
background-image: url("chrome://browser/skin/downloads/downloads.svg");
}
.icon.icon-copy {
background-image: url("chrome://global/skin/icons/edit-copy.svg");
}
.icon.icon-open-file {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-open-file-16.svg");
}
.icon.icon-webextension {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-webextension-16.svg");
}
.icon.icon-highlights {
background-image: url("chrome://global/skin/icons/highlights.svg");
}
.icon.icon-arrowhead-down {
background-image: url("chrome://global/skin/icons/arrow-down.svg");
}
.icon.icon-arrowhead-down-small {
background-image: url("chrome://global/skin/icons/arrow-down-12.svg");
background-size: 12px;
height: 12px;
width: 12px;
}
.icon.icon-arrowhead-forward-small {
background-image: url("chrome://global/skin/icons/arrow-right-12.svg");
background-size: 12px;
height: 12px;
width: 12px;
}
.icon.icon-arrowhead-forward-small:dir(rtl) {
background-image: url("chrome://global/skin/icons/arrow-left-12.svg");
}
.icon.icon-arrowhead-up {
background-image: url("chrome://global/skin/icons/arrow-up.svg");
}
.icon.icon-add {
background-image: url("chrome://global/skin/icons/plus.svg");
}
.icon.icon-minimize {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-minimize-16.svg");
}
.icon.icon-maximize {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-maximize-16.svg");
}
.icon.icon-arrow {
background-image: url("chrome://global/skin/icons/arrow-right-12.svg");
}
.ASRouterButton {
font-weight: 600;
font-size: 14px;
white-space: nowrap;
border-radius: 2px;
border: 0;
font-family: inherit;
padding: 8px 15px;
margin-inline-start: 12px;
color: inherit;
cursor: pointer;
}
.tall .ASRouterButton {
margin-inline-start: 20px;
}
.ASRouterButton.test-only {
width: 0;
height: 0;
overflow: hidden;
display: block;
visibility: hidden;
}
.ASRouterButton.primary {
border: 1px solid var(--newtab-primary-action-background);
background-color: var(--newtab-primary-action-background);
color: var(--newtab-primary-element-text-color);
}
.ASRouterButton.primary:hover {
background-color: var(--newtab-primary-element-hover-color);
}
.ASRouterButton.primary:active {
background-color: var(--newtab-primary-element-active-color);
}
.ASRouterButton.slim {
border: 1px solid var(--newtab-border-color);
margin-inline-start: 0;
font-size: 12px;
padding: 6px 12px;
}
.ASRouterButton.slim:hover, .ASRouterButton.slim:focus {
box-shadow: 0 0 0 5px var(--newtab-element-secondary-color);
transition: box-shadow 150ms;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Ubuntu, "Helvetica Neue", sans-serif;
}
/**
* These styles are copied verbatim from _activity-stream.scss in order to maintain
* a continuity of styling while also decoupling from the newtab code. This should
* be removed when about:asrouter starts using the default in-content style sheets.
*/
.button,
.actions button {
background-color: var(--newtab-button-secondary-color);
border: 1px solid var(--newtab-border-color);
border-radius: 4px;
color: inherit;
cursor: pointer;
margin-bottom: 15px;
padding: 10px 30px;
white-space: nowrap;
}
.button:hover:not(.dismiss), .button:focus:not(.dismiss),
.actions button:hover:not(.dismiss),
.actions button:focus:not(.dismiss) {
box-shadow: 0 0 0 5px var(--newtab-element-secondary-color);
transition: box-shadow 150ms;
}
.button.dismiss,
.actions button.dismiss {
background-color: transparent;
border: 0;
padding: 0;
text-decoration: underline;
}
.button.primary, .button.done,
.actions button.primary,
.actions button.done {
background-color: var(--newtab-primary-action-background);
border: solid 1px var(--newtab-primary-action-background);
color: var(--newtab-primary-element-text-color);
margin-inline-start: auto;
}
.asrouter-admin {
max-width: 1300px;
font-size: 14px;
padding-inline-start: 240px;
color: var(--newtab-text-primary-color);
}
.asrouter-admin.collapsed {
display: none;
}
.asrouter-admin .sidebar {
inset-inline-start: 0;
position: fixed;
width: 240px;
}
.asrouter-admin .sidebar ul {
margin: 0;
padding: 0;
list-style: none;
}
.asrouter-admin .sidebar li a {
padding: 10px 34px;
display: block;
color: var(--lwt-sidebar-text-color);
}
.asrouter-admin .sidebar li a:hover {
background: var(--newtab-background-color-secondary);
}
.asrouter-admin h1 {
font-weight: 200;
font-size: 32px;
}
.asrouter-admin h2 .button,
.asrouter-admin p .button {
font-size: 14px;
padding: 6px 12px;
margin-inline-start: 5px;
margin-bottom: 0;
}
.asrouter-admin .general-textarea {
direction: ltr;
width: 740px;
height: 500px;
overflow: auto;
resize: none;
border-radius: 4px;
display: flex;
}
.asrouter-admin .wnp-textarea {
direction: ltr;
width: 740px;
height: 500px;
overflow: auto;
resize: none;
border-radius: 4px;
display: flex;
}
.asrouter-admin .json-button {
display: inline-flex;
font-size: 10px;
padding: 4px 10px;
margin-bottom: 6px;
margin-inline-end: 4px;
}
.asrouter-admin .json-button:hover {
background-color: var(--newtab-element-hover-color);
box-shadow: none;
}
.asrouter-admin table {
border-collapse: collapse;
}
.asrouter-admin table.minimal-table {
border-collapse: collapse;
border: 1px solid var(--newtab-border-color);
}
.asrouter-admin table.minimal-table td {
padding: 8px;
}
.asrouter-admin table.minimal-table td:first-child {
width: 1%;
white-space: nowrap;
}
.asrouter-admin table.minimal-table td:not(:first-child) {
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace;
}
.asrouter-admin table.errorReporting tr {
border: 1px solid var(--newtab-background-color-secondary);
}
.asrouter-admin table.errorReporting td {
padding: 4px;
}
.asrouter-admin table.errorReporting td[rowspan] {
border: 1px solid var(--newtab-background-color-secondary);
}
.asrouter-admin .sourceLabel {
background: var(--newtab-background-color-secondary);
padding: 2px 5px;
border-radius: 3px;
}
.asrouter-admin .sourceLabel.isDisabled {
background: rgba(215, 0, 34, 0.3);
color: var(--newtab-status-error);
}
.asrouter-admin .message-item:first-child td {
border-top: 1px solid var(--newtab-border-color);
}
.asrouter-admin .message-item td {
vertical-align: top;
padding: 8px;
border-bottom: 1px solid var(--newtab-border-color);
}
.asrouter-admin .message-item td.min {
width: 1%;
white-space: nowrap;
}
.asrouter-admin .message-item td.message-summary {
width: 60%;
}
.asrouter-admin .message-item td.button-column {
width: 15%;
}
.asrouter-admin .message-item td:first-child {
border-inline-start: 1px solid var(--newtab-border-color);
}
.asrouter-admin .message-item td:last-child {
border-inline-end: 1px solid var(--newtab-border-color);
}
.asrouter-admin .message-item.blocked .message-id,
.asrouter-admin .message-item.blocked .message-summary {
opacity: 0.5;
}
.asrouter-admin .message-item.blocked .message-id {
opacity: 0.5;
}
.asrouter-admin .message-item .message-id {
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace;
font-size: 12px;
}
.asrouter-admin .providerUrl {
font-size: 12px;
}
.asrouter-admin pre {
background: var(--newtab-background-color-secondary);
margin: 0;
padding: 8px;
font-size: 12px;
max-width: 750px;
overflow: auto;
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace;
}
.asrouter-admin .errorState {
border: 1px solid var(--newtab-status-error);
}
.asrouter-admin .helpLink {
padding: 10px;
display: flex;
background: rgba(0, 0, 0, 0.1);
border-radius: 3px;
align-items: center;
}
.asrouter-admin .helpLink a {
text-decoration: underline;
}
.asrouter-admin .helpLink .icon {
min-width: 18px;
min-height: 18px;
}
.asrouter-admin .ds-component {
margin-bottom: 20px;
}
.asrouter-admin .modalOverlayInner {
height: 80%;
}
.asrouter-admin .clearButton {
border: 0;
padding: 4px;
border-radius: 4px;
display: flex;
}
.asrouter-admin .clearButton:hover {
background: var(--newtab-element-hover-color);
}
.asrouter-admin .collapsed {
display: none;
}
.asrouter-admin .icon {
display: inline-table;
cursor: pointer;
width: 18px;
height: 18px;
}
.asrouter-admin .button:disabled, .asrouter-admin .button:disabled:active {
opacity: 0.5;
cursor: unset;
box-shadow: none;
}
.asrouter-admin .impressions-section {
display: flex;
flex-direction: column;
gap: 16px;
}
.asrouter-admin .impressions-section .impressions-item {
display: flex;
flex-flow: column nowrap;
padding: 8px;
border: 1px solid var(--newtab-border-color);
border-radius: 5px;
}
.asrouter-admin .impressions-section .impressions-item .impressions-inner-box {
display: flex;
flex-flow: row nowrap;
gap: 8px;
}
.asrouter-admin .impressions-section .impressions-item .impressions-category {
font-size: 1.15em;
white-space: nowrap;
flex-grow: 0.1;
}
.asrouter-admin .impressions-section .impressions-item .impressions-buttons {
display: flex;
flex-direction: column;
gap: 8px;
}
.asrouter-admin .impressions-section .impressions-item .impressions-buttons button {
margin: 0;
}
.asrouter-admin .impressions-section .impressions-item .impressions-editor {
display: flex;
flex-grow: 1.5;
}
.asrouter-admin .impressions-section .impressions-item .impressions-editor .general-textarea {
width: auto;
flex-grow: 1;
}

View File

@ -0,0 +1,7 @@
/* 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/. */
"use strict";
// exported by asrouter-admin.bundle.js
window.ASRouterAdminRenderUtils.renderASRouterAdmin();

View File

@ -0,0 +1,9 @@
# 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/.
browser.jar:
content/browser/asrouter/asrouter-admin.html (content/asrouter-admin.html)
content/browser/asrouter/asrouter-admin.bundle.js (content/asrouter-admin.bundle.js)
content/browser/asrouter/components/ASRouterAdmin/ASRouterAdmin.css (content/components/ASRouterAdmin/ASRouterAdmin.css)
content/browser/asrouter/render.js (content/render.js)

View File

@ -0,0 +1,10 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
JAR_MANIFESTS += ["jar.mn"]
with Files("**"):
BUG_COMPONENT = ("Firefox", "Messaging System")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
{
"name": "ASRouter",
"description": "Task running for ASRouter",
"version": "1.0.0",
"author": "Mozilla (https://mozilla.org/)",
"dependencies": {
"@fluent/bundle": "0.17.1",
"@fluent/react": "0.15.0",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-redux": "7.2.6",
"redux": "4.1.2"
},
"devDependencies": {
"@babel/plugin-proposal-optional-chaining": "7.16.0",
"@babel/preset-react": "7.16.0",
"babel-loader": "8.2.3",
"mocha": "9.1.3",
"npm-run-all": "4.1.5",
"sass": "1.43.4",
"sinon": "12.0.1",
"webpack": "5.56.0",
"webpack-cli": "4.9.1",
"yamscripts": "0.1.0"
},
"engines": {
"firefox": ">=45.0 <=*",
"//": "when changing node versions, also edit .nvmrc",
"node": "16.19.*",
"npm": "8.19.3"
},
"license": "MPL-2.0",
"config": {
"mc_root": "../../..",
"asrouter_path": "browser/components/asrouter"
},
"scripts": {
"bundle": "npm-run-all bundle:*",
"bundle:admin": "webpack-cli --config webpack.asrouter-admin.config.js",
"bundle:css": "sass content-src:content --no-source-map",
"watchmc": "npm-run-all --parallel watchmc:*",
"watchmc:bundle": "npm run bundle:admin -- --env development -w",
"watchmc:css": "npm run bundle:css -- --source-map --embed-sources --embed-source-map -w",
"testmc": "npm-run-all testmc:*",
"testmc:lint": "npm run lint",
"testmc:build": "npm run bundle:admin",
"testmc:unit": "karma start karma.mc.config.js",
"tddmc": "karma start karma.mc.config.js --tdd",
"debugcoverage": "open logs/coverage/lcov-report/index.html",
"lint": "npm-run-all lint:*",
"lint:codespell": "(cd $npm_package_config_mc_root && ./mach lint -l codespell $npm_package_config_asrouter_path)",
"lint:eslint": "(cd $npm_package_config_mc_root && ./mach lint -l eslint $npm_package_config_asrouter_path)",
"lint:license": "(cd $npm_package_config_mc_root && ./mach lint -l license $npm_package_config_asrouter_path)",
"lint:stylelint": "(cd $npm_package_config_mc_root && ./mach lint -l stylelint $npm_package_config_asrouter_path)",
"test": "npm run testmc",
"tdd": "npm run tddmc",
"fix": "npm-run-all fix:*",
"fix:eslint": "npm run lint:eslint -- --fix",
"fix:stylelint": "npm run lint:stylelint -- --fix",
"help": "yamscripts help",
"yamscripts": "yamscripts compile",
"__": "# NOTE: THESE SCRIPTS ARE COMPILED!!! EDIT yamscripts.yml instead!!!"
}
}

View File

@ -0,0 +1,34 @@
/* 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/. */
const path = require("path");
const config = require("../newtab/webpack.system-addon.config.js");
const webpack = require("webpack");
const absolute = relPath => path.join(__dirname, relPath);
const banner = `
NOTE: This file is generated by webpack from ASRouterAdmin.jsx
using the npm bundle task.
`;
module.exports = Object.assign({}, config(), {
entry: absolute("content-src/components/ASRouterAdmin/ASRouterAdmin.jsx"),
output: {
path: absolute("content"),
filename: "asrouter-admin.bundle.js",
library: "ASRouterAdminRenderUtils",
},
externals: {
"prop-types": "PropTypes",
react: "React",
"react-dom": "ReactDOM",
},
plugins: [new webpack.BannerPlugin(banner)],
// This resolve config allows us to import with paths relative to the root directory, e.g. "lib/ActivityStream.jsm"
resolve: {
extensions: [".js", ".jsx"],
alias: {
newtab: absolute("../newtab"),
common: absolute("../newtab/common"),
},
},
});

View File

@ -0,0 +1,44 @@
# This file compiles to package.json scripts.
# When you add or modify anything, you *MUST* run:
# npm run yamscripts
# to compile your changes.
scripts:
# bundle: Build all assets for ASRouter
bundle:
admin: webpack-cli --config webpack.asrouter-admin.config.js
css: sass content-src:content --no-source-map
# watchmc: Automatically rebuild when files are changed. NOTE: Includes sourcemaps, do not use for profiling/perf testing.
watchmc:
_parallel: true
bundle: =>bundle:admin -- --env development -w
css: =>bundle:css -- --source-map --embed-sources --embed-source-map -w
testmc:
lint: =>lint
build: =>bundle:admin
unit: karma start karma.mc.config.js
tddmc: karma start karma.mc.config.js --tdd
debugcoverage: open logs/coverage/lcov-report/index.html
# lint: Run various linters with mach or local dev dependencies
lint:
codespell: (cd $npm_package_config_mc_root && ./mach lint -l codespell $npm_package_config_asrouter_path)
eslint: (cd $npm_package_config_mc_root && ./mach lint -l eslint $npm_package_config_asrouter_path)
license: (cd $npm_package_config_mc_root && ./mach lint -l license $npm_package_config_asrouter_path)
stylelint: (cd $npm_package_config_mc_root && ./mach lint -l stylelint $npm_package_config_asrouter_path)
# test: Run all tests once
test: =>testmc
# tdd: Run content tests continuously
tdd: =>tddmc
fix:
# Note that since we're currently running eslint-plugin-prettier,
# running fix:eslint will also reformat changed JS files using prettier.
eslint: =>lint:eslint -- --fix
stylelint: =>lint:stylelint -- --fix

View File

@ -30,6 +30,7 @@ DIRS += [
"about",
"aboutlogins",
"aboutwelcome",
"asrouter",
"attribution",
"contentanalysis",
"contextualidentity",

View File

@ -1116,6 +1116,7 @@ class _ASRouter {
userPrefs: lazy.ASRouterPreferences.getAllUserPreferences(),
targetingParameters,
errors: this.errors,
devtoolsEnabled: lazy.ASRouterPreferences.devtoolsEnabled,
}));
}

View File

@ -1594,6 +1594,7 @@ describe("ASRouter", () => {
userPrefs: ASRouterPreferences.getAllUserPreferences(),
targetingParameters: {},
errors: Router.errors,
devtoolsEnabled: ASRouterPreferences.devtoolsEnabled,
});
assert.deepEqual(msg, expected);

View File

@ -4,6 +4,9 @@ build/vs/vs2022.yaml
browser/components/aboutwelcome/content/aboutwelcome.bundle.js
browser/components/aboutwelcome/logs/
browser/components/aboutwelcome/node_modules/
browser/components/asrouter/node_modules/
browser/components/asrouter/content/asrouter-admin.bundle.js
browser/components/asrouter/logs/
browser/components/newtab/content-src/asrouter/schemas/BackgroundTaskMessagingExperiment.schema.json
browser/components/newtab/content-src/asrouter/schemas/MessagingExperiment.schema.json
browser/components/newtab/logs/