Update On Thu Jun 20 20:47:57 CEST 2024
This commit is contained in:
parent
0feb4598a2
commit
a852ed1946
743 changed files with 13255 additions and 11584 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -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
4
aclocal.m4
vendored
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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: () => {
|
||||
|
|
|
@ -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;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -341,7 +341,7 @@ const MESSAGES = () => [
|
|||
},
|
||||
},
|
||||
},
|
||||
groups: ["panel-test-provider"],
|
||||
groups: ["panel-test-provider", "pbNewtab"],
|
||||
targeting: "region != 'CN' && !hasActiveEnterprisePolicies",
|
||||
frequency: { lifetime: 3 },
|
||||
},
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
|
|
@ -47,6 +47,8 @@ skip-if = [
|
|||
|
||||
["browser_feature_callout.js"]
|
||||
|
||||
["browser_remote_l10n.js"]
|
||||
|
||||
["browser_trigger_listeners.js"]
|
||||
https_first_disabled = true
|
||||
|
||||
|
|
|
@ -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",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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", () => {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
106
browser/components/backup/content/disable-backup-encryption.mjs
Normal file
106
browser/components/backup/content/disable-backup-encryption.mjs
Normal 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);
|
|
@ -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({});
|
|
@ -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)
|
||||
|
|
|
@ -4,6 +4,9 @@ prefs = [
|
|||
"browser.backup.preferences.ui.enabled=true",
|
||||
"browser.backup.scheduled.enabled=false",
|
||||
]
|
||||
support-files = [
|
||||
"head.js",
|
||||
]
|
||||
|
||||
["browser_settings.js"]
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
14
browser/components/backup/tests/browser/head.js
Normal file
14
browser/components/backup/tests/browser/head.js
Normal 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"
|
||||
);
|
|
@ -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"]
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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],
|
||||
],
|
||||
|
|
|
@ -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();
|
||||
});
|
|
@ -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");
|
||||
|
|
52
browser/components/genai/tests/browser/browser_genai_init.js
Normal file
52
browser/components/genai/tests/browser/browser_genai_init.js
Normal 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();
|
||||
});
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" };
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
||||
|
|
|
@ -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"],
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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.`
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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'"]
|
||||
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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>")
|
||||
);
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
add_setup(async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.urlbar.searchModeSwitcher.featureGate", true]],
|
||||
set: [["browser.urlbar.scotchBonnet.enableOverride", true]],
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue