Update On Thu Jun 20 20:47:57 CEST 2024

This commit is contained in:
github-action[bot] 2024-06-20 20:47:58 +02:00
parent 0feb4598a2
commit a852ed1946
743 changed files with 13255 additions and 11584 deletions

4
Cargo.lock generated
View file

@ -934,7 +934,7 @@ dependencies = [
[[package]]
name = "coreaudio-sys-utils"
version = "0.1.0"
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=f8a4ec05e5d6200b193eff0b7a5b98e462008c5d#f8a4ec05e5d6200b193eff0b7a5b98e462008c5d"
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=b5dc6e24314babd629118add364bce46f791b4db#b5dc6e24314babd629118add364bce46f791b4db"
dependencies = [
"core-foundation-sys",
"coreaudio-sys",
@ -1182,7 +1182,7 @@ dependencies = [
[[package]]
name = "cubeb-coreaudio"
version = "0.1.0"
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=f8a4ec05e5d6200b193eff0b7a5b98e462008c5d#f8a4ec05e5d6200b193eff0b7a5b98e462008c5d"
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=b5dc6e24314babd629118add364bce46f791b4db#b5dc6e24314babd629118add364bce46f791b4db"
dependencies = [
"atomic",
"audio-mixer",

4
aclocal.m4 vendored
View file

@ -7,14 +7,10 @@ builtin(include, build/autoconf/hooks.m4)dnl
builtin(include, build/autoconf/config.status.m4)dnl
builtin(include, build/autoconf/toolchain.m4)dnl
builtin(include, build/autoconf/altoptions.m4)dnl
builtin(include, build/autoconf/mozprog.m4)dnl
builtin(include, build/autoconf/mozheader.m4)dnl
builtin(include, build/autoconf/compiler-opts.m4)dnl
builtin(include, build/autoconf/arch.m4)dnl
builtin(include, build/autoconf/clang-plugin.m4)dnl
MOZ_PROG_CHECKMSYS()
# Read the user's .mozconfig script. We can't do this in
# configure.in: autoconf puts the argument parsing code above anything
# expanded from configure.in, and we need to get the configure options

View file

@ -3,12 +3,6 @@
* 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 lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
});
export class BrowserTabChild extends JSWindowActorChild {
constructor() {
super();
@ -19,34 +13,6 @@ export class BrowserTabChild extends JSWindowActorChild {
let docShell = context.docShell;
switch (message.name) {
// XXX(nika): Should we try to call this in the parent process instead?
case "Browser:Reload":
/* First, we'll try to use the session history object to reload so
* that framesets are handled properly. If we're in a special
* window (such as view-source) that has no session history, fall
* back on using the web navigation's reload method.
*/
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
try {
if (webNav.sessionHistory) {
webNav = webNav.sessionHistory;
}
} catch (e) {}
let reloadFlags = message.data.flags;
if (message.data.handlingUserInput) {
reloadFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_USER_ACTIVATION;
}
try {
lazy.E10SUtils.wrapHandlingUserInput(
this.document.defaultView,
message.data.handlingUserInput,
() => webNav.reload(reloadFlags)
);
} catch (e) {}
break;
case "ForceEncodingDetection":
docShell.forceEncodingDetection();
break;

View file

@ -1752,8 +1752,9 @@ pref("browser.newtabpage.activity-stream.fxaccounts.endpoint", "https://accounts
pref("browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts", true);
// ASRouter provider configuration
pref("browser.newtabpage.activity-stream.asrouter.providers.cfr", "{\"id\":\"cfr\",\"enabled\":true,\"type\":\"remote-settings\",\"collection\":\"cfr\",\"updateCycleInMs\":3600000}");
pref("browser.newtabpage.activity-stream.asrouter.providers.message-groups", "{\"id\":\"message-groups\",\"enabled\":true,\"type\":\"remote-settings\",\"collection\":\"message-groups\",\"updateCycleInMs\":3600000}");
pref("browser.newtabpage.activity-stream.asrouter.providers.onboarding", "{\"id\":\"onboarding\",\"type\":\"local\",\"localProvider\":\"OnboardingMessageProvider\",\"enabled\":true,\"exclude\":[]}");
pref("browser.newtabpage.activity-stream.asrouter.providers.cfr", "{\"id\":\"cfr\",\"enabled\":true,\"type\":\"remote-settings\",\"collection\":\"cfr\",\"updateCycleInMs\":3600000}");
pref("browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments", "{\"id\":\"messaging-experiments\",\"enabled\":true,\"type\":\"remote-experiments\",\"updateCycleInMs\":3600000}");
// ASRouter user prefs

View file

@ -165,17 +165,33 @@ var BrowserCommands = {
gIdentityHandler.hidePopup();
gPermissionPanel.hidePopup();
const handlingUserInput = document.hasValidTransientUserGestureActivation;
if (document.hasValidTransientUserGestureActivation) {
reloadFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_USER_ACTIVATION;
}
for (const tab of unchangedRemoteness) {
reloadBrowser(tab, reloadFlags);
}
function reloadBrowser(tab) {
if (tab.linkedPanel) {
sendReloadMessage(tab);
const { browsingContext } = tab.linkedBrowser;
const { sessionHistory } = browsingContext;
if (sessionHistory) {
sessionHistory.reload(reloadFlags);
} else {
browsingContext.reload(reloadFlags);
}
} else {
// Shift to fully loaded browser and make
// sure load handler is instantiated.
tab.addEventListener("SSTabRestoring", () => sendReloadMessage(tab), {
once: true,
});
tab.addEventListener(
"SSTabRestoring",
() => tab.linkedBrowser.browsingContext.reload(reloadFlags),
{
once: true,
}
);
gBrowser._insertBrowser(tab);
}
}
@ -186,14 +202,6 @@ var BrowserCommands = {
triggeringPrincipal: principal,
});
}
function sendReloadMessage(tab) {
tab.linkedBrowser.sendMessageToActor(
"Browser:Reload",
{ flags: reloadFlags, handlingUserInput },
"BrowserTab"
);
}
},
stop() {

View file

@ -4,40 +4,6 @@
@namespace html url("http://www.w3.org/1999/xhtml");
/* Rules to help integrate WebExtension buttons */
.webextension-browser-action > .toolbarbutton-badge-stack > .toolbarbutton-icon {
height: 16px;
width: 16px;
}
.webextension-browser-action {
list-style-image: var(--webextension-toolbar-image, inherit);
toolbar[brighttext] & {
list-style-image: var(--webextension-toolbar-image-light, inherit);
}
:root[lwtheme] toolbar:not([brighttext]) & {
list-style-image: var(--webextension-toolbar-image-dark, inherit);
}
toolbaritem:is([overflowedItem="true"], [cui-areatype="panel"]) > & {
list-style-image: var(--webextension-menupanel-image, inherit);
/* TODO: This feels a bit odd, why do we have three images? It feels we
* should probably have only two (light/dark), and choose based on
* prefers-color-scheme + lwt-popup */
:root[lwt-popup="dark"] & {
list-style-image: var(--webextension-menupanel-image-light, inherit);
}
:root[lwt-popup="light"] & {
list-style-image: var(--webextension-menupanel-image-dark, inherit);
}
}
}
.webextension-menuitem {
list-style-image: var(--webextension-menuitem-image, inherit) !important;
}
#reload-button:not([displaystop]) + #stop-button,
#reload-button[displaystop] {
display: none;
@ -128,11 +94,6 @@ menupopup[emptyplacesresult="true"] > .hide-if-empty-places-result {
}
}
:root[customizing=true] .addon-banner-item,
:root[customizing=true] .panel-banner-item {
display: none;
}
/* Firefox View */
:root[firefoxviewhidden] #wrapper-firefox-view-button,
:root[firefoxviewhidden] #firefox-view-button {
@ -151,94 +112,6 @@ menupopup[emptyplacesresult="true"] > .hide-if-empty-places-result {
white-space: pre-line;
}
/* Page action buttons */
.pageAction-panel-button > .toolbarbutton-icon,
.urlbar-page-action {
list-style-image: var(--pageAction-image, inherit);
}
/* Print pending */
.printSettingsBrowser {
width: 250px !important;
}
.previewStack {
background-color: #f9f9fa;
color: #0c0c0d;
}
.previewRendering {
background-repeat: no-repeat;
background-size: 60px 60px;
background-position: center center;
display: flex;
align-items: center;
justify-content: center;
visibility: hidden;
}
.printPreviewBrowser {
visibility: collapse;
opacity: 1;
}
.previewStack[rendering=true] > .previewRendering,
.previewStack[previewtype="source"] > .printPreviewBrowser[previewtype="source"],
.previewStack[previewtype="selection"] > .printPreviewBrowser[previewtype="selection"],
.previewStack[previewtype="simplified"] > .printPreviewBrowser[previewtype="simplified"] {
visibility: inherit;
}
.previewStack[rendering=true] > .printPreviewBrowser {
opacity: 0;
}
.print-pending-label {
margin-top: 110px;
font-size: large;
}
printpreview-pagination {
opacity: 0;
}
printpreview-pagination:focus-within,
.previewStack:hover printpreview-pagination {
opacity: 1;
}
.previewStack[rendering=true] printpreview-pagination {
opacity: 0;
}
@media (prefers-color-scheme: dark) {
.previewStack {
background-color: #2A2A2E;
color: rgb(249, 249, 250);
}
}
@media (prefers-reduced-motion: no-preference) {
.previewRendering {
background-image: url("chrome://browser/skin/tabbrowser/pendingpaint.png");
}
.printPreviewBrowser {
transition: opacity 60ms;
}
.previewStack[rendering=true] > .printPreviewBrowser {
transition: opacity 1ms 250ms;
}
printpreview-pagination {
transition: opacity 100ms 500ms;
}
printpreview-pagination:focus-within,
.previewStack:hover printpreview-pagination {
transition: opacity 100ms;
}
}
/* Screenshots */
#screenshotsPagePanel {
position: relative;

View file

@ -127,14 +127,6 @@
</toolbarbutton>
</panel>
<html:template id="printPreviewStackTemplate">
<stack class="previewStack" rendering="true" flex="1" previewtype="primary">
<vbox class="previewRendering" flex="1">
<h1 class="print-pending-label" data-l10n-id="printui-loading"></h1>
</vbox>
</stack>
</html:template>
<html:template id="screenshotsPagePanelTemplate">
<box id="screenshotsPagePanel" hidden="true">
<screenshots-buttons></screenshots-buttons>

View file

@ -47,6 +47,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
"resource:///modules/FirefoxBridgeExtensionUtils.sys.mjs",
FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs",
FxAccounts: "resource://gre/modules/FxAccounts.sys.mjs",
GenAI: "resource:///modules/GenAI.sys.mjs",
HomePage: "resource:///modules/HomePage.sys.mjs",
Integration: "resource://gre/modules/Integration.sys.mjs",
Interactions: "resource:///modules/Interactions.sys.mjs",
@ -455,6 +456,7 @@ let JSWINDOWACTORS = {
"BackupUI:GetBackupFileInfo": { wantUntrusted: true },
"BackupUI:RestoreFromBackupFile": { wantUntrusted: true },
"BackupUI:RestoreFromBackupChooseFile": { wantUntrusted: true },
"BackupUI:ToggleEncryption": { wantUntrusted: true },
},
},
matches: ["about:preferences*", "about:settings*"],
@ -869,13 +871,7 @@ let JSWINDOWACTORS = {
DOMDocElementInserted: {},
},
},
matches: [
"about:asrouter*",
"about:home*",
"about:newtab*",
"about:welcome*",
"about:privatebrowsing*",
],
matches: ["about:asrouter*", "about:welcome*", "about:privatebrowsing*"],
remoteTypes: ["privilegedabout"],
},
@ -3164,6 +3160,13 @@ BrowserGlue.prototype = {
},
},
{
name: "GenAI.init",
task() {
lazy.GenAI.init();
},
},
{
name: "QuickSuggest.init",
task: () => {

View file

@ -3,7 +3,6 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import { MESSAGE_TYPE_HASH as msg } from "../modules/ActorConstants.mjs";
import { actionCreators as ac } from "../../newtab/common/Actions.mjs";
export const ASRouterUtils = {
addListener(listener) {
@ -46,6 +45,26 @@ export const ASRouterUtils = {
data: { id },
});
},
unblockAll() {
return ASRouterUtils.sendMessage({
type: msg.UNBLOCK_ALL,
});
},
resetGroupImpressions() {
return ASRouterUtils.sendMessage({
type: msg.RESET_GROUPS_STATE,
});
},
resetMessageImpressions() {
return ASRouterUtils.sendMessage({
type: msg.RESET_MESSAGE_STATE,
});
},
resetScreenImpressions() {
return ASRouterUtils.sendMessage({
type: msg.RESET_SCREEN_IMPRESSIONS,
});
},
blockBundle(bundle) {
return ASRouterUtils.sendMessage({
type: msg.BLOCK_BUNDLE,
@ -70,8 +89,17 @@ export const ASRouterUtils = {
data: { [key]: value },
});
},
openPBWindow(content) {
ASRouterUtils.sendMessage({
type: "FORCE_PRIVATE_BROWSING_WINDOW",
data: { message: { content } },
});
},
sendTelemetry(ping) {
return ASRouterUtils.sendMessage(ac.ASRouterUserEvent(ping));
return ASRouterUtils.sendMessage({
type: msg.AS_ROUTER_TELEMETRY_USER_EVENT,
data: ping,
});
},
getPreviewEndpoint() {
return null;

View file

@ -4,68 +4,18 @@
/* stylelint-disable max-nesting-depth */
@import '../../../../newtab/content-src/styles/variables';
@import '../../../../newtab/content-src/styles/theme';
@import '../../../../newtab/content-src/styles/icons';
@import '../Button/Button';
body {
font-family: system-ui;
}
/**
* 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;
}
}
@import 'chrome://global/skin/in-content/common.css';
.asrouter-admin {
$monospace: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Fira Mono',
'Droid Sans Mono', 'Source Code Pro', monospace;
$sidebar-width: 160px;
font-family: system-ui;
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;
}
box-sizing: border-box;
font-size: var(--font-size-root);
padding-inline-start: $sidebar-width + 40px;
.sidebar {
inset-inline-start: 0;
@ -81,180 +31,269 @@ body {
li a {
padding: 10px 34px;
display: block;
color: var(--lwt-sidebar-text-color);
color: var(--in-content-page-color);
border-start-end-radius: 5px;
border-end-end-radius: 5px;
&:hover {
background: var(--newtab-background-color-secondary);
background-color: var(--in-content-button-background-hover);
}
}
}
.main-panel {
margin-block: 12px;
margin-inline-end: 12px;
}
.button-box {
display: flex;
align-items: center;
gap: 8px;
&.baseline {
align-items: baseline;
}
&.vertical {
flex-direction: column;
align-items: stretch;
gap: 0;
input[type='radio'] {
align-self: center;
}
button {
margin-block: 0;
}
}
&:not(.vertical) {
button {
margin-inline: 0;
}
}
button {
padding-block: 0;
min-height: 32px;
min-width: 32px;
}
}
.test-only {
display: block;
visibility: hidden;
overflow: hidden;
width: 0;
height: 0;
min-height: 0;
min-width: 0;
margin: 0;
padding: 0;
box-sizing: border-box;
}
.icon {
$icon-size: 16px;
$smaller-icon-size: 12px;
display: inline-block;
width: $icon-size;
height: $icon-size;
vertical-align: middle;
background-position: center center;
background-repeat: no-repeat;
background-size: $icon-size;
-moz-context-properties: fill;
fill: currentColor;
&.small {
width: $smaller-icon-size;
height: $smaller-icon-size;
background-size: $smaller-icon-size;
}
// helper classes
&.icon-small-spacer {
margin-inline-end: 6px;
}
// icon images
&.icon-info {
background-image: url('chrome://global/skin/icons/info.svg');
}
&.icon-dismiss {
background-image: url('chrome://global/skin/icons/close.svg');
}
&.icon-undo {
background-image: url('chrome://global/skin/icons/undo.svg');
}
&.icon-arrowhead-down {
background-image: url('chrome://global/skin/icons/arrow-down-12.svg');
}
&.icon-arrowhead-forward {
background-image: url('chrome://global/skin/icons/arrow-right-12.svg');
&:dir(rtl) {
background-image: url('chrome://global/skin/icons/arrow-left-12.svg');
}
}
}
h1 {
font-size: 2rem;
font-weight: 200;
font-size: 32px;
}
h2 .button,
p .button {
font-size: 14px;
h2 button,
p button {
font-size: 1rem;
padding: 6px 12px;
margin-inline-start: 5px;
margin-bottom: 0;
}
margin-inline-start: 8px;
margin-block: 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;
&.small {
display: inline-flex;
font-size: var(--font-size-small);
padding: 4px 8px;
margin-inline-start: 8px;
}
}
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);
border: 1px solid var(--in-content-box-border-color);
}
td {
padding: 4px;
&[rowspan] {
border: 1px solid var(--newtab-background-color-secondary);
border: 1px solid var(--in-content-box-border-color);
}
}
}
&.bordered-table {
tr:first-child td {
border-top: 1px solid var(--in-content-box-border-color);
}
td {
vertical-align: top;
padding: 8px;
border-bottom: 1px solid var(--in-content-box-border-color);
&.nowrap {
white-space: nowrap;
}
&:first-child {
border-inline-start: 1px solid var(--in-content-box-border-color);
}
&:last-child {
border-inline-end: 1px solid var(--in-content-box-border-color);
}
}
}
input {
&[type='checkbox'],
&[type='radio'] {
margin: 0;
}
}
}
.sourceLabel {
background: var(--newtab-background-color-secondary);
.sourceLabel:not(:empty) {
background: var(--in-content-box-background);
padding: 2px 5px;
border-radius: 3px;
&.isDisabled {
background: $email-input-invalid;
color: var(--newtab-status-error);
background: rgba(215, 0, 34, 30%);
color: var(--dialog-warning-text-color);
}
}
.messages-list {
display: flex;
flex-flow: column nowrap;
gap: 12px;
margin-block: 12px;
}
.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;
}
}
display: flex;
flex-flow: column nowrap;
gap: 4px;
&.blocked {
.message-id,
.message-summary {
opacity: 0.5;
}
.message-id {
.message-stats,
textarea {
opacity: 0.5;
}
}
.message-id {
pre {
display: flex;
&.collapsed {
display: none;
}
}
.message-textarea {
direction: ltr;
border-radius: 4px;
width: auto;
min-width: 400px;
resize: vertical;
flex-grow: 1;
font-family: $monospace;
font-size: 12px;
margin: 4px 0;
}
}
.providerUrl {
font-size: 12px;
.small-text {
font-size: var(--font-size-small);
}
pre {
background: var(--newtab-background-color-secondary);
margin: 0;
padding: 8px;
font-size: 12px;
max-width: 750px;
overflow: auto;
font-size: var(--font-size-small);
font-family: $monospace;
}
.errorState {
border: $input-error-border;
border-color: var(--dialog-warning-text-color);
outline-color: var(--dialog-warning-text-color);
}
.helpLink {
padding: 10px;
display: flex;
background: $black-10;
background: var(--in-content-box-info-background);
border-radius: 3px;
align-items: center;
width: fit-content;
max-width: 100%;
ul {
margin: 0;
padding-inline-start: 1.25em;
}
a {
text-decoration: underline;
@ -266,12 +305,13 @@ body {
}
}
.ds-component {
margin-bottom: 20px;
}
.modalOverlayInner {
height: 80%;
button.small {
align-items: center;
display: flex;
font-size: var(--font-size-small);
padding: 4px 8px;
min-height: 0;
margin-inline: 0;
}
.clearButton {
@ -279,42 +319,99 @@ body {
padding: 4px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
min-height: 0;
min-width: 0;
}
&:hover {
background: var(--newtab-element-hover-color);
.filters {
margin-block: 8px;
h3 {
margin-block: 8px;
}
.row {
display: flex;
flex-flow: row nowrap;
gap: 40px;
}
.col {
display: flex;
flex-flow: column nowrap;
}
}
.collapsed {
display: none;
moz-toggle::part(label) {
justify-content: revert;
}
.icon {
display: inline-table;
cursor: pointer;
width: 18px;
height: 18px;
.jexl-evaluator-row {
vertical-align: top;
}
.button {
&:disabled,
&:disabled:active {
opacity: 0.5;
cursor: unset;
box-shadow: none;
.jexl-evaluator {
display: flex;
flex-flow: column nowrap;
gap: 8px;
.jexl-evaluator-textareas {
display: flex;
flex-flow: row nowrap;
gap: 8px;
align-items: flex-start;
}
.jexl-evaluator-input {
display: flex;
flex-flow: column nowrap;
gap: 8px;
align-items: start;
}
.jexl-evaluator-output {
display: flex;
flex-flow: column nowrap;
gap: 8px;
align-items: end;
}
}
textarea[readonly] {
color: var(--text-color-deemphasized);
border-color: var(--in-content-border-color);
// Show a text cursor on readonly textareas. Otherwise, they are selectable,
// but there's no cursor to show the caret position.
-moz-user-modify: read-write;
}
.monospace {
font-family: $monospace;
}
input[type='text'].monospace {
min-height: revert;
}
.no-margins,
h2 .no-margins,
p .no-margins {
margin: 0;
}
.impressions-section {
display: flex;
flex-direction: column;
gap: 16px;
gap: 12px;
margin-block: 12px;
.impressions-item {
display: flex;
flex-flow: column nowrap;
padding: 8px;
border: 1px solid $border-color;
border: 1px solid var(--in-content-box-border-color);
border-radius: 5px;
.impressions-inner-box {
@ -324,7 +421,8 @@ body {
}
.impressions-category {
font-size: 1.15em;
font-size: var(--font-size-large);
font-weight: var(--font-weight-bold);
white-space: nowrap;
flex-grow: 0.1;
}
@ -343,9 +441,15 @@ body {
display: flex;
flex-grow: 1.5;
.general-textarea {
textarea {
direction: ltr;
border-radius: 4px;
width: auto;
min-width: 400px;
margin: 0;
resize: vertical;
flex-grow: 1;
font-family: $monospace;
}
}
}

View file

@ -2,7 +2,7 @@
* 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 "../../asrouter-utils";
import { ASRouterUtils } from "../../asrouter-utils.mjs";
import React, {
useState,
useMemo,
@ -134,11 +134,7 @@ const ImpressionsItem = ({
</button>
</div>
<div className="impressions-editor">
<textarea
className="general-textarea"
value={json}
onChange={handleChange}
/>
<textarea rows="15" value={json} onChange={handleChange} />
</div>
</div>
</div>

View file

@ -1,32 +0,0 @@
/* 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";
const ALLOWED_STYLE_TAGS = ["color", "backgroundColor"];
export const Button = props => {
const style = {};
// Add allowed style tags from props, e.g. props.color becomes style={color: props.color}
for (const tag of ALLOWED_STYLE_TAGS) {
if (typeof props[tag] !== "undefined") {
style[tag] = props[tag];
}
}
// remove border if bg is set to something custom
if (style.backgroundColor) {
style.border = "0";
}
return (
<button
onClick={props.onClick}
className={props.className || "ASRouterButton secondary"}
style={style}
>
{props.children}
</button>
);
};

View file

@ -1,51 +0,0 @@
.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 & {
margin-inline-start: 20px;
}
&.test-only {
width: 0;
height: 0;
overflow: hidden;
display: block;
visibility: hidden;
}
&.primary {
border: 1px solid var(--newtab-primary-action-background);
background-color: var(--newtab-primary-action-background);
color: var(--newtab-primary-element-text-color);
&:hover {
background-color: var(--newtab-primary-element-hover-color);
}
&:active {
background-color: var(--newtab-primary-element-active-color);
}
}
&.slim {
border: $border-primary;
margin-inline-start: 0;
font-size: 12px;
padding: 6px 12px;
&:hover,
&:focus {
box-shadow: $shadow-primary;
transition: box-shadow 150ms;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -26,7 +26,6 @@
</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>

View file

@ -2,351 +2,18 @@
* 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 {
color-scheme: light;
--newtab-background-color: #F9F9FB;
--newtab-background-color-secondary: #FFF;
--newtab-text-primary-color: rgb(21, 20, 26);
--newtab-contextual-text-primary-color: light-dark(var(--newtab-text-primary-color), rgb(251, 251, 254));
--newtab-primary-action-background: light-dark(rgb(0, 97, 224), rgb(0, 221, 255));
--newtab-primary-action-background-pocket: rgb(0, 128, 120);
--newtab-text-secondary-color: color-mix(in srgb, var(--newtab-text-primary-color) 70%, transparent);
--newtab-contextual-text-secondary-color: color-mix(in srgb, var(--newtab-contextual-text-primary-color) 70%, transparent);
--newtab-weather-background-color: light-dark(rgba(255, 255, 255, 70%), rgba(35, 34, 43, 70%));
--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-button-background: var(--button-background-color);
--newtab-button-focus-background: var(--newtab-button-background);
--newtab-button-focus-border: var(--focus-outline-color);
--newtab-button-hover-background: var(--button-background-color-hover);
--newtab-button-active-background: var(--button-background-color-active);
--newtab-button-text: var(--button-text-color);
--newtab-button-static-background: light-dark(#F0F0F4, #2B2A33);
--newtab-button-static-focus-background: var(--newtab-button-static-background);
--newtab-button-static-hover-background: light-dark(#E0E0E6, #52525E);
--newtab-button-static-active-background: light-dark(#CFCFD8, #5B5B66);
--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: light-dark(#20123A, rgb(251, 251, 254));
--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] {
color-scheme: dark;
--newtab-background-color: #2B2A33;
--newtab-background-color-secondary: rgb(66, 65, 77);
--newtab-text-primary-color: rgb(251, 251, 254);
--newtab-contextual-text-primary-color: light-dark(rgb(21, 20, 26), var(--newtab-text-primary-color));
--newtab-primary-action-background-pocket: rgb(0, 221, 255);
--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: rgb(43, 42, 51);
--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(--icon-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-info-critical {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-info-critical-16.svg");
}
.icon.icon-help {
background-image: url("chrome://global/skin/icons/help.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-weather {
background-image: url("chrome://browser/skin/weather/sunny.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: system-ui;
}
/**
* 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;
}
@import 'chrome://global/skin/in-content/common.css';
.asrouter-admin {
font-family: system-ui;
max-width: 1300px;
font-size: 14px;
padding-inline-start: 240px;
color: var(--newtab-text-primary-color);
}
.asrouter-admin.collapsed {
display: none;
box-sizing: border-box;
font-size: var(--font-size-root);
padding-inline-start: 200px;
}
.asrouter-admin .sidebar {
inset-inline-start: 0;
position: fixed;
width: 240px;
width: 160px;
}
.asrouter-admin .sidebar ul {
margin: 0;
@ -356,142 +23,208 @@ body {
.asrouter-admin .sidebar li a {
padding: 10px 34px;
display: block;
color: var(--lwt-sidebar-text-color);
color: var(--in-content-page-color);
border-start-end-radius: 5px;
border-end-end-radius: 5px;
}
.asrouter-admin .sidebar li a:hover {
background: var(--newtab-background-color-secondary);
background-color: var(--in-content-button-background-hover);
}
.asrouter-admin .main-panel {
margin-block: 12px;
margin-inline-end: 12px;
}
.asrouter-admin .button-box {
display: flex;
align-items: center;
gap: 8px;
}
.asrouter-admin .button-box.baseline {
align-items: baseline;
}
.asrouter-admin .button-box.vertical {
flex-direction: column;
align-items: stretch;
gap: 0;
}
.asrouter-admin .button-box.vertical input[type=radio] {
align-self: center;
}
.asrouter-admin .button-box.vertical button {
margin-block: 0;
}
.asrouter-admin .button-box:not(.vertical) button {
margin-inline: 0;
}
.asrouter-admin .button-box button {
padding-block: 0;
min-height: 32px;
min-width: 32px;
}
.asrouter-admin .test-only {
display: block;
visibility: hidden;
overflow: hidden;
width: 0;
height: 0;
min-height: 0;
min-width: 0;
margin: 0;
padding: 0;
box-sizing: border-box;
}
.asrouter-admin .icon {
display: inline-block;
width: 16px;
height: 16px;
vertical-align: middle;
background-position: center center;
background-repeat: no-repeat;
background-size: 16px;
-moz-context-properties: fill;
fill: currentColor;
}
.asrouter-admin .icon.small {
width: 12px;
height: 12px;
background-size: 12px;
}
.asrouter-admin .icon.icon-small-spacer {
margin-inline-end: 6px;
}
.asrouter-admin .icon.icon-info {
background-image: url("chrome://global/skin/icons/info.svg");
}
.asrouter-admin .icon.icon-dismiss {
background-image: url("chrome://global/skin/icons/close.svg");
}
.asrouter-admin .icon.icon-undo {
background-image: url("chrome://global/skin/icons/undo.svg");
}
.asrouter-admin .icon.icon-arrowhead-down {
background-image: url("chrome://global/skin/icons/arrow-down-12.svg");
}
.asrouter-admin .icon.icon-arrowhead-forward {
background-image: url("chrome://global/skin/icons/arrow-right-12.svg");
}
.asrouter-admin .icon.icon-arrowhead-forward:dir(rtl) {
background-image: url("chrome://global/skin/icons/arrow-left-12.svg");
}
.asrouter-admin h1 {
font-size: 2rem;
font-weight: 200;
font-size: 32px;
}
.asrouter-admin h2 .button,
.asrouter-admin p .button {
font-size: 14px;
.asrouter-admin h2 button,
.asrouter-admin p button {
font-size: 1rem;
padding: 6px 12px;
margin-inline-start: 5px;
margin-bottom: 0;
margin-inline-start: 8px;
margin-block: 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 {
.asrouter-admin h2 button.small,
.asrouter-admin p button.small {
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;
font-size: var(--font-size-small);
padding: 4px 8px;
margin-inline-start: 8px;
}
.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);
border: 1px solid var(--in-content-box-border-color);
}
.asrouter-admin table.errorReporting td {
padding: 4px;
}
.asrouter-admin table.errorReporting td[rowspan] {
border: 1px solid var(--newtab-background-color-secondary);
border: 1px solid var(--in-content-box-border-color);
}
.asrouter-admin .sourceLabel {
background: var(--newtab-background-color-secondary);
.asrouter-admin table.bordered-table tr:first-child td {
border-top: 1px solid var(--in-content-box-border-color);
}
.asrouter-admin table.bordered-table td {
vertical-align: top;
padding: 8px;
border-bottom: 1px solid var(--in-content-box-border-color);
}
.asrouter-admin table.bordered-table td.nowrap {
white-space: nowrap;
}
.asrouter-admin table.bordered-table td:first-child {
border-inline-start: 1px solid var(--in-content-box-border-color);
}
.asrouter-admin table.bordered-table td:last-child {
border-inline-end: 1px solid var(--in-content-box-border-color);
}
.asrouter-admin table input[type=checkbox], .asrouter-admin table input[type=radio] {
margin: 0;
}
.asrouter-admin .sourceLabel:not(:empty) {
background: var(--in-content-box-background);
padding: 2px 5px;
border-radius: 3px;
}
.asrouter-admin .sourceLabel.isDisabled {
.asrouter-admin .sourceLabel:not(:empty).isDisabled {
background: rgba(215, 0, 34, 0.3);
color: var(--newtab-status-error);
color: var(--dialog-warning-text-color);
}
.asrouter-admin .message-item:first-child td {
border-top: 1px solid var(--newtab-border-color);
.asrouter-admin .messages-list {
display: flex;
flex-flow: column nowrap;
gap: 12px;
margin-block: 12px;
}
.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 {
display: flex;
flex-flow: column nowrap;
gap: 4px;
}
.asrouter-admin .message-item.blocked .message-id,
.asrouter-admin .message-item.blocked .message-summary {
.asrouter-admin .message-item.blocked .message-stats,
.asrouter-admin .message-item.blocked textarea {
opacity: 0.5;
}
.asrouter-admin .message-item.blocked .message-id {
opacity: 0.5;
.asrouter-admin .message-item pre {
display: flex;
}
.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 .message-item pre.collapsed {
display: none;
}
.asrouter-admin .providerUrl {
font-size: 12px;
.asrouter-admin .message-item .message-textarea {
direction: ltr;
border-radius: 4px;
width: auto;
min-width: 400px;
resize: vertical;
flex-grow: 1;
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Code", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace;
margin: 4px 0;
}
.asrouter-admin .small-text {
font-size: var(--font-size-small);
}
.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;
font-size: var(--font-size-small);
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Code", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace;
}
.asrouter-admin .errorState {
border: 1px solid var(--newtab-status-error);
border-color: var(--dialog-warning-text-color);
outline-color: var(--dialog-warning-text-color);
}
.asrouter-admin .helpLink {
padding: 10px;
display: flex;
background: rgba(0, 0, 0, 0.1);
background: var(--in-content-box-info-background);
border-radius: 3px;
align-items: center;
width: fit-content;
max-width: 100%;
}
.asrouter-admin .helpLink ul {
margin: 0;
padding-inline-start: 1.25em;
}
.asrouter-admin .helpLink a {
text-decoration: underline;
@ -500,45 +233,95 @@ body {
min-width: 18px;
min-height: 18px;
}
.asrouter-admin .ds-component {
margin-bottom: 20px;
}
.asrouter-admin .modalOverlayInner {
height: 80%;
.asrouter-admin button.small {
align-items: center;
display: flex;
font-size: var(--font-size-small);
padding: 4px 8px;
min-height: 0;
margin-inline: 0;
}
.asrouter-admin .clearButton {
border: 0;
padding: 4px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
min-height: 0;
min-width: 0;
}
.asrouter-admin .clearButton:hover {
background: var(--newtab-element-hover-color);
.asrouter-admin .filters {
margin-block: 8px;
}
.asrouter-admin .collapsed {
display: none;
.asrouter-admin .filters h3 {
margin-block: 8px;
}
.asrouter-admin .icon {
display: inline-table;
cursor: pointer;
width: 18px;
height: 18px;
.asrouter-admin .filters .row {
display: flex;
flex-flow: row nowrap;
gap: 40px;
}
.asrouter-admin .button:disabled, .asrouter-admin .button:disabled:active {
opacity: 0.5;
cursor: unset;
box-shadow: none;
.asrouter-admin .filters .col {
display: flex;
flex-flow: column nowrap;
}
.asrouter-admin moz-toggle::part(label) {
justify-content: revert;
}
.asrouter-admin .jexl-evaluator-row {
vertical-align: top;
}
.asrouter-admin .jexl-evaluator {
display: flex;
flex-flow: column nowrap;
gap: 8px;
}
.asrouter-admin .jexl-evaluator .jexl-evaluator-textareas {
display: flex;
flex-flow: row nowrap;
gap: 8px;
align-items: flex-start;
}
.asrouter-admin .jexl-evaluator .jexl-evaluator-input {
display: flex;
flex-flow: column nowrap;
gap: 8px;
align-items: start;
}
.asrouter-admin .jexl-evaluator .jexl-evaluator-output {
display: flex;
flex-flow: column nowrap;
gap: 8px;
align-items: end;
}
.asrouter-admin textarea[readonly] {
color: var(--text-color-deemphasized);
border-color: var(--in-content-border-color);
-moz-user-modify: read-write;
}
.asrouter-admin .monospace {
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Code", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace;
}
.asrouter-admin input[type=text].monospace {
min-height: revert;
}
.asrouter-admin .no-margins,
.asrouter-admin h2 .no-margins,
.asrouter-admin p .no-margins {
margin: 0;
}
.asrouter-admin .impressions-section {
display: flex;
flex-direction: column;
gap: 16px;
gap: 12px;
margin-block: 12px;
}
.asrouter-admin .impressions-section .impressions-item {
display: flex;
flex-flow: column nowrap;
padding: 8px;
border: 1px solid var(--newtab-border-color);
border: 1px solid var(--in-content-box-border-color);
border-radius: 5px;
}
.asrouter-admin .impressions-section .impressions-item .impressions-inner-box {
@ -547,7 +330,8 @@ body {
gap: 8px;
}
.asrouter-admin .impressions-section .impressions-item .impressions-category {
font-size: 1.15em;
font-size: var(--font-size-large);
font-weight: var(--font-weight-bold);
white-space: nowrap;
flex-grow: 0.1;
}
@ -563,7 +347,13 @@ body {
display: flex;
flex-grow: 1.5;
}
.asrouter-admin .impressions-section .impressions-item .impressions-editor .general-textarea {
.asrouter-admin .impressions-section .impressions-item .impressions-editor textarea {
direction: ltr;
border-radius: 4px;
width: auto;
min-width: 400px;
margin: 0;
resize: vertical;
flex-grow: 1;
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Code", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace;
}

View file

@ -10,7 +10,7 @@
const { RemoteL10n } = ChromeUtils.importESModule(
"resource:///modules/asrouter/RemoteL10n.sys.mjs"
);
class MozTextParagraph extends HTMLElement {
class MozRemoteText extends HTMLElement {
constructor() {
super();
@ -68,5 +68,5 @@
}
}
customElements.define("remote-text", MozTextParagraph);
customElements.define("remote-text", MozRemoteText);
}

View file

@ -6,6 +6,7 @@ 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/components/remote-text.js (content/components/remote-text.js)
content/browser/asrouter/render.js (content/render.js)
content/browser/asrouter/schemas/BackgroundTaskMessagingExperiment.schema.json (content-src/schemas/BackgroundTaskMessagingExperiment.schema.json)
content/browser/asrouter/schemas/MessagingExperiment.schema.json (content-src/schemas/MessagingExperiment.schema.json)

View file

@ -28,6 +28,7 @@ const { RemoteSettings } = ChromeUtils.importESModule(
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
MESSAGE_TYPE_HASH: "resource:///modules/asrouter/ActorConstants.mjs",
ASRouterPreferences:
"resource:///modules/asrouter/ASRouterPreferences.sys.mjs",
ASRouterTargeting: "resource:///modules/asrouter/ASRouterTargeting.sys.mjs",
@ -66,7 +67,6 @@ ChromeUtils.defineLazyGetter(lazy, "log", () => {
);
return new Logger("ASRouter");
});
import { actionCreators as ac } from "resource://activity-stream/common/Actions.mjs";
import { MESSAGING_EXPERIMENTS_DEFAULT_FEATURES } from "resource:///modules/asrouter/MessagingExperimentConstants.sys.mjs";
import { CFRMessageProvider } from "resource:///modules/asrouter/CFRMessageProvider.sys.mjs";
import { OnboardingMessageProvider } from "resource:///modules/asrouter/OnboardingMessageProvider.sys.mjs";
@ -437,16 +437,15 @@ export const MessageLoaderUtils = {
},
_handleRemoteSettingsUndesiredEvent(event, providerId, dispatchCFRAction) {
if (dispatchCFRAction) {
dispatchCFRAction(
ac.ASRouterUserEvent({
action: "asrouter_undesired_event",
event,
message_id: "n/a",
event_context: providerId,
})
);
}
dispatchCFRAction?.({
type: lazy.MESSAGE_TYPE_HASH.AS_ROUTER_TELEMETRY_USER_EVENT,
data: {
action: "asrouter_undesired_event",
message_id: "n/a",
event,
event_context: providerId,
},
});
},
/**
@ -925,6 +924,10 @@ export class _ASRouter {
if (needsUpdate.length) {
let newState = { messages: [], providers: [] };
for (const provider of this.state.providers) {
if (provider.id === "message-groups") {
// Message groups are handled separately by loadAllMessageGroups
continue;
}
if (needsUpdate.includes(provider)) {
const { messages, lastUpdated, errors } =
await MessageLoaderUtils.loadMessagesForProvider(provider, {
@ -1241,14 +1244,15 @@ export class _ASRouter {
_handleTargetingError(error, message) {
console.error(error);
this.dispatchCFRAction(
ac.ASRouterUserEvent({
message_id: message.id,
this.dispatchCFRAction?.({
type: lazy.MESSAGE_TYPE_HASH.AS_ROUTER_TELEMETRY_USER_EVENT,
data: {
action: "asrouter_undesired_event",
message_id: message.id,
event: "TARGETING_EXPRESSION_ERROR",
event_context: {},
})
);
},
});
}
// Return an object containing targeting parameters used to select messages

View file

@ -315,7 +315,7 @@ export class PageAction {
maybeLoadCustomElement(win) {
if (!win.customElements.get("remote-text")) {
Services.scriptloader.loadSubScript(
"resource://activity-stream/data/custom-elements/paragraph.js",
"chrome://browser/content/asrouter/components/remote-text.js",
win
);
}

View file

@ -132,7 +132,7 @@ export const InfoBar = {
maybeLoadCustomElement(win) {
if (!win.customElements.get("remote-text")) {
Services.scriptloader.loadSubScript(
"resource://activity-stream/data/custom-elements/paragraph.js",
"chrome://browser/content/asrouter/components/remote-text.js",
win
);
}

View file

@ -341,7 +341,7 @@ const MESSAGES = () => [
},
},
},
groups: ["panel-test-provider"],
groups: ["panel-test-provider", "pbNewtab"],
targeting: "region != 'CN' && !hasActiveEnterprisePolicies",
frequency: { lifetime: 3 },
},

View file

@ -11,6 +11,84 @@
export const NimbusRolloutMessageProvider = {
getMessages() {
return [
{
// Nimbus slug: recommend-add-ons-staff-pick-relaunch-treatment-a-rollout:treatment-a
// Version range: 123+
// Recipe: https://experimenter.services.mozilla.com/nimbus/recommend-add-ons-staff-pick-relaunch-treatment-a-rollout/summary#treatment-a
id: "ADDONS_STAFF_PICK",
groups: ["cfr"],
content: {
id: "ADDONS_STAFF_PICK",
screens: [
{
id: "ADDONS_STAFF_PICK_A",
anchors: [
{
selector: "#unified-extensions-button",
arrow_width: "26.9",
panel_position: {
anchor_attachment: "bottomcenter",
callout_attachment: "topright",
},
},
],
content: {
title: {
raw: "Give your browsing a boost",
marginInline: "0 48px",
},
width: "310px",
padding: 16,
position: "callout",
subtitle: {
raw: "Make browsing faster, safer, or just plain fun with Firefox add-ons. See what our staff recommends!",
paddingInline: "34px 0",
},
title_logo: {
width: "24px",
height: "24px",
imageURL:
"https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/173414e1-81f7-4612-b868-e78df0557011.svg",
marginInline: "4px 14px",
},
dismiss_button: {
size: "small",
action: {
dismiss: true,
},
marginBlock: "14px 0",
marginInline: "0 14px",
},
primary_button: {
label: {
raw: "Explore add-ons",
},
action: {
data: {
args: "https://addons.mozilla.org/en-US/firefox/collections/4757633/25c2b44583534b3fa8fea977c419cd/?page=1&collection_sort=-added",
where: "tabshifted",
},
type: "OPEN_URL",
dismiss: true,
},
},
},
},
],
backdrop: "transparent",
template: "multistage",
transitions: false,
},
trigger: {
id: "defaultBrowserCheck",
},
template: "feature_callout",
frequency: {
lifetime: 1,
},
targeting:
"!screenImpressions.AW_AMO_INTRODUCE && !willShowDefaultPrompt && !activeNotifications && source == 'newtab' && !isFirstStartup",
},
{
// Nimbus slug: device-migration-q4-spotlights-remaining-population-esr:treatment (message 1 of 3)
// Recipe: https://experimenter.services.mozilla.com/nimbus/device-migration-q4-spotlights-remaining-population-esr/summary#treatment
@ -432,189 +510,6 @@ export const NimbusRolloutMessageProvider = {
targeting:
"source == 'startup' && !willShowDefaultPrompt && 'browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features'|preferenceValue && !usesFirefoxSync && !hasActiveEnterprisePolicies && userMonthlyActivity[userMonthlyActivity|length - 2][1]|date >= currentDate|date - (28 * 24 * 60 * 60 * 1000) && !(((currentDate|date - profileAgeCreated|date) / 86400000 >= 168) || totalBookmarksCount >= 35) && !(os.isWindows && os.windowsVersion >= 6.1 && os.windowsBuildNumber < 22000)",
},
{
// Nimbus slug: fox-doodle-set-to-default-early-day-user-de-fr-it-treatment-a-rollout:treatment-a
// Version range: 116+
// Recipe: https://experimenter.services.mozilla.com/nimbus/fox-doodle-set-to-default-early-day-user-de-fr-it-treatment-a-rollout/summary#treatment-a
id: "fox-doodle-set-to-default-early-day-user-de-fr-it:A",
groups: ["eco"],
content: {
id: "fox-doodle-set-to-default-early-day-user-de-fr-it:A",
screens: [
{
id: "SET_DEFAULT",
content: {
logo: {
height: "140px",
imageURL:
"https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/05f5265b-d1e4-4fe1-9a46-0ea36f8afced.png",
reducedMotionImageURL:
"https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/05f5265b-d1e4-4fe1-9a46-0ea36f8afced.png",
},
title: {
raw: {
$l10n: {
id: "fox-doodle-trackers-title",
text: "Keep pesky trackers off your tail",
comment:
"This title is displayed together with the picture of a running fox with a long tail. In English, this is a figure of speech meaning 'stop something from following you'. If the localization of this message is challenging, consider using a simplified alternative as a reference for translation: 'Keep unwanted trackers away'.",
},
},
fontSize: "22px",
fontWeight: 590,
paddingBlock: "4px 0",
letterSpacing: 0,
paddingInline: "24px",
},
subtitle: {
raw: {
$l10n: {
id: "fox-doodle-trackers-subtitle",
text: "Say goodbye to annoying ad trackers and settle into a safer, speedy internet experience.",
comment: "",
},
},
fontSize: "15px",
lineHeight: "1.4",
marginBlock: "8px 16px",
letterSpacing: 0,
paddingInline: "24px",
},
dismiss_button: {
action: {
navigate: true,
},
},
primary_button: {
label: {
raw: {
$l10n: {
id: "fox-doodle-set-default-driving-primary-button-label",
text: "Open my links with Firefox",
comment: "",
},
},
marginBlock: "4px 0",
paddingBlock: "0",
paddingInline: "16px",
},
action: {
type: "SET_DEFAULT_BROWSER",
navigate: true,
},
},
secondary_button: {
label: {
raw: {
$l10n: {
id: "fox-doodle-driving-secondary-button-label",
text: "Not now",
comment: "",
},
},
marginBlock: "0 -20px",
},
action: {
navigate: true,
},
},
},
},
],
backdrop: "transparent",
template: "multistage",
transitions: true,
},
trigger: {
id: "defaultBrowserCheck",
},
priority: 1,
template: "spotlight",
frequency: {
lifetime: 1,
},
targeting:
"source == 'startup' && !willShowDefaultPrompt && !isMajorUpgrade && !activeNotifications && (((currentDate|date) - (profileAgeCreated|date)) / 3600000 >= 6) && !isDefaultBrowser",
},
{
// Nimbus slug: fox-doodle-set-to-default-early-day-user-en-treatment-a-rollout:treatment-a
// Version range: 116+
// Recipe: https://experimenter.services.mozilla.com/nimbus/fox-doodle-set-to-default-early-day-user-en-treatment-a-rollout/summary#treatment-a
id: "fox-doodle-set-to-default-early-day-user:A",
groups: ["eco"],
content: {
id: "fox-doodle-set-to-default-early-day-user:A",
screens: [
{
id: "SET_DEFAULT",
content: {
logo: {
height: "140px",
imageURL:
"https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/05f5265b-d1e4-4fe1-9a46-0ea36f8afced.png",
reducedMotionImageURL:
"https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/05f5265b-d1e4-4fe1-9a46-0ea36f8afced.png",
},
title: {
raw: "Keep pesky trackers off your tail",
fontSize: "22px",
fontWeight: 590,
paddingBlock: "4px 0",
letterSpacing: 0,
paddingInline: "24px",
},
subtitle: {
raw: "Say goodbye to annoying ad trackers and settle into a safer, speedy internet experience.",
fontSize: "15px",
lineHeight: "1.4",
marginBlock: "8px 16px",
letterSpacing: 0,
paddingInline: "24px",
},
dismiss_button: {
action: {
navigate: true,
},
},
primary_button: {
label: {
raw: "Open my links with Firefox",
marginBlock: "4px 0",
paddingBlock: "0",
paddingInline: "16px",
},
action: {
type: "SET_DEFAULT_BROWSER",
navigate: true,
},
},
secondary_button: {
label: {
raw: "Not now",
marginBlock: "0 -20px",
},
action: {
navigate: true,
},
},
},
},
],
backdrop: "transparent",
template: "multistage",
transitions: true,
},
trigger: {
id: "defaultBrowserCheck",
},
priority: 1,
template: "spotlight",
frequency: {
lifetime: 1,
},
targeting:
"source == 'startup' && !willShowDefaultPrompt && !isMajorUpgrade && !activeNotifications && (((currentDate|date) - (profileAgeCreated|date)) / 3600000 >= 6) && !isDefaultBrowser",
},
];
},
};

View file

@ -47,6 +47,8 @@ skip-if = [
["browser_feature_callout.js"]
["browser_remote_l10n.js"]
["browser_trigger_listeners.js"]
https_first_disabled = true

View file

@ -562,7 +562,7 @@ describe("ASRouter", () => {
Router.onPrefChange
);
});
it("should send a AS_ROUTER_TARGETING_UPDATE message", async () => {
it("should call clearChildMessages (does nothing, see bug 1899028)", async () => {
const messageTargeted = {
id: "1",
campaign: "foocampaign",
@ -991,14 +991,13 @@ describe("ASRouter", () => {
.rejects("fake error");
await createRouterAndInit();
assert.calledWith(initParams.dispatchCFRAction, {
type: "AS_ROUTER_TELEMETRY_USER_EVENT",
data: {
action: "asrouter_undesired_event",
message_id: "n/a",
event: "ASR_RS_ERROR",
event_context: "remotey-settingsy",
message_id: "n/a",
},
meta: { from: "ActivityStream:Content", to: "ActivityStream:Main" },
type: "AS_ROUTER_TELEMETRY_USER_EVENT",
});
});
it("should dispatch undesired event if RemoteSettings returns no messages", async () => {
@ -1006,14 +1005,13 @@ describe("ASRouter", () => {
.stub(MessageLoaderUtils, "_getRemoteSettingsMessages")
.resolves([]);
assert.calledWith(initParams.dispatchCFRAction, {
type: "AS_ROUTER_TELEMETRY_USER_EVENT",
data: {
action: "asrouter_undesired_event",
message_id: "n/a",
event: "ASR_RS_NO_MESSAGES",
event_context: "remotey-settingsy",
message_id: "n/a",
},
meta: { from: "ActivityStream:Content", to: "ActivityStream:Main" },
type: "AS_ROUTER_TELEMETRY_USER_EVENT",
});
});
it("should download the attachment if RemoteSettings returns some messages", async () => {
@ -1054,14 +1052,13 @@ describe("ASRouter", () => {
await createRouterAndInit([provider]);
assert.calledWith(initParams.dispatchCFRAction, {
type: "AS_ROUTER_TELEMETRY_USER_EVENT",
data: {
action: "asrouter_undesired_event",
message_id: "n/a",
event: "ASR_RS_NO_MESSAGES",
event_context: "ms-language-packs",
message_id: "n/a",
},
meta: { from: "ActivityStream:Content", to: "ActivityStream:Main" },
type: "AS_ROUTER_TELEMETRY_USER_EVENT",
});
});
});

View file

@ -5,7 +5,7 @@ import {
import { ASRouterUtils } from "content-src/asrouter-utils";
import { GlobalOverrider } from "test/unit/utils";
import React from "react";
import { shallow } from "enzyme";
import { mount } from "enzyme";
describe("ASRouterAdmin", () => {
let globalOverrider;
@ -39,8 +39,8 @@ describe("ASRouterAdmin", () => {
ASRouterRemoveParentListener: sandbox.stub(),
};
globalOverrider.set(globals);
wrapper = shallow(<ASRouterAdminInner location={{ routes: [""] }} />);
wrapper.setState({ devtoolsEnabled: true });
wrapper = mount(<ASRouterAdminInner location={{ routes: [""] }} />);
wrapper.setState({ devtoolsEnabled: true, messages: [] });
});
afterEach(() => {
sandbox.restore();
@ -58,17 +58,17 @@ describe("ASRouterAdmin", () => {
});
describe("#getSection", () => {
it("should render a message provider section by default", () => {
assert.equal(wrapper.find("h2").at(1).text(), "Messages");
assert.lengthOf(wrapper.find(".messages-list").at(0), 1);
});
it("should render a targeting section for targeting route", () => {
wrapper = shallow(
wrapper = mount(
<ASRouterAdminInner location={{ routes: ["targeting"] }} />
);
wrapper.setState({ devtoolsEnabled: true });
assert.equal(wrapper.find("h2").at(0).text(), "Targeting Utilities");
assert.lengthOf(wrapper.find(".targeting-table").at(0), 1);
});
it("should render two error messages", () => {
wrapper = shallow(
wrapper = mount(
<ASRouterAdminInner location={{ routes: ["errors"] }} Sections={[]} />
);
wrapper.setState({ devtoolsEnabled: true });
@ -110,8 +110,7 @@ describe("ASRouterAdmin", () => {
providers: FAKE_PROVIDER,
});
// Header + 1 item
assert.lengthOf(wrapper.find(".message-item"), 2);
assert.lengthOf(wrapper.find(`[data-provider]`), 1);
});
});
describe("#renderMessages", () => {
@ -121,11 +120,17 @@ describe("ASRouterAdmin", () => {
sandbox.stub(ASRouterUtils, "overrideMessage").resolves({ foo: "bar" });
sandbox.stub(ASRouterUtils, "sendMessage").resolves();
wrapper.setState({
messageFilter: "all",
filterProviders: [],
filterGroups: [],
filterTemplates: [],
filtersCollapsed: false,
messageBlockList: [],
messageImpressions: { foo: 2 },
groups: [{ id: "messageProvider", enabled: true }],
providers: [{ id: "messageProvider", enabled: true }],
providers: [
{ id: "messageProvider", enabled: true },
{ id: "nullProvider", enabled: true },
],
});
});
it("should render a message when no filtering is applied", () => {
@ -141,8 +146,8 @@ describe("ASRouterAdmin", () => {
assert.lengthOf(wrapper.find(".message-id"), 1);
wrapper.find(".message-item button.primary").simulate("click");
assert.calledOnce(ASRouterUtils.blockById);
assert.calledWith(ASRouterUtils.blockById, "foo");
assert.calledOnce(ASRouterUtils.overrideMessage);
assert.calledWith(ASRouterUtils.overrideMessage, "foo");
});
it("should render a blocked message", () => {
wrapper.setState({
@ -156,16 +161,19 @@ describe("ASRouterAdmin", () => {
messageBlockList: ["foo"],
});
assert.lengthOf(wrapper.find(".message-item.blocked"), 1);
wrapper.find(".message-item.blocked button").simulate("click");
wrapper.find(".message-item.blocked button.primary").simulate("click");
assert.calledOnce(ASRouterUtils.unblockById);
assert.calledWith(ASRouterUtils.unblockById, "foo");
});
it("should render a message if provider matches filter", () => {
it("should render a message if it matches filter", () => {
wrapper.setState({
messageFilter: "messageProvider",
filterProviders: ["messageProvider"],
filterGroups: ["messageProvider"],
filterTemplates: ["bar"],
messages: [
{
id: "foo",
template: "bar",
provider: "messageProvider",
groups: ["messageProvider"],
},
@ -176,7 +184,8 @@ describe("ASRouterAdmin", () => {
});
it("should override with the selected message", async () => {
wrapper.setState({
messageFilter: "messageProvider",
filterProviders: ["messageProvider"],
filterGroups: ["messageProvider"],
messages: [
{
id: "foo",
@ -195,7 +204,8 @@ describe("ASRouterAdmin", () => {
});
it("should hide message if provider filter changes", () => {
wrapper.setState({
messageFilter: "messageProvider",
filterProviders: ["messageProvider"],
filterGroups: ["messageProvider"],
messages: [
{
id: "foo",
@ -207,59 +217,15 @@ describe("ASRouterAdmin", () => {
assert.lengthOf(wrapper.find(".message-id"), 1);
wrapper.find("select").simulate("change", { target: { value: "bar" } });
let ckbx1 = wrapper.find("[data-provider='messageProvider']");
ckbx1.getDOMNode().checked = false;
ckbx1.simulate("change");
let ckbx2 = wrapper.find("[data-provider='nullProvider']");
ckbx2.getDOMNode().checked = true;
ckbx2.simulate("change");
assert.lengthOf(wrapper.find(".message-id"), 0);
});
it("should not display Reset All button if provider filter value is set to all or test providers", () => {
wrapper.setState({
messageFilter: "messageProvider",
messages: [
{
id: "foo",
provider: "messageProvider",
groups: ["messageProvider"],
},
],
});
assert.lengthOf(wrapper.find(".messages-reset"), 1);
wrapper.find("select").simulate("change", { target: { value: "all" } });
assert.lengthOf(wrapper.find(".messages-reset"), 0);
wrapper
.find("select")
.simulate("change", { target: { value: "test_local_testing" } });
assert.lengthOf(wrapper.find(".messages-reset"), 0);
});
it("should trigger disable and enable provider on Reset All button click", () => {
wrapper.setState({
messageFilter: "messageProvider",
messages: [
{
id: "foo",
provider: "messageProvider",
groups: ["messageProvider"],
},
],
providerPrefs: [
{
id: "messageProvider",
},
],
});
wrapper.find(".messages-reset").simulate("click");
assert.calledTwice(ASRouterUtils.sendMessage);
assert.calledWith(ASRouterUtils.sendMessage, {
type: "DISABLE_PROVIDER",
data: "messageProvider",
});
assert.calledWith(ASRouterUtils.sendMessage, {
type: "ENABLE_PROVIDER",
data: "messageProvider",
});
});
});
});
describe("toBinary", () => {

View file

@ -71,6 +71,8 @@ export class BackupUIChild extends JSWindowActorChild {
});
} else if (event.type == "BackupUI:RestoreFromBackupChooseFile") {
this.sendAsyncMessage("RestoreFromBackupChooseFile");
} else if (event.type == "BackupUI:ToggleEncryption") {
this.sendAsyncMessage("ToggleEncryption", event.detail);
}
}

View file

@ -151,6 +151,26 @@ export class BackupUIParent extends JSWindowActorParent {
} else if (message.name == "RestoreFromBackupFile") {
// TODO: Call restore from single-file archive method
// in BackupService once it is implemented in Bug 1890322.
} else if (message.name == "ToggleEncryption") {
let { isEncryptionEnabled } = message.data;
if (!isEncryptionEnabled) {
try {
this.#bs.disableEncryption();
/**
* TODO: (Bug 1901640) after disabling encryption, recreate the backup,
* this time without sensitive data.
*/
} catch (e) {
/**
* TODO: (Bug 1901308) maybe display an error if there is a problem with
* disabling encryption.
*/
return null;
}
return true;
}
}
return null;

View file

@ -2,14 +2,36 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
@import url("chrome://global/skin/in-content/common.css");
:host {
--margin-inline-start-checkbox-content: calc(var(--checkbox-margin-inline) + var(--checkbox-size));
}
#turn-on-scheduled-backups-dialog {
width: 27.8rem;
}
#turn-off-scheduled-backups-dialog {
#turn-off-scheduled-backups-dialog,
#disable-backup-encryption-dialog {
width: 23.94rem;
}
#restore-from-backup-dialog {
width: 29.27rem;
}
#backup-sensitive-data-checkbox {
display: flex;
flex-direction: column;
row-gap: var(--space-xsmall);
}
#backup-sensitive-data-checkbox-label {
display: flex;
align-items: center;
}
#backup-sensitive-data-checkbox-description {
margin-inline-start: var(--margin-inline-start-checkbox-content);
}

View file

@ -11,6 +11,8 @@ import "chrome://browser/content/backup/turn-on-scheduled-backups.mjs";
import "chrome://browser/content/backup/turn-off-scheduled-backups.mjs";
// eslint-disable-next-line import/no-unassigned-import
import "chrome://browser/content/backup/restore-from-backup.mjs";
// eslint-disable-next-line import/no-unassigned-import
import "chrome://browser/content/backup/disable-backup-encryption.mjs";
/**
* The widget for managing the BackupService that is embedded within the main
@ -24,6 +26,8 @@ export default class BackupSettings extends MozLitElement {
static get queries() {
return {
scheduledBackupsButtonEl: "#backup-toggle-scheduled-button",
disableBackupEncryptionEl: "disable-backup-encryption",
disableBackupEncryptionDialogEl: "#disable-backup-encryption-dialog",
turnOnScheduledBackupsDialogEl: "#turn-on-scheduled-backups-dialog",
turnOnScheduledBackupsEl: "turn-on-scheduled-backups",
turnOffScheduledBackupsEl: "turn-off-scheduled-backups",
@ -31,6 +35,7 @@ export default class BackupSettings extends MozLitElement {
restoreFromBackupEl: "restore-from-backup",
restoreFromBackupButtonEl: "#backup-toggle-restore-button",
restoreFromBackupDialogEl: "#restore-from-backup-dialog",
sensitiveDataCheckboxInputEl: "#backup-sensitive-data-checkbox-input",
};
}
@ -50,6 +55,7 @@ export default class BackupSettings extends MozLitElement {
path: "",
iconURL: "",
},
encryptionEnabled: false,
scheduledBackupsEnabled: false,
};
}
@ -67,9 +73,10 @@ export default class BackupSettings extends MozLitElement {
this.addEventListener("turnOnScheduledBackups", this);
this.addEventListener("turnOffScheduledBackups", this);
this.addEventListener("dialogCancel", this);
this.addEventListener("getBackupFileInfo", this);
this.addEventListener("disableEncryption", this);
this.addEventListener("restoreFromBackupConfirm", this);
this.addEventListener("restoreFromBackupChooseFile", this);
this.addEventListener("getBackupFileInfo", this);
}
handleEvent(event) {
@ -106,6 +113,8 @@ export default class BackupSettings extends MozLitElement {
this.turnOffScheduledBackupsDialogEl.close();
} else if (this.restoreFromBackupDialogEl.open) {
this.restoreFromBackupDialogEl.close();
} else if (this.disableBackupEncryptionDialogEl.open) {
this.disableBackupEncryptionDialogEl.close();
}
break;
case "restoreFromBackupConfirm":
@ -140,6 +149,18 @@ export default class BackupSettings extends MozLitElement {
})
);
break;
case "disableEncryption":
this.disableBackupEncryptionDialogEl.close();
this.dispatchEvent(
new CustomEvent("BackupUI:ToggleEncryption", {
bubbles: true,
composed: true,
detail: {
isEncryptionEnabled: false,
},
})
);
break;
}
}
@ -157,6 +178,19 @@ export default class BackupSettings extends MozLitElement {
}
}
handleToggleBackupEncryption(event) {
event.preventDefault();
// Checkbox was unchecked, meaning encryption is already enabled and should be disabled.
let toggledToDisable =
!event.target.checked && this.backupServiceState.encryptionEnabled;
if (toggledToDisable && this.disableBackupEncryptionDialogEl) {
this.disableBackupEncryptionDialogEl.showModal();
}
// TODO: else, show enable encryption dialog (bug 1893295)
}
turnOnScheduledBackupsDialogTemplate() {
let { fileName, path, iconURL } = this.backupServiceState.defaultParent;
return html`<dialog id="turn-on-scheduled-backups-dialog">
@ -204,6 +238,12 @@ export default class BackupSettings extends MozLitElement {
}
}
disableBackupEncryptionDialogTemplate() {
return html`<dialog id="disable-backup-encryption-dialog">
<disable-backup-encryption></disable-backup-encryption>
</dialog>`;
}
render() {
return html`<link
rel="stylesheet"
@ -221,6 +261,7 @@ export default class BackupSettings extends MozLitElement {
${this.turnOnScheduledBackupsDialogTemplate()}
${this.turnOffScheduledBackupsDialogTemplate()}
${this.disableBackupEncryptionDialogTemplate()}
<moz-button
id="backup-toggle-scheduled-button"
@ -229,7 +270,42 @@ export default class BackupSettings extends MozLitElement {
></moz-button>
${this.restoreFromBackupTemplate()}
</div> `;
<!-- TODO: we can use the moz-checkbox reusable component once it is ready (bug 1901635)-->
<div id="backup-sensitive-data-checkbox">
<label
id="backup-sensitive-data-checkbox-label"
for="backup-sensitive-data-checkbox-input"
>
<input
id="backup-sensitive-data-checkbox-input"
@click=${this.handleToggleBackupEncryption}
type="checkbox"
.checked=${this.backupServiceState.encryptionEnabled}
/>
<span
id="backup-sensitive-data-checkbox-span"
data-l10n-id="settings-data-toggle-encryption-label"
></span>
</label>
<div
id="backup-sensitive-data-checkbox-description"
class="text-deemphasized"
>
<span
id="backup-sensitive-data-checkbox-description-span"
data-l10n-id="settings-data-toggle-encryption-description"
></span>
<!--TODO: finalize support page links (bug 1900467)-->
<a
id="settings-data-toggle-encryption-learn-more-link"
is="moz-support-link"
support-page="todo-backup"
data-l10n-id="settings-data-toggle-encryption-support-link"
></a>
</div>
</div>
</div>`;
}
}

View file

@ -57,3 +57,17 @@ ScheduledBackupsEnabled.args = {
scheduledBackupsEnabled: true,
},
};
export const EncryptionEnabled = Template.bind({});
EncryptionEnabled.args = {
backupServiceState: {
backupDirPath: "/Some/User/Documents",
backupInProgress: false,
defaultParent: {
path: "/Some/User/Documents",
fileName: "Documents",
},
scheduledBackupsEnabled: true,
encryptionEnabled: true,
},
};

View file

@ -0,0 +1,32 @@
/* 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 https://mozilla.org/MPL/2.0/. */
@import url("chrome://global/skin/in-content/common.css");
#backup-disable-encryption-wrapper {
display: grid;
grid-template-areas:
"header"
"content"
"button-group";
grid-template-rows: auto auto auto;
}
#backup-disable-encryption-header {
grid-area: header;
margin: 0;
}
#backup-disable-encryption-content {
display: flex;
flex-direction: column;
grid-area: content;
margin-block-start: var(--space-small);
margin-block-end: var(--space-large);
row-gap: var(--space-large);
}
#backup-disable-encryption-button-group {
grid-area: button-group;
}

View file

@ -0,0 +1,106 @@
/* 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 { html } from "chrome://global/content/vendor/lit.all.mjs";
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
/**
* The widget for disabling password protection if the backup is already
* encrypted.
*/
export default class DisableBackupEncryption extends MozLitElement {
static get queries() {
return {
cancelButtonEl: "#backup-disable-encryption-cancel-button",
confirmButtonEl: "#backup-disable-encryption-confirm-button",
};
}
/**
* Dispatches the BackupUI:InitWidget custom event upon being attached to the
* DOM, which registers with BackupUIChild for BackupService state updates.
*/
connectedCallback() {
super.connectedCallback();
this.dispatchEvent(
new CustomEvent("BackupUI:InitWidget", { bubbles: true })
);
}
handleCancel() {
this.dispatchEvent(
new CustomEvent("dialogCancel", {
bubbles: true,
composed: true,
})
);
}
handleConfirm() {
this.dispatchEvent(
new CustomEvent("disableEncryption", {
bubbles: true,
composed: true,
})
);
}
contentTemplate() {
return html`
<div
id="backup-disable-encryption-wrapper"
aria-labelledby="backup-disable-encryption-header"
aria-describedby="backup-disable-encryption-description"
>
<h1
id="backup-disable-encryption-header"
class="heading-medium"
data-l10n-id="disable-backup-encryption-header"
></h1>
<main id="backup-disable-encryption-content">
<div id="backup-disable-encryption-description">
<span
id="backup-disable-encryption-description-span"
data-l10n-id="disable-backup-encryption-description"
>
<!--TODO: finalize support page links (bug 1900467)-->
</span>
<a
id="backup-disable-encryption-learn-more-link"
is="moz-support-link"
support-page="todo-backup"
data-l10n-id="disable-backup-encryption-support-link"
></a>
</div>
</main>
<moz-button-group id="backup-disable-encryption-button-group">
<moz-button
id="backup-disable-encryption-cancel-button"
@click=${this.handleCancel}
data-l10n-id="disable-backup-encryption-cancel-button"
></moz-button>
<moz-button
id="backup-disable-encryption-confirm-button"
@click=${this.handleConfirm}
type="primary"
data-l10n-id="disable-backup-encryption-confirm-button"
></moz-button>
</moz-button-group>
</div>
`;
}
render() {
return html`
<link
rel="stylesheet"
href="chrome://browser/content/backup/disable-backup-encryption.css"
/>
${this.contentTemplate()}
`;
}
}
customElements.define("disable-backup-encryption", DisableBackupEncryption);

View file

@ -0,0 +1,25 @@
/* 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/. */
// eslint-disable-next-line import/no-unresolved
import { html } from "lit.all.mjs";
import "chrome://global/content/elements/moz-card.mjs";
import "./disable-backup-encryption.mjs";
window.MozXULElement.insertFTLIfNeeded("locales-preview/backupSettings.ftl");
window.MozXULElement.insertFTLIfNeeded("branding/brand.ftl");
export default {
title: "Domain-specific UI Widgets/Backup/Disable Encryption",
component: "disable-backup-encryption",
argTypes: {},
};
const Template = () => html`
<moz-card style="width: 23.94rem;">
<disable-backup-encryption></disable-backup-encryption>
</moz-card>
`;
export const Default = Template.bind({});

View file

@ -10,6 +10,8 @@ browser.jar:
content/browser/backup/BackupManifest.1.schema.json (content/BackupManifest.1.schema.json)
content/browser/backup/backup-settings.css (content/backup-settings.css)
content/browser/backup/backup-settings.mjs (content/backup-settings.mjs)
content/browser/backup/disable-backup-encryption.css (content/disable-backup-encryption.css)
content/browser/backup/disable-backup-encryption.mjs (content/disable-backup-encryption.mjs)
content/browser/backup/turn-off-scheduled-backups.css (content/turn-off-scheduled-backups.css)
content/browser/backup/turn-off-scheduled-backups.mjs (content/turn-off-scheduled-backups.mjs)
content/browser/backup/turn-on-scheduled-backups.css (content/turn-on-scheduled-backups.css)

View file

@ -4,6 +4,9 @@ prefs = [
"browser.backup.preferences.ui.enabled=true",
"browser.backup.scheduled.enabled=false",
]
support-files = [
"head.js",
]
["browser_settings.js"]

View file

@ -3,8 +3,6 @@
"use strict";
const { MockFilePicker } = SpecialPowers;
add_setup(async () => {
MockFilePicker.init(window.browsingContext);
registerCleanupFunction(() => {
@ -48,6 +46,67 @@ add_task(async function test_preferences_visibility() {
await SpecialPowers.popPrefEnv();
});
/**
* Tests that the disable-backup-encryption dialog can disable encryption
* from the settings page.
*/
add_task(async function test_disable_backup_encryption_confirm() {
await BrowserTestUtils.withNewTab("about:preferences", async browser => {
let sandbox = sinon.createSandbox();
let disableEncryptionStub = sandbox
.stub(BackupService.prototype, "disableEncryption")
.resolves(true);
let settings = browser.contentDocument.querySelector("backup-settings");
/**
* For this test, we can pretend that browser-settings receives a backupServiceState
* with encryptionEnable set to true. Normally, Lit only detects reactive property updates if a
* property's reference changes (ex. completely replace backupServiceState with a new object),
* which we actually do after calling BackupService.stateUpdate() and BackupUIParent.sendState().
*
* Since we only care about encryptionEnabled, we can just call Lit's requestUpdate() to force
* the update explicitly.
*/
settings.backupServiceState.encryptionEnabled = true;
await settings.requestUpdate();
await settings.updateComplete;
let sensitiveDataCheckbox = settings.sensitiveDataCheckboxInputEl;
Assert.ok(sensitiveDataCheckbox, "Sensitive data checkbox should be found");
Assert.ok(
sensitiveDataCheckbox.checked,
"Sensitive data checkbox should be checked"
);
let disableBackupEncryption = settings.disableBackupEncryptionEl;
Assert.ok(
disableBackupEncryption,
"disable-backup-encryption should be found"
);
let confirmButton = disableBackupEncryption.confirmButtonEl;
let promise = BrowserTestUtils.waitForEvent(window, "disableEncryption");
Assert.ok(confirmButton, "Confirm button should be found");
confirmButton.click();
await promise;
await settings.updateComplete;
Assert.ok(
disableEncryptionStub.calledOnce,
"BackupService was called to disable encryption"
);
sandbox.restore();
});
});
/**
* Tests that the turn off scheduled backups dialog can set
* browser.backup.scheduled.enabled to false from the settings page.

View file

@ -3,16 +3,6 @@
"use strict";
const { BackupService } = ChromeUtils.importESModule(
"resource:///modules/backup/BackupService.sys.mjs"
);
const { MockFilePicker } = SpecialPowers;
const { sinon } = ChromeUtils.importESModule(
"resource://testing-common/Sinon.sys.mjs"
);
const SCHEDULED_BACKUPS_ENABLED_PREF = "browser.backup.scheduled.enabled";
add_setup(async () => {

View file

@ -0,0 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { BackupService } = ChromeUtils.importESModule(
"resource:///modules/backup/BackupService.sys.mjs"
);
const { MockFilePicker } = SpecialPowers;
const { sinon } = ChromeUtils.importESModule(
"resource://testing-common/Sinon.sys.mjs"
);

View file

@ -6,6 +6,8 @@ skip-if = ["os == 'android'"]
["test_backup_settings.html"]
["test_disable_backup_encryption.html"]
["test_restore_from_backup.html"]
["test_turn_off_scheduled_backups.html"]

View file

@ -75,7 +75,7 @@
* Tests that the dialog for turning off scheduled backups can be displayed
* from settings, or hidden if cancelled.
*/
add_task(async function test_turnOffScheduledBackupsDialog() {
add_task(async function test_turnOffScheduledBackupsDialog() {
let settings = document.getElementById("test-backup-settings");
const testDefaultName = "test-default-path";
settings.backupServiceState = {
@ -110,6 +110,47 @@
ok(!dialog.open, "Dialog should not be open");
});
/**
* Tests that the dialog for turning off scheduled backups can be displayed
* from settings, or hidden if cancelled.
*/
add_task(async function test_disableBackupEncryptionDialog() {
let settings = document.getElementById("test-backup-settings");
const testDefaultName = "test-default-path";
settings.backupServiceState = {
defaultParent: {
path: PathUtils.join(PathUtils.tempDir, testDefaultName),
fileName: testDefaultName,
},
scheduledBackupsEnabled: true,
encryptionEnabled: true,
}
await settings.updateComplete;
let sensitiveDataCheckboxInput = settings.sensitiveDataCheckboxInputEl;
let dialog = settings.disableBackupEncryptionDialogEl;
ok(sensitiveDataCheckboxInput, "Checkbox for toggling encryption should be found");
ok(!dialog.open, "Dialog should not be open");
sensitiveDataCheckboxInput.click();
await settings.updateComplete;
ok(dialog?.open, "Dialog should be open");
let disableBackupEncryption = dialog.querySelector("disable-backup-encryption");
ok(disableBackupEncryption, "disable-backup-encryption should be found");
let cancelButton = disableBackupEncryption.shadowRoot.getElementById("backup-disable-encryption-cancel-button");
ok(cancelButton, "Cancel button should be found");
cancelButton.click();
await settings.updateComplete;
ok(!dialog.open, "Dialog should not be open");
});
</script>
</head>
<body>

View file

@ -0,0 +1,79 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Tests for the disable-backup-encryption component</title>
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script
src="chrome://browser/content/backup/disable-backup-encryption.mjs"
type="module"
></script>
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
<script>
const { BrowserTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/BrowserTestUtils.sys.mjs"
);
/**
* Tests that adding a disable-backup-encryption element to the DOM causes it to
* fire a BackupUI:InitWidget event.
*/
add_task(async function test_initWidget() {
let disableBackupEncryption = document.createElement("disable-backup-encryption");
let content = document.getElementById("content");
let sawInitWidget = BrowserTestUtils.waitForEvent(content, "BackupUI:InitWidget");
content.appendChild(disableBackupEncryption);
await sawInitWidget;
ok(true, "Saw BackupUI:InitWidget");
disableBackupEncryption.remove();
});
/**
* Tests that pressing the confirm button will dispatch the expected events.
*/
add_task(async function test_confirm() {
let disableBackupEncryption = document.getElementById("test-disable-backup-encryption");
let confirmButton = disableBackupEncryption.confirmButtonEl;
ok(confirmButton, "Confirm button should be found");
let content = document.getElementById("content");
let promise = BrowserTestUtils.waitForEvent(content, "disableEncryption");
confirmButton.click()
await promise;
ok(true, "Detected event after selecting the confirm button");
})
/**
* Tests that pressing the cancel button will dispatch the expected events.
*/
add_task(async function test_cancel() {
let disableBackupEncryption = document.getElementById("test-disable-backup-encryption");
let cancelButton = disableBackupEncryption.cancelButtonEl;
ok(cancelButton, "Cancel button should be found");
let content = document.getElementById("content");
let promise = BrowserTestUtils.waitForEvent(content, "dialogCancel");
cancelButton.click()
await promise;
ok(true, "Detected event after selecting the cancel button");
})
</script>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
<disable-backup-encryption id="test-disable-backup-encryption"></disable-backup-encryption>
</div>
<pre id="test"></pre>
</body>
</html>

View file

@ -996,7 +996,8 @@ class Window extends WindowBase {
}
get alwaysOnTop() {
return this.appWindow.zLevel >= Ci.nsIAppWindow.raisedZ;
// We never create alwaysOnTop browser windows.
return false;
}
get isLastFocused() {

View file

@ -11,33 +11,32 @@ async function verifyTitle(win, test, desc) {
}
add_task(async function testWindowGetAll() {
let raisedWin = Services.ww.openWindow(
let secondWin = Services.ww.openWindow(
null,
AppConstants.BROWSER_CHROME_URL,
"_blank",
"chrome,dialog=no,all,alwaysRaised",
"chrome,dialog=no,all",
null
);
await TestUtils.topicObserved(
"browser-delayed-startup-finished",
subject => subject == raisedWin
subject => subject == secondWin
);
let extension = ExtensionTestUtils.loadExtension({
background: async function () {
let wins = await browser.windows.getAll();
browser.test.assertEq(2, wins.length, "Expect two windows");
browser.test.assertEq(
false,
wins[0].alwaysOnTop,
"Expect first window not to be always on top"
);
browser.test.assertEq(
true,
false,
wins[1].alwaysOnTop,
"Expect first window to be always on top"
"Expect second window not to be always on top"
);
let win = await browser.windows.create({
@ -70,7 +69,7 @@ add_task(async function testWindowGetAll() {
await extension.awaitFinish("getAll");
await extension.unload();
await BrowserTestUtils.closeWindow(raisedWin);
await BrowserTestUtils.closeWindow(secondWin);
});
add_task(async function testWindowTitle() {

View file

@ -12,6 +12,12 @@ XPCOMUtils.defineLazyPreferenceGetter(
"chatEnabled",
"browser.ml.chat.enabled"
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"chatOpenSidebarOnProviderChange",
"browser.ml.chat.openSidebarOnProviderChange",
true
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"chatPromptPrefix",
@ -20,7 +26,9 @@ XPCOMUtils.defineLazyPreferenceGetter(
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"chatProvider",
"browser.ml.chat.provider"
"browser.ml.chat.provider",
null,
(_pref, _old, val) => onChatProviderChange(val)
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
@ -31,6 +39,17 @@ XPCOMUtils.defineLazyPreferenceGetter(
export const GenAI = {
chatProviders: new Map(),
/**
* Handle startup tasks like telemetry, adding listeners.
*/
init() {
// Access this getter for its side effect of observing provider pref change
lazy.chatProvider;
// Detect about:preferences to add controls
Services.obs.addObserver(this, "experimental-pane-loaded");
},
/**
* Build prompts menu to ask chat for context menu or popup.
*
@ -88,23 +107,44 @@ export const GenAI = {
* @param {Event} event from menu command
*/
async handleAskChat({ target }) {
// TODO bug 1902449 to make this less context-menu specific
const win = target.ownerGlobal;
const { selectedTab } = win.gBrowser;
const { gBrowser, SidebarController } = win;
const { selectedTab } = gBrowser;
const prompt = this.buildChatPrompt(target, {
currentTabTitle:
(selectedTab._labelIsContentTitle && selectedTab.label) || "",
selection: target.closest("menu").context.selectionInfo.fullText ?? "",
});
// Pass the prompt via GET url ?q= param or request header
const { header } = this.chatProviders.get(lazy.chatProvider) ?? {};
const url = new URL(lazy.chatProvider);
url.searchParams.set(
"q",
this.buildChatPrompt(target, {
currentTabTitle:
(selectedTab._labelIsContentTitle && selectedTab.label) || "",
selection: target.closest("menu").context.selectionInfo.fullText ?? "",
})
);
if (lazy.chatSidebar) {
await win.SidebarController.show("viewGenaiChatSidebar");
win.SidebarController.browser.contentWindow.request(url);
const options = {
inBackground: false,
relatedToCurrent: true,
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
{}
),
};
if (header) {
options.headers = Cc[
"@mozilla.org/io/string-input-stream;1"
].createInstance(Ci.nsIStringInputStream);
options.headers.data = `${header}: ${encodeURIComponent(prompt)}\r\n`;
} else {
win.openWebLinkIn(url + "", "tab", { relatedToCurrent: true });
url.searchParams.set("q", prompt);
}
// Get the desired browser to handle the prompt url request
let browser;
if (lazy.chatSidebar) {
await SidebarController.show("viewGenaiChatSidebar");
browser = await SidebarController.browser.contentWindow.browserPromise;
} else {
browser = gBrowser.addTab("", options).linkedBrowser;
}
browser.fixupAndLoadURIString(url, options);
},
/**
@ -126,4 +166,22 @@ export const GenAI = {
// TODO bug 1895433 populate providers
Preferences.add({ id: "browser.ml.chat.provider", type: "string" });
},
// nsIObserver
observe(window) {
this.buildPreferences(window);
},
};
/**
* Ensure the chat sidebar is shown to reflect changed provider.
*
* @param {string} value New pref value
*/
function onChatProviderChange(value) {
if (value && lazy.chatEnabled && lazy.chatOpenSidebarOnProviderChange) {
Services.wm
.getMostRecentWindow("navigator:browser")
?.SidebarController.show("viewGenaiChatSidebar");
}
}

View file

@ -87,11 +87,18 @@ function handleChange({ target }) {
break;
}
}
async function handleLoad() {
node.chat = renderChat();
node.provider = await renderProviders();
}
addEventListener("change", handleChange);
addEventListener("load", handleLoad);
// Expose a promise for loading and rendering the chat browser element
var browserPromise = new Promise((resolve, reject) => {
addEventListener("load", async () => {
try {
node.chat = renderChat();
node.provider = await renderProviders();
resolve(node.chat);
} catch (ex) {
console.error("Failed to render on load", ex);
reject(ex);
}
});
});

View file

@ -1,2 +1,10 @@
[DEFAULT]
prefs = [
"browser.ml.chat.enabled=true",
"browser.ml.chat.openSidebarOnProviderChange=false",
]
["browser_chat_contextmenu.js"]
["browser_chat_request.js"]
["browser_chat_sidebar.js"]
["browser_genai_init.js"]

View file

@ -41,10 +41,7 @@ add_task(async function test_hidden_menu() {
*/
add_task(async function test_menu_enabled() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.ml.chat.enabled", true],
["browser.ml.chat.provider", "http://localhost:8080"],
],
set: [["browser.ml.chat.provider", "http://localhost:8080"]],
});
await BrowserTestUtils.withNewTab("about:blank", async () => {
await openContextMenu();
@ -62,7 +59,6 @@ add_task(async function test_menu_enabled() {
add_task(async function test_open_tab() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.ml.chat.enabled", true],
["browser.ml.chat.provider", "http://localhost:8080"],
["browser.ml.chat.sidebar", false],
],
@ -87,7 +83,6 @@ add_task(async function test_open_tab() {
add_task(async function test_open_sidebar() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.ml.chat.enabled", true],
["browser.ml.chat.provider", "http://localhost:8080"],
["browser.ml.chat.sidebar", true],
],

View file

@ -0,0 +1,57 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const { GenAI } = ChromeUtils.importESModule(
"resource:///modules/GenAI.sys.mjs"
);
const { HttpServer } = ChromeUtils.importESModule(
"resource://testing-common/httpd.sys.mjs"
);
const { sinon } = ChromeUtils.importESModule(
"resource://testing-common/Sinon.sys.mjs"
);
/**
* Check that prompts can be sent with header
*/
add_task(async function test_chat_header() {
const server = new HttpServer();
const requestPromise = new Promise(resolve => {
server.registerPathHandler("/", resolve);
});
server.start(-1);
const url = `http://localhost:${server.identity.primaryPort}`;
const sandbox = sinon.createSandbox();
sandbox
.stub(GenAI, "chatProviders")
.value(new Map([[url, { header: "X-Prompt" }]]));
sandbox.stub(GenAI, "buildChatPrompt").returns("hello world?");
await SpecialPowers.pushPrefEnv({
set: [
["browser.ml.chat.provider", url],
["browser.ml.chat.sidebar", false],
],
});
await GenAI.handleAskChat({
target: {
closest() {
return { context: { selectionInfo: {} } };
},
ownerGlobal: window,
},
});
const request = await requestPromise;
Assert.equal(
request.getHeader("x-prompt"),
"hello%20world%3F",
"Prompt passed via header"
);
Assert.equal(request.queryString, "", "Prompt not passed via ?q");
gBrowser.removeTab(gBrowser.selectedTab);
sandbox.restore();
server.stop();
});

View file

@ -6,10 +6,7 @@
*/
add_task(async function test_sidebar_render() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.ml.chat.enabled", true],
["browser.ml.chat.provider", "http://mochi.test:8888"],
],
set: [["browser.ml.chat.provider", "http://mochi.test:8888"]],
});
await SidebarController.show("viewGenaiChatSidebar");

View file

@ -0,0 +1,52 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const { GenAI } = ChromeUtils.importESModule(
"resource:///modules/GenAI.sys.mjs"
);
const { sinon } = ChromeUtils.importESModule(
"resource://testing-common/Sinon.sys.mjs"
);
/**
* Check that chat sidebar auto opens
*/
add_task(async function test_chat_autoopen() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.ml.chat.openSidebarOnProviderChange", true],
["browser.ml.chat.provider", "http://localhost:8080"],
],
});
Assert.ok(SidebarController.isOpen, "Pref change opened sidebar");
SidebarController.hide();
});
/**
* Check that chat sidebar doesn't open if disabled
*/
add_task(async function test_chat_no_open() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.ml.chat.openSidebarOnProviderChange", false],
["browser.ml.chat.provider", "http://localhost:8080"],
],
});
Assert.ok(!SidebarController.isOpen, "Pref changes didn't open sidebar");
});
/**
* Check that about:preferences is detected
*/
add_task(async function test_preferences_observer() {
const sandbox = sinon.createSandbox();
const stub = sandbox.stub(GenAI, "buildPreferences");
await BrowserTestUtils.withNewTab("about:preferences#experimental", () => {
Assert.equal(stub.callCount, 1, "Would have built genai preferences");
});
sandbox.restore();
});

View file

@ -23,8 +23,6 @@ module.exports = {
{
// These files use fluent-dom to insert content
files: [
"content-src/asrouter/templates/OnboardingMessage/**",
"content-src/asrouter/templates/FirstRun/**",
"content-src/components/TopSites/**",
"content-src/components/MoreRecommendations/MoreRecommendations.jsx",
"content-src/components/CollapsibleSection/CollapsibleSection.jsx",

View file

@ -30,10 +30,6 @@ for (const type of [
"ADDONS_INFO_REQUEST",
"ADDONS_INFO_RESPONSE",
"ARCHIVE_FROM_POCKET",
"AS_ROUTER_INITIALIZED",
"AS_ROUTER_PREF_CHANGED",
"AS_ROUTER_TARGETING_UPDATE",
"AS_ROUTER_TELEMETRY_USER_EVENT",
"BLOCK_URL",
"BOOKMARK_URL",
"CLEAR_PREF",
@ -325,20 +321,6 @@ function DiscoveryStreamUserEvent(data) {
});
}
/**
* ASRouterUserEvent - A telemetry ping indicating a user action from AS router. This should only
* be sent from the UI during a user session.
*
* @param {object} data Fields to include in the ping (source, etc.)
* @return {object} An AlsoToMain action
*/
function ASRouterUserEvent(data) {
return AlsoToMain({
type: actionTypes.AS_ROUTER_TELEMETRY_USER_EVENT,
data,
});
}
/**
* ImpressionStats - A telemetry ping indicating an impression stats.
*
@ -412,7 +394,6 @@ export const actionCreators = {
BroadcastToContent,
UserEvent,
DiscoveryStreamUserEvent,
ASRouterUserEvent,
ImpressionStats,
AlsoToOneContent,
OnlyToOneContent,

View file

@ -19,7 +19,6 @@ export const INITIAL_STATE = {
isForStartupCache: false,
customizeMenuVisible: false,
},
ASRouter: { initialized: false },
TopSites: {
// Have we received real data from history yet?
initialized: false,
@ -148,15 +147,6 @@ function App(prevState = INITIAL_STATE.App, action) {
}
}
function ASRouter(prevState = INITIAL_STATE.ASRouter, action) {
switch (action.type) {
case at.AS_ROUTER_INITIALIZED:
return { ...action.data, initialized: true };
default:
return prevState;
}
}
/**
* insertPinned - Inserts pinned links in their specified slots
*
@ -909,7 +899,6 @@ function Weather(prevState = INITIAL_STATE.Weather, action) {
export const reducers = {
TopSites,
App,
ASRouter,
Prefs,
Dialog,
Sections,

View file

@ -186,7 +186,4 @@ input {
@import '../components/DiscoveryStreamComponents/FeatureHighlight/FeatureHighlight';
@import '../components/DiscoveryStreamComponents/FeatureHighlight/SponsoredContentHighlight';
@import '../components/DiscoveryStreamComponents/FeatureHighlight/WallpaperFeatureHighlight';
// AS Router
@import '../../../asrouter/content-src/components/Button/Button';
// stylelint-enable no-invalid-position-at-import-rule

View file

@ -5211,47 +5211,3 @@ main section {
background-color: var(--button-background-color);
margin-block-start: var(--space-small);
}
.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;
}

View file

@ -5215,47 +5215,3 @@ main section {
background-color: var(--button-background-color);
margin-block-start: var(--space-small);
}
.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;
}

View file

@ -5211,47 +5211,3 @@ main section {
background-color: var(--button-background-color);
margin-block-start: var(--space-small);
}
.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;
}

View file

@ -103,10 +103,6 @@ for (const type of [
"ADDONS_INFO_REQUEST",
"ADDONS_INFO_RESPONSE",
"ARCHIVE_FROM_POCKET",
"AS_ROUTER_INITIALIZED",
"AS_ROUTER_PREF_CHANGED",
"AS_ROUTER_TARGETING_UPDATE",
"AS_ROUTER_TELEMETRY_USER_EVENT",
"BLOCK_URL",
"BOOKMARK_URL",
"CLEAR_PREF",
@ -398,20 +394,6 @@ function DiscoveryStreamUserEvent(data) {
});
}
/**
* ASRouterUserEvent - A telemetry ping indicating a user action from AS router. This should only
* be sent from the UI during a user session.
*
* @param {object} data Fields to include in the ping (source, etc.)
* @return {object} An AlsoToMain action
*/
function ASRouterUserEvent(data) {
return AlsoToMain({
type: actionTypes.AS_ROUTER_TELEMETRY_USER_EVENT,
data,
});
}
/**
* ImpressionStats - A telemetry ping indicating an impression stats.
*
@ -485,7 +467,6 @@ const actionCreators = {
BroadcastToContent,
UserEvent,
DiscoveryStreamUserEvent,
ASRouterUserEvent,
ImpressionStats,
AlsoToOneContent,
OnlyToOneContent,
@ -5569,7 +5550,6 @@ const INITIAL_STATE = {
isForStartupCache: false,
customizeMenuVisible: false,
},
ASRouter: { initialized: false },
TopSites: {
// Have we received real data from history yet?
initialized: false,
@ -5698,15 +5678,6 @@ function App(prevState = INITIAL_STATE.App, action) {
}
}
function ASRouter(prevState = INITIAL_STATE.ASRouter, action) {
switch (action.type) {
case actionTypes.AS_ROUTER_INITIALIZED:
return { ...action.data, initialized: true };
default:
return prevState;
}
}
/**
* insertPinned - Inserts pinned links in their specified slots
*
@ -6459,7 +6430,6 @@ function Weather(prevState = INITIAL_STATE.Weather, action) {
const reducers = {
TopSites,
App,
ASRouter,
Prefs,
Dialog,
Sections,

View file

@ -25,7 +25,6 @@ browser.jar:
content/activity-stream/data/content/tippytop/ (./data/content/tippytop/*)
res/activity-stream/data/content/activity-stream.bundle.js (./data/content/activity-stream.bundle.js)
res/activity-stream/data/content/newtab-render.js (./data/content/newtab-render.js)
res/activity-stream/data/custom-elements/ (./components/CustomElements/*)
#ifdef XP_MACOSX
content/activity-stream/css/activity-stream.css (./css/activity-stream-mac.css)
#elifdef XP_WIN

View file

@ -186,12 +186,6 @@ module.exports = function (config) {
functions: 85.71,
branches: 68.75,
},
"content-src/asrouter/**/*.jsx": {
statements: 57,
lines: 58,
functions: 60,
branches: 50,
},
/**
* WallpaperSection.jsx is tested via an xpcshell test
*/
@ -253,9 +247,6 @@ module.exports = function (config) {
resolve: {
extensions: [".js", ".jsx", ".mjs"],
modules: [PATHS.moduleResolveDirectory, "node_modules"],
alias: {
asrouter: path.join(__dirname, "../asrouter"),
},
},
plugins: [
// The ResourceUriPlugin handles translating resource URIs in import

View file

@ -413,21 +413,6 @@ export const PREFS_CONFIG = new Map([
value: false,
},
],
[
"asrouter.providers.onboarding",
{
title: "Configuration for onboarding provider",
value: JSON.stringify({
id: "onboarding",
type: "local",
localProvider: "OnboardingMessageProvider",
enabled: true,
// Block specific messages from this local provider
exclude: [],
}),
},
],
// See browser/app/profile/firefox.js for other ASR preferences. They must be defined there to enable roll-outs.
[
"discoverystream.flight.blocks",
{

View file

@ -2,14 +2,14 @@
* 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/. */
// We use importESModule here instead of static import so that
// the Karma test environment won't choke on these module. This
// is because the Karma test environment already stubs out
// XPCOMUtils, and overrides importESModule to be a no-op (which
// can't be done for a static import statement). MESSAGE_TYPES_HASH / msg
// isn't something that the tests for this module seem to rely on in the
// Karma environment, but if that ever becomes the case, we should import
// those into unit-entry like we do for the ASRouter tests.
// We use importESModule here instead of static import so that the Karma test
// environment won't choke on these module. This is because the Karma test
// environment already stubs out XPCOMUtils, AppConstants and RemoteSettings,
// and overrides importESModule to be a no-op (which can't be done for a static
// import statement). MESSAGE_TYPE_HASH / msg isn't something that the tests
// for this module seem to rely on in the Karma environment, but if that ever
// becomes the case, we should import those into unit-entry like we do for the
// ASRouter tests.
// eslint-disable-next-line mozilla/use-static-import
const { XPCOMUtils } = ChromeUtils.importESModule(
@ -899,25 +899,6 @@ export class TelemetryFeed {
case at.TELEMETRY_USER_EVENT:
this.handleUserEvent(action);
break;
// The next few action types come from ASRouter, which doesn't use
// Actions from Actions.jsm, but uses these other custom strings.
case msg.TOOLBAR_BADGE_TELEMETRY:
// Intentional fall-through
case msg.TOOLBAR_PANEL_TELEMETRY:
// Intentional fall-through
case msg.MOMENTS_PAGE_TELEMETRY:
// Intentional fall-through
case msg.DOORHANGER_TELEMETRY:
// Intentional fall-through
case msg.INFOBAR_TELEMETRY:
// Intentional fall-through
case msg.SPOTLIGHT_TELEMETRY:
// Intentional fall-through
case msg.TOAST_NOTIFICATION_TELEMETRY:
// Intentional fall-through
case at.AS_ROUTER_TELEMETRY_USER_EVENT:
this.handleASRouterUserEvent(action);
break;
case at.TOP_SITES_SPONSORED_IMPRESSION_STATS:
this.handleTopSitesSponsoredImpressionStats(action);
break;
@ -948,6 +929,25 @@ export class TelemetryFeed {
case at.WEATHER_LOCATION_DATA_UPDATE:
this.handleWeatherUserEvent(action);
break;
// The remaining action types come from ASRouter, which doesn't use
// Actions from Actions.mjs, but uses these other custom strings.
case msg.TOOLBAR_BADGE_TELEMETRY:
// Intentional fall-through
case msg.TOOLBAR_PANEL_TELEMETRY:
// Intentional fall-through
case msg.MOMENTS_PAGE_TELEMETRY:
// Intentional fall-through
case msg.DOORHANGER_TELEMETRY:
// Intentional fall-through
case msg.INFOBAR_TELEMETRY:
// Intentional fall-through
case msg.SPOTLIGHT_TELEMETRY:
// Intentional fall-through
case msg.TOAST_NOTIFICATION_TELEMETRY:
// Intentional fall-through
case msg.AS_ROUTER_TELEMETRY_USER_EVENT:
this.handleASRouterUserEvent(action);
break;
}
}

View file

@ -14,11 +14,6 @@ const SCRIPT_TEMPLATE_RESOURCE_PATH =
let window = self;
window.requestAnimationFrame = () => {};
window.cancelAnimationFrame = () => {};
window.ASRouterMessage = () => {
return Promise.resolve();
};
window.ASRouterAddParentListener = () => {};
window.ASRouterRemoveParentListener = () => {};
/* import-globals-from /toolkit/components/workerloader/require.js */
importScripts("resource://gre/modules/workers/require.js");

View file

@ -66,8 +66,6 @@ skip-if = ["verify"] # bug 1834620 - order of events not stable
["browser_open_tab_focus.js"]
skip-if = ["os == 'linux'"] # Test setup only implemented for OSX and Windows
["browser_remote_l10n.js"]
["browser_topsites_annotation.js"]
skip-if = [
"os == 'linux' && debug", # Bug 1785005

View file

@ -176,16 +176,6 @@ describe("ActionCreators", () => {
assert.isTrue(au.isSendToMain(action), "isSendToMain");
});
});
describe("ASRouterUserEvent", () => {
it("should include the given data", () => {
const data = { action: "foo" };
assert.equal(ac.ASRouterUserEvent(data).data, data);
});
it("should wrap with AlsoToMain", () => {
const action = ac.ASRouterUserEvent({ action: "foo" });
assert.isTrue(au.isSendToMain(action), "isSendToMain");
});
});
describe("ImpressionStats", () => {
it("should include the right data", () => {
const data = { action: "foo" };

View file

@ -9,7 +9,6 @@ const {
Personalization,
DiscoveryStream,
Search,
ASRouter,
} = reducers;
import { actionTypes as at } from "common/Actions.mjs";
@ -1518,8 +1517,4 @@ describe("Reducers", () => {
assert.propertyVal(nextState, "disable", false);
});
});
it("should set initialized to true on AS_ROUTER_INITIALIZED", () => {
const nextState = ASRouter(undefined, { type: "AS_ROUTER_INITIALIZED" });
assert.propertyVal(nextState, "initialized", true);
});
});

View file

@ -119,23 +119,6 @@ const TEST_GLOBAL = {
},
platform: "win",
},
ASRouterPreferences: {
console: new FakeConsoleAPI({
maxLogLevel: "off", // set this to "debug" or "all" to get more ASRouter logging in tests
prefix: "ASRouter",
}),
},
AWScreenUtils: {
evaluateTargetingAndRemoveScreens() {
return true;
},
async removeScreens() {
return true;
},
evaluateScreenTargeting() {
return true;
},
},
BrowserUtils: {
sendToDeviceEmailsSupported() {
return true;

View file

@ -1244,12 +1244,14 @@ add_task(async function test_createASRouterEvent_valid_ping() {
"ASRouterEventPing ping"
);
let instance = new TelemetryFeed();
let data = {
action: "cfr_user_event",
event: "CLICK",
message_id: "cfr_message_01",
let action = {
type: msg.AS_ROUTER_TELEMETRY_USER_EVENT,
data: {
action: "cfr_user_event",
event: "CLICK",
message_id: "cfr_message_01",
},
};
let action = ac.ASRouterUserEvent(data);
let { ping } = await instance.createASRouterEvent(action);
await assertASRouterEventPingValid(ping);
@ -1266,7 +1268,7 @@ add_task(async function test_createASRouterEvent_call_correctPolicy() {
let instance = new TelemetryFeed();
sandbox.stub(instance, expectedPolicyFnName);
let action = ac.ASRouterUserEvent(data);
let action = { type: msg.AS_ROUTER_TELEMETRY_USER_EVENT, data };
await instance.createASRouterEvent(action);
Assert.ok(
instance[expectedPolicyFnName].calledOnce,
@ -1324,12 +1326,14 @@ add_task(async function test_createASRouterEvent_stringify_event_context() {
"it is an Object"
);
let instance = new TelemetryFeed();
let data = {
action: "asrouter_undesired_event",
event: "UNDESIRED_EVENT",
event_context: { foo: "bar" },
let action = {
type: msg.AS_ROUTER_TELEMETRY_USER_EVENT,
data: {
action: "asrouter_undesired_event",
event: "UNDESIRED_EVENT",
event_context: { foo: "bar" },
},
};
let action = ac.ASRouterUserEvent(data);
let { ping } = await instance.createASRouterEvent(action);
Assert.equal(ping.event_context, JSON.stringify({ foo: "bar" }));
@ -1341,12 +1345,14 @@ add_task(async function test_createASRouterEvent_not_stringify_event_context() {
"if it is a String"
);
let instance = new TelemetryFeed();
let data = {
action: "asrouter_undesired_event",
event: "UNDESIRED_EVENT",
event_context: "foo",
let action = {
type: msg.AS_ROUTER_TELEMETRY_USER_EVENT,
data: {
action: "asrouter_undesired_event",
event: "UNDESIRED_EVENT",
event_context: "foo",
},
};
let action = ac.ASRouterUserEvent(data);
let { ping } = await instance.createASRouterEvent(action);
Assert.equal(ping.event_context, "foo");
@ -1811,7 +1817,7 @@ add_task(async function test_onAction_basic_actions() {
add_task(async function test_onAction_calls_handleASRouterUserEvent() {
let actions = [
at.AS_ROUTER_TELEMETRY_USER_EVENT,
msg.AS_ROUTER_TELEMETRY_USER_EVENT,
msg.TOOLBAR_BADGE_TELEMETRY,
msg.TOOLBAR_PANEL_TELEMETRY,
msg.MOMENTS_PAGE_TELEMETRY,

View file

@ -4,10 +4,6 @@
/* import-globals-from preferences.js */
ChromeUtils.defineESModuleGetters(this, {
GenAI: "resource:///modules/GenAI.sys.mjs",
});
var gExperimentalPane = {
inited: false,
_template: null,
@ -170,7 +166,6 @@ var gExperimentalPane = {
}
this._featureGatesContainer.appendChild(frag);
// Bug 1895494 to allow more generic logic
GenAI.buildPreferences(window);
Services.obs.notifyObservers(window, "experimental-pane-loaded");
},
};

View file

@ -35,6 +35,15 @@ var gSearchResultsPane = {
searchResultsHighlighted: false,
searchableNodes: new Set([
"button",
"label",
"description",
"menulist",
"menuitem",
"checkbox",
]),
init() {
if (this.inited) {
return;
@ -414,13 +423,9 @@ var gSearchResultsPane = {
let matchesFound = false;
if (
nodeObject.childElementCount == 0 ||
nodeObject.localName == "button" ||
nodeObject.localName == "label" ||
nodeObject.localName == "description" ||
nodeObject.localName == "menulist" ||
nodeObject.localName == "menuitem" ||
nodeObject.localName == "checkbox" ||
nodeObject.localName == "moz-toggle"
this.searchableNodes.has(nodeObject.localName) ||
(nodeObject.localName?.startsWith("moz-") &&
nodeObject.localName !== "moz-input-box")
) {
let simpleTextNodes = this.textNodeDescendants(nodeObject);
if (nodeObject.shadowRoot) {

View file

@ -189,12 +189,14 @@ add_task(async function testSearchShadowDOM() {
});
// Create the toggle.
let { toggle, SHADOW_DOM_TEXT } = createToggle(gBrowser);
let { mozElements, SHADOW_DOM_TEXT } = createMozCustomElements(gBrowser);
ok(
!BrowserTestUtils.isVisible(toggle),
"Toggle is not visible prior to search."
);
mozElements.forEach(el => {
ok(
!BrowserTestUtils.isVisible(el),
`${el.localName} is not visible prior to search.`
);
});
// Perform search with text found in moz-toggle's shadow DOM.
let query = SHADOW_DOM_TEXT;
@ -205,10 +207,12 @@ add_task(async function testSearchShadowDOM() {
);
EventUtils.sendString(query);
await searchCompletedPromise;
ok(
BrowserTestUtils.isVisible(toggle),
"Toggle is visible after searching for string in the shadow DOM."
);
mozElements.forEach(el => {
ok(
BrowserTestUtils.isVisible(el),
`${el.localName} is visible after searching for string in the shadow DOM.`
);
});
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
@ -223,7 +227,10 @@ add_task(async function testSearchLightDOM() {
});
// Create the toggle.
let { toggle, LIGHT_DOM_TEXT } = createToggle(gBrowser);
let { mozElements, LIGHT_DOM_TEXT } = createMozCustomElements(gBrowser, [
"moz-toggle",
]);
let toggle = mozElements[0];
// Perform search with text found in moz-toggle's slotted content.
let query = LIGHT_DOM_TEXT;
@ -242,23 +249,42 @@ add_task(async function testSearchLightDOM() {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
// Create a toggle with a slotted link element.
function createToggle(gBrowser) {
const MOZ_CUSTOM_ELEMENTS = [
"moz-toggle",
"moz-radio-group",
"moz-radio",
"moz-checkbox",
];
// Create multiple moz- custom elements with the same label.
function createMozCustomElements(gBrowser, elements = MOZ_CUSTOM_ELEMENTS) {
const SHADOW_DOM_TEXT = "This text lives in the shadow DOM";
const LIGHT_DOM_TEXT = "This text lives in the light DOM";
let doc = gBrowser.contentDocument;
let toggle = doc.createElement("moz-toggle");
toggle.label = SHADOW_DOM_TEXT;
let link = doc.createElement("a");
link.href = "https://mozilla.org/";
link.textContent = LIGHT_DOM_TEXT;
toggle.append(link);
link.slot = "support-link";
let mozElements = elements.map(tag => {
let el = doc.createElement(tag);
el.label = SHADOW_DOM_TEXT;
return el;
});
let [toggle, radioGroup, radioButton, ...rest] = mozElements;
let protectionsGroup = doc.getElementById("trackingGroup");
protectionsGroup.append(toggle);
return { SHADOW_DOM_TEXT, LIGHT_DOM_TEXT, toggle };
if (toggle) {
let link = doc.createElement("a");
link.href = "https://mozilla.org/";
link.textContent = LIGHT_DOM_TEXT;
toggle.append(link);
link.slot = "support-link";
protectionsGroup.append(toggle);
}
if (radioGroup && radioButton) {
radioGroup.appendChild(radioButton);
protectionsGroup.append(radioGroup);
}
protectionsGroup.append(...rest);
return { SHADOW_DOM_TEXT, LIGHT_DOM_TEXT, mozElements };
}

View file

@ -187,7 +187,7 @@ add_task(async function test_timezone_exmpt_browser() {
null,
AppConstants.BROWSER_CHROME_URL,
"_blank",
"chrome,dialog=no,all,alwaysRaised",
"chrome,dialog=no,all",
null
);

View file

@ -80,8 +80,6 @@ const CHROME_FLAGS_MAP = [
// Do not inherit remoteness and fissionness from the previous session.
//[Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW, "remote", "non-remote"],
//[Ci.nsIWebBrowserChrome.CHROME_FISSION_WINDOW, "fission", "non-fission"],
[Ci.nsIWebBrowserChrome.CHROME_WINDOW_LOWERED, "alwayslowered"],
[Ci.nsIWebBrowserChrome.CHROME_WINDOW_RAISED, "alwaysraised"],
// "chrome" and "suppressanimation" are always set.
//[Ci.nsIWebBrowserChrome.CHROME_SUPPRESS_ANIMATION, "suppressanimation"],
[Ci.nsIWebBrowserChrome.CHROME_ALWAYS_ON_TOP, "alwaysontop"],

View file

@ -83,20 +83,16 @@ add_task(async function testRestoredWindowFeatures() {
{
chrome: true,
url: "http://example.com/browser/" + DUMMY_PAGE,
features: "chrome,all,dialog=no,alwayslowered,centerscreen",
features: "chrome,all,dialog=no,centerscreen",
barprops: ALL_BARPROPS,
chromeFlags:
Ci.nsIWebBrowserChrome.CHROME_WINDOW_LOWERED |
Ci.nsIWebBrowserChrome.CHROME_CENTER_SCREEN,
chromeFlags: Ci.nsIWebBrowserChrome.CHROME_CENTER_SCREEN,
},
{
chrome: true,
url: "http://example.com/browser/" + DUMMY_PAGE,
features: "chrome,all,dialog=no,alwaysraised,dependent",
features: "chrome,all,dialog=no,dependent",
barprops: ALL_BARPROPS,
chromeFlags:
Ci.nsIWebBrowserChrome.CHROME_WINDOW_RAISED |
Ci.nsIWebBrowserChrome.CHROME_DEPENDENT,
chromeFlags: Ci.nsIWebBrowserChrome.CHROME_DEPENDENT,
},
];
const TEST_URL_CHROME = "chrome://mochitests/content/browser/" + DUMMY_PAGE;

View file

@ -3,31 +3,28 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
.container {
font-size: 15px;
}
padding-inline: var(--space-small);
font: menu;
.customize-firefox-tools {
.inputs {
display: flex;
flex-direction: column;
gap: var(--space-medium);
> * + * {
margin-top: var(--space-xlarge);
}
.input-wrapper {
display: flex;
align-items: center;
gap: var(--space-small);
> sidebar-panel-header + * {
margin-top: var(--space-medium);
}
}
> input {
margin-inline-start: 0;
}
.customize-header {
display: flex;
justify-content: space-between;
align-items: center;
-moz-context-properties: fill;
fill: currentColor;
color: currentColor;
> label {
display: inline-flex;
align-items: center;
gap: var(--space-small);
font-size: 0.9em;
}
.customize-close-button::part(button) {
background-image: url("chrome://global/skin/icons/close-12.svg");
}
}
@ -42,25 +39,23 @@
.extensions {
display: flex;
flex-direction: column;
gap: var(--space-medium);
gap: var(--space-large);
margin-top: var(--space-small);
}
.extension-item {
display: flex;
gap: var(--space-medium);
gap: var(--space-small);
align-items: center;
font-size: 0.9em;
}
}
.customize-extensions-heading {
margin: 0;
}
#manage-settings {
display: flex;
align-items: center;
gap: var(--space-medium);
margin-block-start: var(--space-xxlarge);
font-size: 0.9em;
.icon {
background-image: url("chrome://browser/skin/preferences/category-general.svg");
}
gap: var(--space-small);
}

View file

@ -5,8 +5,6 @@
import { html, when } from "chrome://global/content/vendor/lit.all.mjs";
import { SidebarPage } from "./sidebar-page.mjs";
// eslint-disable-next-line import/no-unassigned-import
import "chrome://global/content/elements/moz-button.mjs";
const l10nMap = new Map([
["viewHistorySidebar", "sidebar-menu-history-label"],
@ -25,7 +23,7 @@ export class SidebarCustomize extends SidebarPage {
};
static queries = {
toolInputs: { all: ".customize-firefox-tools input" },
toolInputs: { all: ".customize-firefox-tools moz-checkbox" },
extensionLinks: { all: ".extension-link" },
};
@ -77,19 +75,17 @@ export class SidebarCustomize extends SidebarPage {
}
inputTemplate(tool) {
return html`<div class="input-wrapper">
<input
return html`
<moz-checkbox
type="checkbox"
id=${tool.view}
name=${tool.view}
iconsrc=${tool.iconUrl}
data-l10n-id=${this.getInputL10nId(tool.view)}
@change=${this.onToggleInput}
?checked=${!tool.disabled}
/>
<label for=${tool.view}>
<img src=${tool.iconUrl} class="icon" role="presentation" />
<span data-l10n-id=${this.getInputL10nId(tool.view)} />
</label>
</div>`;
`;
}
async manageAddon(extensionId) {
@ -154,18 +150,18 @@ export class SidebarCustomize extends SidebarPage {
<div class="container">
<sidebar-panel-header data-l10n-id="sidebar-menu-customize-header" data-l10n-attrs="heading" view="viewCustomizeSidebar">
</sidebar-panel-header>
<div class="customize-firefox-tools">
<h5 data-l10n-id="sidebar-customize-firefox-tools"></h5>
<div class="inputs">
<moz-fieldset class="customize-firefox-tools" data-l10n-id="sidebar-customize-firefox-tools">
${this.getWindow()
.SidebarController.getTools()
.map(tool => this.inputTemplate(tool))}
</div>
</div>
</moz-fieldset>
${when(
extensions.length,
() => html`<div class="customize-extensions">
<h5 data-l10n-id="sidebar-customize-extensions"></h5>
<h5
class="heading-medium customize-extensions-heading"
data-l10n-id="sidebar-customize-extensions"
></h5>
<div role="list" class="extensions">
${extensions.map((extension, index) =>
this.extensionTemplate(extension, index)
@ -174,7 +170,7 @@ export class SidebarCustomize extends SidebarPage {
</div>`
)}
<div id="manage-settings">
<span class="icon ghost-icon" role="presentation"></span>
<img src="chrome://browser/skin/preferences/category-general.svg" class="icon" role="presentation" />
<a
href="about:preferences"
@click=${this.openFirefoxSettings}

View file

@ -25,7 +25,8 @@ sidebar-history-date-prev-month =
sidebar-search-results-header =
.heading = Search results for “{ $query }”
sidebar-customize-firefox-tools = { -brand-product-name } tools
sidebar-customize-firefox-tools =
.label = { -brand-product-name } tools
sidebar-customize-firefox-settings = Manage { -brand-short-name } settings
## Labels for sidebar context menu items
@ -42,22 +43,26 @@ sidebar-customize-extensions = Sidebar extensions
## Labels for sidebar menu items.
sidebar-menu-genai-chat-label = AI chatbot
sidebar-menu-history-label = History
sidebar-menu-synced-tabs-label = Tabs from other devices
sidebar-menu-bookmarks-label = Bookmarks
sidebar-menu-genai-chat-label =
.label = AI chatbot
sidebar-menu-history-label =
.label = History
sidebar-menu-synced-tabs-label =
.label = Tabs from other devices
sidebar-menu-bookmarks-label =
.label = Bookmarks
sidebar-menu-customize-label = Customize sidebar
## Tooltips for sidebar menu items.
sidebar-menu-genai-chat-item = {""}
.title = { sidebar-menu-genai-chat-label }
.title = AI chatbot
sidebar-menu-history-item = {""}
.title = { sidebar-menu-history-label }
.title = History
sidebar-menu-synced-tabs-item = {""}
.title = { sidebar-menu-synced-tabs-label }
.title = Tabs from other devices
sidebar-menu-bookmarks-item = {""}
.title = { sidebar-menu-bookmarks-label }
.title = Bookmarks
sidebar-menu-customize-item = {""}
.title = { sidebar-menu-customize-label }

View file

@ -99,7 +99,7 @@ add_task(async function test_menu_items_labeled() {
// Use waitForCondition() here because sidebar needs a chance to load
// Fluent strings.
await TestUtils.waitForCondition(
() => button.hasVisibleLabel,
() => button.label || button.hasVisibleLabel,
`Expanded ${view} button has a label.`
);
}

View file

@ -66,11 +66,25 @@ class MyCustomElement extends MozLitElement {
`MozLitElement` differs from `LitElement` in a few important ways:
#### 1. It provides automatic Fluent support for the shadow DOM
#### It provides automatic Fluent support for the shadow DOM
When working with Fluent in the shadow DOM an element's `shadowRoot` must be connected before Fluent can be used. `MozLitElement` handles this by extending `LitElement`'s `connectedCallback` to [call](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/lit-utils.mjs#84) `document.l10n.connectRoot` if needed. `MozLitElement` also automatically calls `document.l10n.translateFragment` on the renderRoot anytime an element updates. The net result of these modifications is that you can use Fluent in your Lit based components just like you would in any other markup in `mozilla-central`.
#### 2. It implements support for Lit's `@query` and `@queryAll` decorators
#### It provides automatic Fluent support for localized Reactive Properties
Fluent requires that attributes be marked as safe if they don't fall into the default list of [allowed attributes](https://searchfox.org/mozilla-central/rev/4c8627a76e2e0a9b49c2b673424da478e08715ad/dom/l10n/L10nOverlays.cpp#44-95). By setting `fluent: true` in your Reactive Property's definition `MozLitElement` will automatically populate the `data-l10n-attrs` in `connectedCallback()` to mark the attribute as safe for Fluent.
```js
class MyCustomElement extends MozLitElement {
static properties = {
label: { type: String, fluent: true },
description: { type: String, fluent: true },
value: { type: String },
};
}
```
#### It implements support for Lit's `@query` and `@queryAll` decorators
The Lit library includes `@query` and `@queryAll` [decorators](https://lit.dev/docs/components/shadow-dom/#@query-@queryall-and-@queryasync-decorators) that provide an easy way of finding elements within the internal component DOM. These do not work in `mozilla-central` as we do not have support for JavaScript decorators. Instead, `MozLitElement` provides equivalent [DOM querying functionality](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/lit-utils.mjs#87-99) via defining a static `queries` property on the subclass. For example the following Lit code that queries the component's DOM for certain selectors and assigns the results to different class properties:
@ -115,7 +129,7 @@ class MyCustomElement extends MozLitElement {
}
```
#### 3. It adds a `dispatchOnUpdateComplete` method
#### It adds a `dispatchOnUpdateComplete` method
The `dispatchOnUpdateComplete` method provides an easy way to communicate to test code or other element consumers that a reactive property change has taken effect. It leverages Lit's [updateComplete](https://lit.dev/docs/components/lifecycle/#updatecomplete) promise to emit an event after all updates have been applied and the component's DOM is ready to be queried. It has the potential to be particularly useful when you need to query the DOM in test code, for example:

View file

@ -4,6 +4,8 @@ support-files = [
"../../../../dom/security/test/csp/dummy.pdf",
]
["browser_browserGlue_client_association_ping.js"]
["browser_browserGlue_os_auth.js"]
skip-if = ["os == 'linux'"]

View file

@ -0,0 +1,114 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
/**
* These tests ensure that the right values are sent for the client_association
* metrics, depending on the FxA signed-in state. Note that this does NOT test
* the "ride along" mechanism that the fxAccounts ping uses to be sent at the
* same frequency as the baseline ping, as this is something that is implemented
* and tested externally, in Glean.
*/
"use strict";
const { ClientID } = ChromeUtils.importESModule(
"resource://gre/modules/ClientID.sys.mjs"
);
const { sinon } = ChromeUtils.importESModule(
"resource://testing-common/Sinon.sys.mjs"
);
const { UIState } = ChromeUtils.importESModule(
"resource://services-sync/UIState.sys.mjs"
);
const FAKE_UID = "0123456789abcdef0123456789abcdef";
let gClientID = null;
add_setup(async () => {
gClientID = await ClientID.getClientID();
await SpecialPowers.pushPrefEnv({
set: [
["identity.fxaccounts.telemetry.clientAssociationPing.enabled", true],
],
});
});
/**
* A helper function that mocks out the accounts UIState to be in a particular
* status, and then forces the fxAccounts ping to be sent before running a
* taskFn to see what the values that'd be sent are.
*
* @param {string} status
* One of the UIState.STATUS_* constants.
* @param {function} taskFn
* A synchronous function that will be called just before the fxAccounts ping
* is sent.
*/
async function testMockUIState(status, taskFn) {
let sandbox = sinon.createSandbox();
sandbox.stub(UIState, "get").returns({
status,
lastSync: new Date(),
email: "test@example.com",
uid: FAKE_UID,
});
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
try {
let checkValues = new Promise(resolve => {
GleanPings.fxAccounts.testBeforeNextSubmit(() => {
taskFn();
resolve();
});
});
GleanPings.fxAccounts.submit();
await checkValues;
} finally {
sandbox.restore();
}
}
/**
* Tests that the accounts uid and legacy telemetry client ID are sent if
* the state is STATUS_SIGNED_IN.
*/
add_task(async function test_client_association_logged_in() {
await testMockUIState(UIState.STATUS_SIGNED_IN, async () => {
Assert.equal(
Glean.clientAssociation.uid.testGetValue(),
FAKE_UID,
"Got expected account uid"
);
Assert.equal(
Glean.clientAssociation.legacyClientId.testGetValue(),
gClientID,
"Got expected legacy telemetry client ID"
);
});
});
/**
* Tests that the accounts uid and legacy telemetry client ID are NOT sent if
* the state is not STATUS_SIGNED_IN.
*/
add_task(async function test_client_association_not_logged_in() {
for (let status of [
UIState.STATUS_NOT_CONFIGURED,
UIState.STATUS_LOGIN_FAILED,
UIState.STATUS_NOT_VERIFIED,
]) {
await testMockUIState(status, async () => {
Assert.equal(
Glean.clientAssociation.uid.testGetValue(),
null,
"No value set for account uid"
);
Assert.equal(
Glean.clientAssociation.legacyClientId.testGetValue(),
null,
"No value set for legacy telemetry client ID"
);
});
}
});

View file

@ -2523,12 +2523,11 @@ export class UrlbarInput {
// Check overflow again to ensure it didn't change in the meanwhile.
let input = this.inputField;
if (input && this._overflowing) {
// Normally we would overflow at the final side of text direction,
// though RTL domains may cause us to overflow at the opposite side.
// This happens dynamically as a consequence of the input field contents
// and the call to _ensureFormattedHostVisible, this code only reports
// the final state of all that scrolling into an attribute, because
// there's no other way to capture this in css.
// Normally we overflow at the end side of the text direction, though
// RTL domains may cause us to overflow at the opposite side.
// The outcome differs depending on the input field contents and applied
// formatting, and reports the final state of all the scrolling into an
// attribute available to css rules.
// Note it's also possible to scroll an unfocused input field using
// SHIFT + mousewheel on Windows, or with just the mousewheel / touchpad
// scroll (without modifiers) on Mac.
@ -2738,8 +2737,10 @@ export class UrlbarInput {
let trimmedValue = lazy.UrlbarPrefs.get("trimURLs")
? lazy.BrowserUIUtils.trimURL(val)
: val;
// Only trim value if the directionality doesn't change to RTL.
return lazy.UrlbarUtils.isTextDirectionRTL(trimmedValue, this.window)
// Only trim value if the directionality doesn't change to RTL and we're not
// showing a strikeout https protocol.
return lazy.UrlbarUtils.isTextDirectionRTL(trimmedValue, this.window) ||
this.valueFormatter.willShowFormattedMixedContentProtocol(val)
? val
: trimmedValue;
}

View file

@ -201,7 +201,12 @@ class ProviderHeuristicFallback extends UrlbarProvider {
// pass the pretty, unescaped URL as the result's title, since it is
// displayed to the user.
let escapedURL = uri.toString();
let displayURL = UrlbarUtils.prepareUrlForDisplay(uri, { trimURL: false });
let displayURL = UrlbarUtils.prepareUrlForDisplay(uri, {
trimURL: false,
// If the user didn't type a protocol, and we added one, don't show it,
// as https-first may upgrade it, potentially breaking expectations.
schemeless: !prefix,
});
// We don't know if this url is in Places or not, and checking that would
// be expensive. Thus we also don't know if we may have an icon.

View file

@ -1317,21 +1317,29 @@ export var UrlbarUtils = {
* @param {object} [options] Preparation options.
* @param {boolean} [options.trimURL] Whether the displayed URL should be
* trimmed or not.
* @param {boolean} [options.schemeless] Trim `http(s)://`.
* @returns {string} Prepared url.
*/
prepareUrlForDisplay(url, { trimURL = true } = {}) {
prepareUrlForDisplay(url, { trimURL = true, schemeless = false } = {}) {
// Some domains are encoded in punycode. The following ensures we display
// the url in utf-8.
try {
url = new URL(url).URI.displaySpec;
} catch {} // In some cases url is not a valid url.
if (url && trimURL && lazy.UrlbarPrefs.get("trimURLs")) {
url = lazy.BrowserUIUtils.removeSingleTrailingSlashFromURL(url);
if (url.startsWith("https://")) {
url = url.substring(8);
if (url.startsWith("www.")) {
url = url.substring(4);
if (url) {
if (schemeless) {
url = UrlbarUtils.stripPrefixAndTrim(url, {
stripHttp: true,
stripHttps: true,
})[0];
} else if (trimURL && lazy.UrlbarPrefs.get("trimURLs")) {
url = lazy.BrowserUIUtils.removeSingleTrailingSlashFromURL(url);
if (url.startsWith("https://")) {
url = url.substring(8);
if (url.startsWith("www.")) {
url = url.substring(4);
}
}
}
}

View file

@ -86,46 +86,40 @@ export class UrlbarValueFormatter {
// Apply new formatting. Formatter methods should return true if they
// successfully formatted the value and false if not. We apply only
// one formatter at a time, so we stop at the first successful one.
this._formattingApplied = this._formatURL() || this._formatSearchAlias();
this.window.requestAnimationFrame(() => {
if (this._updateInstance != instance) {
return;
}
this._formattingApplied = this._formatURL() || this._formatSearchAlias();
});
}
_ensureFormattedHostVisible(urlMetaData) {
// Used to avoid re-entrance in the requestAnimationFrame callback.
let instance = (this._formatURLInstance = {});
#ensureFormattedHostVisible(urlMetaData) {
// Make sure the host is always visible. Since it is aligned on
// the first strong directional character, we set scrollLeft
// appropriately to ensure the domain stays visible in case of an
// overflow.
this.window.requestAnimationFrame(() => {
// Check for re-entrance. On focus change this formatting code is
// invoked regardless, thus this should be enough.
if (this._formatURLInstance != instance) {
return;
}
// In the future, for example in bug 525831, we may add a forceRTL
// char just after the domain, and in such a case we should not
// scroll to the left.
urlMetaData = urlMetaData || this._getUrlMetaData();
if (!urlMetaData) {
this.urlbarInput.removeAttribute("domaindir");
return;
}
let { url, preDomain, domain } = urlMetaData;
let directionality = this.window.windowUtils.getDirectionFromText(domain);
if (
directionality == this.window.windowUtils.DIRECTION_RTL &&
url[preDomain.length + domain.length] != "\u200E"
) {
this.urlbarInput.setAttribute("domaindir", "rtl");
this.inputField.scrollLeft = this.inputField.scrollLeftMax;
} else {
this.urlbarInput.setAttribute("domaindir", "ltr");
this.inputField.scrollLeft = 0;
}
this.urlbarInput.updateTextOverflow();
});
// In the future, for example in bug 525831, we may add a forceRTL
// char just after the domain, and in such a case we should not
// scroll to the left.
urlMetaData = urlMetaData || this._getUrlMetaData();
if (!urlMetaData) {
this.urlbarInput.removeAttribute("domaindir");
return;
}
let { url, preDomain, domain } = urlMetaData;
let directionality = this.window.windowUtils.getDirectionFromText(domain);
if (
directionality == this.window.windowUtils.DIRECTION_RTL &&
url[preDomain.length + domain.length] != "\u200E"
) {
this.urlbarInput.setAttribute("domaindir", "rtl");
this.inputField.scrollLeft = this.inputField.scrollLeftMax;
} else {
this.urlbarInput.setAttribute("domaindir", "ltr");
this.inputField.scrollLeft = 0;
}
this.urlbarInput.updateTextOverflow();
}
_getUrlMetaData() {
@ -269,6 +263,36 @@ export class UrlbarValueFormatter {
this.inputField.style.setProperty("--urlbar-scheme-size", "0px");
}
/**
* Whether formatting is enabled.
*
* @returns {boolean}
*/
get formattingEnabled() {
return lazy.UrlbarPrefs.get("formatting.enabled");
}
/**
* Whether value would show strike-through mixed content protocol.
*
* @param {string} val The value to evaluate. It should normally be the
* input field value, otherwise this returns false.
* @returns {boolean}
*/
willShowFormattedMixedContentProtocol(val) {
let shownUrlIsMixedContentLoadedPage =
this.urlbarInput.getAttribute("pageproxystate") == "valid" &&
this.window.gBrowser.securityUI.state &
Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT;
return (
this.formattingEnabled &&
!lazy.UrlbarPrefs.get("security.insecure_connection_text.enabled") &&
val.startsWith("https://") &&
val == this.urlbarInput.value &&
shownUrlIsMixedContentLoadedPage
);
}
/**
* If the input value is a URL and the input is not focused, this
* formatter method highlights the domain, and if mixed content is present,
@ -287,14 +311,9 @@ export class UrlbarValueFormatter {
let { domain, origin, preDomain, schemeWSlashes, trimmedLength, url } =
urlMetaData;
let isMixedContent =
schemeWSlashes == "https://" &&
this.urlbarInput.value.startsWith("https://") &&
this.urlbarInput.getAttribute("pageproxystate") == "valid" &&
this.window.gBrowser.securityUI.state &
Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT;
let isUnformattedMixedContent =
isMixedContent && !lazy.UrlbarPrefs.get("formatting.enabled");
let showMixedContentProtocol = this.willShowFormattedMixedContentProtocol(
this.urlbarInput.value
);
// When RTL domains cause the address bar to overflow to the left, the
// protocol may get hidden, if it was not trimmed. We then set the
@ -306,8 +325,8 @@ export class UrlbarValueFormatter {
// - The insecure label is active. The label is a sufficient indicator.
if (
this.urlbarInput.value.startsWith(schemeWSlashes) &&
!isUnformattedMixedContent &&
!lazy.UrlbarPrefs.get("security.insecure_connection_text.enabled")
showMixedContentProtocol &&
this.formattingEnabled
) {
this.scheme.value = schemeWSlashes;
this.inputField.style.setProperty(
@ -316,9 +335,9 @@ export class UrlbarValueFormatter {
);
}
this._ensureFormattedHostVisible(urlMetaData);
this.#ensureFormattedHostVisible(urlMetaData);
if (!lazy.UrlbarPrefs.get("formatting.enabled")) {
if (!this.formattingEnabled) {
return false;
}
@ -329,9 +348,9 @@ export class UrlbarValueFormatter {
let textNode = editor.rootElement.firstChild;
// Strike out the "https" part if mixed active content is loaded and https
// is not trimmed.
if (isMixedContent) {
// Strike out the "https" part if mixed active content status should be
// shown.
if (showMixedContentProtocol) {
let range = this.document.createRange();
range.setStart(textNode, 0);
range.setEnd(textNode, 5);
@ -411,7 +430,7 @@ export class UrlbarValueFormatter {
* True if formatting was applied and false if not.
*/
_formatSearchAlias() {
if (!lazy.UrlbarPrefs.get("formatting.enabled")) {
if (!this.formattingEnabled) {
return false;
}
@ -535,7 +554,12 @@ export class UrlbarValueFormatter {
}
this._resizeThrottleTimeout = this.window.setTimeout(() => {
this._resizeThrottleTimeout = null;
this._ensureFormattedHostVisible();
let instance = (this._resizeInstance = {});
this.window.requestAnimationFrame(() => {
if (instance == this._resizeInstance) {
this.#ensureFormattedHostVisible();
}
});
}, 100);
}
}

View file

@ -1351,7 +1351,7 @@ export var UrlbarTestUtils = {
* @param {int} [options.selectionType]
* The selectionType for which the input should be checked.
*/
checkFormatting(
async checkFormatting(
win,
urlFormatString,
{
@ -1360,6 +1360,7 @@ export var UrlbarTestUtils = {
selectionType = Ci.nsISelectionController.SELECTION_URLSECONDARY,
} = {}
) {
await new Promise(resolve => win.requestAnimationFrame(resolve));
let selectionController = win.gURLBar.editor.selectionController;
let selection = selectionController.getSelection(selectionType);
let value = win.gURLBar.editor.rootElement.textContent;
@ -1390,6 +1391,7 @@ export var UrlbarTestUtils = {
"popupshown"
);
let button = win.document.getElementById("urlbar-searchmode-switcher");
this.Assert.ok(lazy.BrowserTestUtils.isVisible(button));
await this.EventUtils.promiseElementReadyForUserInput(button, win);
this.EventUtils.synthesizeMouseAtCenter(button, {}, win);
return promiseMenuOpen;

View file

@ -21,7 +21,7 @@ async function testVal(urlFormatString, clobberedURLString = null) {
info("Setting the value property directly");
gURLBar.value = str;
gBrowser.selectedBrowser.focus();
UrlbarTestUtils.checkFormatting(window, urlFormatString, {
await UrlbarTestUtils.checkFormatting(window, urlFormatString, {
clobberedURLString,
});
@ -33,7 +33,7 @@ async function testVal(urlFormatString, clobberedURLString = null) {
"URL is not highlighted"
);
gBrowser.selectedBrowser.focus();
UrlbarTestUtils.checkFormatting(window, urlFormatString, {
await UrlbarTestUtils.checkFormatting(window, urlFormatString, {
clobberedURLString,
additionalMsg: "with input simulation",
});
@ -182,6 +182,9 @@ add_task(async function test_url_formatting_after_visiting_bookmarks() {
EventUtils.sendKey("RETURN");
await BrowserTestUtils.browserLoaded(gBrowser, false, null, true);
UrlbarTestUtils.checkFormatting(window, "<something.>example.com</test>");
await UrlbarTestUtils.checkFormatting(
window,
"<something.>example.com</test>"
);
SpecialPowers.popPrefEnv();
});

View file

@ -45,13 +45,13 @@ add_task(async function detach() {
let win = await detachTab(tabToDetach);
UrlbarTestUtils.checkFormatting(
await UrlbarTestUtils.checkFormatting(
win,
UrlbarTestUtils.trimURL("<https://>example.com</detach>")
);
await BrowserTestUtils.closeWindow(win);
UrlbarTestUtils.checkFormatting(
await UrlbarTestUtils.checkFormatting(
window,
UrlbarTestUtils.trimURL("<https://>example.com</original-tab>")
);
@ -84,7 +84,7 @@ add_task(async function detach_emptyTab() {
await focusPromise;
ok(!gURLBar.focused, "urlbar is not focused");
UrlbarTestUtils.checkFormatting(
await UrlbarTestUtils.checkFormatting(
window,
UrlbarTestUtils.trimURL("<https://>example.com</original-tab>")
);

View file

@ -26,7 +26,7 @@ async function testVal(urlFormatString, clobberedURLString = null) {
info("Setting the value property directly");
gURLBar.value = str;
gBrowser.selectedBrowser.focus();
UrlbarTestUtils.checkFormatting(window, urlFormatString, {
await UrlbarTestUtils.checkFormatting(window, urlFormatString, {
clobberedURLString,
selectionType: Ci.nsISelectionController.SELECTION_URLSTRIKEOUT,
});
@ -36,11 +36,12 @@ add_task(async function test_strikeout_on_no_https_trimming() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.trimHttps", false],
["security.insecure_connection_text.enabled", false],
["security.mixed_content.block_active_content", false],
],
});
await BrowserTestUtils.withNewTab(TEST_URL, function () {
testVal("<https>://example.com/mixed_active.html");
await BrowserTestUtils.withNewTab(TEST_URL, async function () {
await testVal("<https>://example.com/mixed_active.html");
});
await SpecialPowers.popPrefEnv();
});
@ -49,11 +50,12 @@ add_task(async function test_no_strikeout_on_https_trimming() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.trimHttps", true],
["security.insecure_connection_text.enabled", false],
["security.mixed_content.block_active_content", false],
],
});
await BrowserTestUtils.withNewTab(TEST_URL, function () {
testVal(
await BrowserTestUtils.withNewTab(TEST_URL, async function () {
await testVal(
"https://example.com/mixed_active.html",
"example.com/mixed_active.html"
);

View file

@ -72,7 +72,7 @@ add_task(async function search_strings() {
assertSearchStringIsInUrlbar(searchString);
info("Check that no formatting is applied.");
UrlbarTestUtils.checkFormatting(window, searchString);
await UrlbarTestUtils.checkFormatting(window, searchString);
}
BrowserTestUtils.removeTab(tab);

View file

@ -10,7 +10,7 @@ const TEST_DATA = [
input: "this is a\ntest",
expected: {
urlbar: "this is a test",
autocomplete: "this is a test",
title: "this is a test",
type: UrlbarUtils.RESULT_TYPE.SEARCH,
},
},
@ -18,7 +18,7 @@ const TEST_DATA = [
input: "this is a\n\ttest",
expected: {
urlbar: "this is a test",
autocomplete: "this is a test",
title: "this is a test",
type: UrlbarUtils.RESULT_TYPE.SEARCH,
},
},
@ -26,7 +26,7 @@ const TEST_DATA = [
input: "http:\n//\nexample.\ncom",
expected: {
urlbar: "http://example.com",
autocomplete: "http://example.com/",
title: "http://example.com/",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -34,7 +34,7 @@ const TEST_DATA = [
input: "htp:example.\ncom",
expected: {
urlbar: "htp:example.com",
autocomplete: "http://example.com/",
title: "example.com/",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -42,7 +42,7 @@ const TEST_DATA = [
input: "example.\ncom",
expected: {
urlbar: "example.com",
autocomplete: "http://example.com/",
title: "example.com/",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -50,7 +50,7 @@ const TEST_DATA = [
input: "http://example.com/foo bar/",
expected: {
urlbar: "http://example.com/foo bar/",
autocomplete: "http://example.com/foo bar/",
title: "http://example.com/foo bar/",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -58,7 +58,7 @@ const TEST_DATA = [
input: "http://exam\nple.com/foo bar/",
expected: {
urlbar: "http://example.com/foo bar/",
autocomplete: "http://example.com/foo bar/",
title: "http://example.com/foo bar/",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -66,7 +66,7 @@ const TEST_DATA = [
input: "javasc\nript:\nalert(1)",
expected: {
urlbar: "alert(1)",
autocomplete: "alert(1)",
title: "alert(1)",
type: UrlbarUtils.RESULT_TYPE.SEARCH,
},
},
@ -74,7 +74,7 @@ const TEST_DATA = [
input: "a\nb\nc",
expected: {
urlbar: "a b c",
autocomplete: "a b c",
title: "a b c",
type: UrlbarUtils.RESULT_TYPE.SEARCH,
},
},
@ -82,7 +82,7 @@ const TEST_DATA = [
input: "lo\ncal\nhost",
expected: {
urlbar: "localhost",
autocomplete: "http://localhost/",
title: "localhost/",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -90,7 +90,7 @@ const TEST_DATA = [
input: "data:text/html,<iframe\n src='example\n.com'>\n</iframe>",
expected: {
urlbar: "data:text/html,<iframe src='example .com'> </iframe>",
autocomplete: "data:text/html,<iframe src='example .com'> </iframe>",
title: "data:text/html,<iframe src='example .com'> </iframe>",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -98,7 +98,7 @@ const TEST_DATA = [
input: "data:,123\n4 5\n6",
expected: {
urlbar: "data:,123 4 5 6",
autocomplete: "data:,123 4 5 6",
title: "data:,123 4 5 6",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -106,7 +106,7 @@ const TEST_DATA = [
input: "data:text/html;base64,123\n4 5\n6",
expected: {
urlbar: "data:text/html;base64,1234 56",
autocomplete: "data:text/html;base64,123456",
title: "data:text/html;base64,123456",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -114,7 +114,7 @@ const TEST_DATA = [
input: "http://example.com\n",
expected: {
urlbar: "http://example.com",
autocomplete: "http://example.com/",
title: "http://example.com/",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -122,7 +122,7 @@ const TEST_DATA = [
input: "http://example.com\r",
expected: {
urlbar: "http://example.com",
autocomplete: "http://example.com/",
title: "http://example.com/",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -130,7 +130,7 @@ const TEST_DATA = [
input: "http://ex\ra\nmp\r\nle.com\r\n",
expected: {
urlbar: "http://example.com",
autocomplete: "http://example.com/",
title: "http://example.com/",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -138,7 +138,7 @@ const TEST_DATA = [
input: "http://example.com/titled",
expected: {
urlbar: "http://example.com/titled",
autocomplete: "example title",
title: "example title",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -146,7 +146,7 @@ const TEST_DATA = [
input: "127.0.0.1\r",
expected: {
urlbar: "127.0.0.1",
autocomplete: "http://127.0.0.1/",
title: "127.0.0.1/",
type: UrlbarUtils.RESULT_TYPE.URL,
},
},
@ -154,7 +154,7 @@ const TEST_DATA = [
input: "\r\n\r\n\r\n\r\n\r\n",
expected: {
urlbar: "",
autocomplete: "",
title: "",
type: UrlbarUtils.RESULT_TYPE.SEARCH,
},
},
@ -212,11 +212,7 @@ async function assertResult(expected) {
Assert.equal(gURLBar.value, expected.urlbar, "Pasted value is correct");
const result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.equal(
result.title,
expected.autocomplete,
"Title of autocomplete is correct"
);
Assert.equal(result.title, expected.title, "Title of result is correct");
Assert.equal(result.type, expected.type, "Type of autocomplete is correct");
if (gURLBar.value) {

View file

@ -3,7 +3,7 @@
add_setup(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.searchModeSwitcher.featureGate", true]],
set: [["browser.urlbar.scotchBonnet.enableOverride", true]],
});
});

View file

@ -341,12 +341,12 @@ add_task(async function test_no_highlight_fallback_heuristic_url() {
await testResult(
{
query: "nonexisting.com",
title: "http://nonexisting.com/",
title: "nonexisting.com/",
url: "http://nonexisting.com/",
},
{
displayedUrl: "", // URL heuristic only has title.
highlightedTitle: [["http://nonexisting.com/", false]],
highlightedTitle: [["nonexisting.com/", false]],
highlightedUrl: [],
},
0 // Test the heuristic result.

View file

@ -26,7 +26,7 @@ add_task(async function test_not_autofill_ws_1() {
matches: [
makeVisitResult(context, {
uri: "http://mozilla.org/",
fallbackTitle: "http://mozilla.org/",
fallbackTitle: "mozilla.org/",
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
heuristic: true,
}),
@ -46,7 +46,7 @@ add_task(async function test_not_autofill_ws_2() {
matches: [
makeVisitResult(context, {
uri: "http://mozilla.org/",
fallbackTitle: "http://mozilla.org/",
fallbackTitle: "mozilla.org/",
iconUri: "page-icon:http://mozilla.org/",
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
heuristic: true,
@ -67,7 +67,7 @@ add_task(async function test_not_autofill_ws_3() {
matches: [
makeVisitResult(context, {
uri: "http://mozilla.org/link",
fallbackTitle: "http://mozilla.org/link",
fallbackTitle: "mozilla.org/link",
iconUri: "page-icon:http://mozilla.org/",
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
heuristic: true,

View file

@ -182,7 +182,7 @@ add_task(async function portNoMatch1() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: `http://${origin}:89/`,
fallbackTitle: `http://${origin}:89/`,
fallbackTitle: `${origin}:89/`,
iconUri: "",
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,
@ -206,7 +206,7 @@ add_task(async function portNoMatch2() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: `http://${origin}:9/`,
fallbackTitle: `http://${origin}:9/`,
fallbackTitle: `${origin}:9/`,
iconUri: "",
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,
@ -230,7 +230,7 @@ add_task(async function trailingSlash_2() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: "http://example/",
fallbackTitle: "http://example/",
fallbackTitle: "example/",
iconUri: "page-icon:http://example/",
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,

View file

@ -171,7 +171,7 @@ add_autofill_task(async function wwwShouldNotMatchNoWWW() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: "http://www." + search + "/",
fallbackTitle: "http://www." + search + "/",
fallbackTitle: "www." + search + "/",
displayUrl: "http://www." + search,
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,
@ -189,7 +189,7 @@ add_autofill_task(async function wwwShouldNotMatchNoWWW() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: "http://www." + search,
fallbackTitle: "http://www." + search,
fallbackTitle: "www." + search,
iconUri: `page-icon:http://www.${host}/`,
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,
@ -408,7 +408,7 @@ add_autofill_task(async function httpsWWWShouldNotMatchNoWWW() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: "http://www." + search + "/",
fallbackTitle: "http://www." + search + "/",
fallbackTitle: "www." + search + "/",
displayUrl: "http://www." + search,
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,
@ -426,7 +426,7 @@ add_autofill_task(async function httpsWWWShouldNotMatchNoWWW() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: "http://www." + search,
fallbackTitle: "http://www." + search,
fallbackTitle: "www." + search,
iconUri: `page-icon:http://www.${host}/`,
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,
@ -820,7 +820,7 @@ add_autofill_task(async function frecency() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: "http://" + search,
fallbackTitle: "http://" + search,
fallbackTitle: search,
iconUri: `page-icon:http://${host}/`,
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,
@ -1029,7 +1029,7 @@ add_autofill_task(async function suggestHistoryFalse_visit() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: "http://" + search,
fallbackTitle: "http://" + search,
fallbackTitle: search,
iconUri: `page-icon:http://${host}/`,
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,
@ -1087,7 +1087,7 @@ add_autofill_task(async function suggestHistoryFalse_visit_prefix() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: "http://" + search,
fallbackTitle: "http://" + search,
fallbackTitle: search,
iconUri: `page-icon:http://${host}/`,
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,
@ -1191,7 +1191,7 @@ add_autofill_task(async function suggestHistoryFalse_bookmark_1() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: "http://" + search,
fallbackTitle: "http://" + search,
fallbackTitle: search,
iconUri: `page-icon:http://${host}/`,
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,
@ -1451,7 +1451,10 @@ add_autofill_task(async function suggestBookmarkFalse_visit_1() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: prefixedUrl,
fallbackTitle: prefixedUrl,
fallbackTitle: UrlbarUtils.stripPrefixAndTrim(prefixedUrl, {
stripHttp: true,
stripHttps: true,
})[0],
heuristic: true,
iconUri: origins ? "" : `page-icon:http://${host}/`,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,
@ -1660,7 +1663,7 @@ add_autofill_task(async function suggestBookmarkFalse_unvisitedBookmark() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: "http://" + search,
fallbackTitle: "http://" + search,
fallbackTitle: search,
iconUri: `page-icon:http://${host}/`,
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,

View file

@ -74,7 +74,7 @@ add_task(async function portNoMatch() {
makeVisitResult(context, {
source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
uri: "http://example.com:8999/f",
fallbackTitle: "http://example.com:8999/f",
fallbackTitle: "example.com:8999/f",
iconUri: "page-icon:http://example.com:8999/",
heuristic: true,
providerName: HEURISTIC_FALLBACK_PROVIDERNAME,

Some files were not shown because too many files have changed in this diff Show more