Update On Tue Mar 25 19:23:03 CET 2025
This commit is contained in:
parent
240d9e05c1
commit
61427ba9a4
3058 changed files with 59039 additions and 22181 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -2781,9 +2781,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.22"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178"
|
||||
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
|
|
|
@ -219,6 +219,11 @@ bool SelectionManager::SelectionRangeChanged(SelectionType aType,
|
|||
dom::Document* doc = start->OwnerDoc();
|
||||
MOZ_ASSERT(doc);
|
||||
nsINode* node = aRange.GetClosestCommonInclusiveAncestor();
|
||||
if (!node) {
|
||||
// Bug 1954751: This can happen when a Selection is being garbage collected,
|
||||
// but it's unclear exactly what other circumstances are involved.
|
||||
return false;
|
||||
}
|
||||
HyperTextAccessible* acc = nsAccUtils::GetTextContainer(node);
|
||||
if (!acc) {
|
||||
return true;
|
||||
|
|
|
@ -116,10 +116,8 @@ void nsCoreUtils::DispatchClickEvent(XULTreeElement* aTree, int32_t aRowIndex,
|
|||
int32_t cnvdY = presContext->CSSPixelsToDevPixels(tcY + int32_t(rect.y) + 1) +
|
||||
presContext->AppUnitsToDevPixels(offset.y);
|
||||
|
||||
if (StaticPrefs::dom_popup_experimental()) {
|
||||
// This isn't needed once bug 1924790 is fixed.
|
||||
tcElm->OwnerDoc()->NotifyUserGestureActivation();
|
||||
}
|
||||
// This isn't needed once bug 1924790 is fixed.
|
||||
tcElm->OwnerDoc()->NotifyUserGestureActivation();
|
||||
|
||||
// XUL is just desktop, so there is no real reason for senfing touch events.
|
||||
DispatchMouseEvent(eMouseDown, cnvdX, cnvdY, tcElm, tcFrame, presShell,
|
||||
|
|
|
@ -2573,10 +2573,9 @@ void LocalAccessible::DispatchClickEvent(uint32_t aActionIndex) const {
|
|||
nsCoreUtils::DispatchTouchEvent(eTouchStart, x, y, mContent, frame, presShell,
|
||||
widget);
|
||||
|
||||
if (StaticPrefs::dom_popup_experimental()) {
|
||||
// This isn't needed once bug 1924790 is fixed.
|
||||
mContent->OwnerDoc()->NotifyUserGestureActivation();
|
||||
}
|
||||
// This isn't needed once bug 1924790 is fixed.
|
||||
mContent->OwnerDoc()->NotifyUserGestureActivation();
|
||||
|
||||
nsCoreUtils::DispatchMouseEvent(eMouseDown, x, y, mContent, frame, presShell,
|
||||
widget);
|
||||
nsCoreUtils::DispatchTouchEvent(eTouchEnd, x, y, mContent, frame, presShell,
|
||||
|
|
|
@ -381,19 +381,6 @@ between
|
|||
<div id="popover2" popover>popover2</div>
|
||||
<button id="toggle5">toggle5</button>
|
||||
</template></div>
|
||||
<script>
|
||||
const toggle1 = document.getElementById("toggle1");
|
||||
const popover1 = document.getElementById("popover1");
|
||||
toggle1.popoverTargetElement = popover1;
|
||||
const toggle3 = document.getElementById("toggle3");
|
||||
const shadow = document.getElementById("shadowHost").shadowRoot;
|
||||
const toggle4 = shadow.getElementById("toggle4");
|
||||
const popover2 = shadow.getElementById("popover2");
|
||||
toggle3.popoverTargetElement = popover2;
|
||||
toggle4.popoverTargetElement = popover2;
|
||||
const toggle5 = shadow.getElementById("toggle5");
|
||||
toggle5.popoverTargetElement = popover1;
|
||||
</script>
|
||||
`,
|
||||
async function testPopoverIdl(browser, docAcc) {
|
||||
// No popover is showing, so there shouldn't be any details relations.
|
||||
|
@ -465,7 +452,23 @@ between
|
|||
await hidden;
|
||||
await testCachedRelation(toggle4, RELATION_DETAILS, []);
|
||||
},
|
||||
{ chrome: true, topLevel: true }
|
||||
{
|
||||
chrome: true,
|
||||
topLevel: true,
|
||||
contentSetup: async function contentSetup() {
|
||||
const toggle1 = content.document.getElementById("toggle1");
|
||||
const popover1 = content.document.getElementById("popover1");
|
||||
toggle1.popoverTargetElement = popover1;
|
||||
const toggle3 = content.document.getElementById("toggle3");
|
||||
const shadow = content.document.getElementById("shadowHost").shadowRoot;
|
||||
const toggle4 = shadow.getElementById("toggle4");
|
||||
const popover2 = shadow.getElementById("popover2");
|
||||
toggle3.popoverTargetElement = popover2;
|
||||
toggle4.popoverTargetElement = popover2;
|
||||
const toggle5 = shadow.getElementById("toggle5");
|
||||
toggle5.popoverTargetElement = popover1;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
@ -348,6 +348,12 @@ function wrapWithIFrame(doc, options = {}) {
|
|||
id: DEFAULT_IFRAME_DOC_BODY_ID,
|
||||
...iframeDocBodyAttrs,
|
||||
};
|
||||
if (options.contentSetup) {
|
||||
// Hide the body initially so we can ensure that any changes made by
|
||||
// contentSetup are included when the body's content is initially added to
|
||||
// the accessibility tree.
|
||||
iframeDocBodyAttrs["aria-hidden"] = "true";
|
||||
}
|
||||
if (options.remoteIframe) {
|
||||
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
|
||||
const srcURL = new URL(`http://example.net/document-builder.sjs`);
|
||||
|
@ -418,6 +424,11 @@ function snippetToURL(doc, options = {}) {
|
|||
|
||||
if (gIsIframe) {
|
||||
doc = wrapWithIFrame(doc, options);
|
||||
} else if (options.contentSetup) {
|
||||
// Hide the body initially so we can ensure that any changes made by
|
||||
// contentSetup are included when the body's content is initially added to
|
||||
// the accessibility tree.
|
||||
attrs["aria-hidden"] = "true";
|
||||
}
|
||||
|
||||
const encodedDoc = encodeURIComponent(
|
||||
|
@ -595,6 +606,21 @@ function accessibleTask(doc, task, options = {}) {
|
|||
}
|
||||
}
|
||||
|
||||
if (options.contentSetup) {
|
||||
info("Executing contentSetup");
|
||||
const ready = waitForEvent(EVENT_REORDER, currentContentDoc());
|
||||
await invokeContentTask(browser, [], options.contentSetup);
|
||||
// snippetToURL set aria-hidden on the body. We now Remove aria-hidden
|
||||
// and wait for a reorder on the body. This guarantees that any
|
||||
// changes made by contentSetup are included when the body's content
|
||||
// is initially added to the accessibility tree and that the
|
||||
// accessibility tree is up to date.
|
||||
await invokeContentTask(browser, [], () => {
|
||||
content.document.body.removeAttribute("aria-hidden");
|
||||
});
|
||||
await ready;
|
||||
info("contentSetup done");
|
||||
}
|
||||
await loadContentScripts(browser, {
|
||||
script: "Common.sys.mjs",
|
||||
symbol: "CommonUtils",
|
||||
|
@ -670,6 +696,22 @@ function accessibleTask(doc, task, options = {}) {
|
|||
* - {CacheDomain} cacheDomains
|
||||
* The set of cache domains that should be present at the start of the
|
||||
* test. If not set, all cache domains will be present.
|
||||
* - {Function|AsyncFunction} contentSetup
|
||||
* An optional task to run to set up the content document before the
|
||||
* test starts. If this test is to be run as a chrome document in the
|
||||
* parent process (chrome: true), This should be used instead of an
|
||||
* inline <script> element in the test snippet, since inline script is
|
||||
* not allowed in such documents. This task is ultimately executed
|
||||
* using SpecialPowers.spawn. Any updates to the content within the
|
||||
* body will be included when the content is initially added to the
|
||||
* accessibility tree. The accessibility tree is guaranteed to be up
|
||||
* to date when the test starts. This will not work correctly for
|
||||
* changes to the html or body elements themselves. Note that you will
|
||||
* need to define this exactly as follows:
|
||||
* contentSetup: async function contentSetup() { ... }
|
||||
* async contentSetup() will fail when the task is serialized.
|
||||
* contentSetup: async function() will be changed to
|
||||
* async contentSetup() by the linter and likewise fail.
|
||||
*/
|
||||
function addAccessibleTask(doc, task, options = {}) {
|
||||
const {
|
||||
|
|
|
@ -170,18 +170,7 @@ add_task(async function testTextFragmentSamePage() {
|
|||
* Test custom highlight mutations.
|
||||
*/
|
||||
addAccessibleTask(
|
||||
`
|
||||
${snippet}
|
||||
<script>
|
||||
const firstText = document.getElementById("first").firstChild;
|
||||
// Highlight the word "first".
|
||||
const range1 = new Range();
|
||||
range1.setStart(firstText, 4);
|
||||
range1.setEnd(firstText, 9);
|
||||
const highlight1 = new Highlight(range1);
|
||||
CSS.highlights.set("highlight1", highlight1);
|
||||
</script>
|
||||
`,
|
||||
snippet,
|
||||
async function testCustomHighlightMutations(browser, docAcc) {
|
||||
info("Checking initial highlight");
|
||||
const first = findAccessibleChildByID(docAcc, "first");
|
||||
|
@ -272,41 +261,26 @@ ${snippet}
|
|||
});
|
||||
await rangeCheck;
|
||||
},
|
||||
{ chrome: true, topLevel: true }
|
||||
{
|
||||
chrome: true,
|
||||
topLevel: true,
|
||||
contentSetup: async function contentSetup() {
|
||||
const firstText = content.document.getElementById("first").firstChild;
|
||||
// Highlight the word "first".
|
||||
const range1 = new content.Range();
|
||||
range1.setStart(firstText, 4);
|
||||
range1.setEnd(firstText, 9);
|
||||
const highlight1 = new content.Highlight(range1);
|
||||
content.CSS.highlights.set("highlight1", highlight1);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Test custom highlight types.
|
||||
*/
|
||||
addAccessibleTask(
|
||||
`
|
||||
${snippet}
|
||||
<script>
|
||||
const firstText = document.getElementById("first").firstChild;
|
||||
// Highlight the word "The".
|
||||
const range1 = new Range();
|
||||
range1.setStart(firstText, 0);
|
||||
range1.setEnd(firstText, 3);
|
||||
const highlight = new Highlight(range1);
|
||||
CSS.highlights.set("highlight", highlight);
|
||||
|
||||
// Make the word "first" a spelling error.
|
||||
const range2 = new Range();
|
||||
range2.setStart(firstText, 4);
|
||||
range2.setEnd(firstText, 9);
|
||||
const spelling = new Highlight(range2);
|
||||
spelling.type = "spelling-error";
|
||||
CSS.highlights.set("spelling", spelling);
|
||||
|
||||
// Make the word "phrase" a grammar error.
|
||||
const range3 = new Range();
|
||||
range3.setStart(firstText, 10);
|
||||
range3.setEnd(firstText, 16);
|
||||
const grammar = new Highlight(range3);
|
||||
grammar.type = "grammar-error";
|
||||
CSS.highlights.set("grammar", grammar);
|
||||
</script>
|
||||
`,
|
||||
snippet,
|
||||
async function testCustomHighlightTypes(browser, docAcc) {
|
||||
const first = findAccessibleChildByID(docAcc, "first");
|
||||
ok(
|
||||
|
@ -345,85 +319,42 @@ ${snippet}
|
|||
"second highlight ranges correct"
|
||||
);
|
||||
},
|
||||
{ chrome: true, topLevel: true }
|
||||
{
|
||||
chrome: true,
|
||||
topLevel: true,
|
||||
contentSetup: async function contentSetup() {
|
||||
const firstText = content.document.getElementById("first").firstChild;
|
||||
// Highlight the word "The".
|
||||
const range1 = new content.Range();
|
||||
range1.setStart(firstText, 0);
|
||||
range1.setEnd(firstText, 3);
|
||||
const highlight = new content.Highlight(range1);
|
||||
content.CSS.highlights.set("highlight", highlight);
|
||||
|
||||
// Make the word "first" a spelling error.
|
||||
const range2 = new content.Range();
|
||||
range2.setStart(firstText, 4);
|
||||
range2.setEnd(firstText, 9);
|
||||
const spelling = new content.Highlight(range2);
|
||||
spelling.type = "spelling-error";
|
||||
content.CSS.highlights.set("spelling", spelling);
|
||||
|
||||
// Make the word "phrase" a grammar error.
|
||||
const range3 = new content.Range();
|
||||
range3.setStart(firstText, 10);
|
||||
range3.setEnd(firstText, 16);
|
||||
const grammar = new content.Highlight(range3);
|
||||
grammar.type = "grammar-error";
|
||||
content.CSS.highlights.set("grammar", grammar);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Test overlapping custom highlights.
|
||||
*/
|
||||
addAccessibleTask(
|
||||
`
|
||||
${snippet}
|
||||
<script>
|
||||
const firstText = document.getElementById("first").firstChild;
|
||||
// Make the word "The" both a highlight and a spelling error.
|
||||
const range1 = new Range();
|
||||
range1.setStart(firstText, 0);
|
||||
range1.setEnd(firstText, 3);
|
||||
const highlight1 = new Highlight(range1);
|
||||
CSS.highlights.set("highlight1", highlight1);
|
||||
const spelling = new Highlight(range1);
|
||||
spelling.type = "spelling-error";
|
||||
CSS.highlights.set("spelling", spelling);
|
||||
|
||||
// Highlight the word "first".
|
||||
const range2 = new Range();
|
||||
range2.setStart(firstText, 4);
|
||||
range2.setEnd(firstText, 9);
|
||||
highlight1.add(range2);
|
||||
// Make "fir" a spelling error.
|
||||
const range3 = new Range();
|
||||
range3.setStart(firstText, 4);
|
||||
range3.setEnd(firstText, 7);
|
||||
spelling.add(range3);
|
||||
// Make "rst" a spelling error.
|
||||
const range4 = new Range();
|
||||
range4.setStart(firstText, 6);
|
||||
range4.setEnd(firstText, 9);
|
||||
spelling.add(range4);
|
||||
|
||||
// Highlight the word "phrase".
|
||||
const range5 = new Range();
|
||||
range5.setStart(firstText, 10);
|
||||
range5.setEnd(firstText, 16);
|
||||
highlight1.add(range5);
|
||||
// Make "ras" a spelling error.
|
||||
const range6 = new Range();
|
||||
range6.setStart(firstText, 12);
|
||||
range6.setEnd(firstText, 15);
|
||||
spelling.add(range6);
|
||||
|
||||
const secondText = document.querySelector("#second i").firstChild;
|
||||
// Highlight the word "second".
|
||||
const range7 = new Range();
|
||||
range7.setStart(secondText, 0);
|
||||
range7.setEnd(secondText, 6);
|
||||
highlight1.add(range7);
|
||||
// Make "sec" a spelling error.
|
||||
const range8 = new Range();
|
||||
range8.setStart(secondText, 0);
|
||||
range8.setEnd(secondText, 3);
|
||||
spelling.add(range8);
|
||||
// Make "nd" a spelling error.
|
||||
const range9 = new Range();
|
||||
range9.setStart(secondText, 4);
|
||||
range9.setEnd(secondText, 6);
|
||||
spelling.add(range9);
|
||||
|
||||
const phrase2Text = document.querySelector("#second b").firstChild;
|
||||
// Highlight the word "phrase".
|
||||
const range10 = new Range();
|
||||
range10.setStart(phrase2Text, 0);
|
||||
range10.setEnd(phrase2Text, 6);
|
||||
highlight1.add(range10);
|
||||
// Highlight "ras" using a different Highlight.
|
||||
const range11 = new Range();
|
||||
range11.setStart(phrase2Text, 2);
|
||||
range11.setEnd(phrase2Text, 5);
|
||||
const highlight2 = new Highlight(range11);
|
||||
CSS.highlights.set("highlight2", highlight2);
|
||||
</script>
|
||||
`,
|
||||
snippet,
|
||||
async function testCustomHighlightOverlapping(browser, docAcc) {
|
||||
const first = findAccessibleChildByID(docAcc, "first");
|
||||
ok(
|
||||
|
@ -484,5 +415,78 @@ ${snippet}
|
|||
"second spelling ranges correct"
|
||||
);
|
||||
},
|
||||
{ chrome: true, topLevel: true }
|
||||
{
|
||||
chrome: true,
|
||||
topLevel: true,
|
||||
contentSetup: async function contentSetup() {
|
||||
const firstText = content.document.getElementById("first").firstChild;
|
||||
// Make the word "The" both a highlight and a spelling error.
|
||||
const range1 = new content.Range();
|
||||
range1.setStart(firstText, 0);
|
||||
range1.setEnd(firstText, 3);
|
||||
const highlight1 = new content.Highlight(range1);
|
||||
content.CSS.highlights.set("highlight1", highlight1);
|
||||
const spelling = new content.Highlight(range1);
|
||||
spelling.type = "spelling-error";
|
||||
content.CSS.highlights.set("spelling", spelling);
|
||||
|
||||
// Highlight the word "first".
|
||||
const range2 = new content.Range();
|
||||
range2.setStart(firstText, 4);
|
||||
range2.setEnd(firstText, 9);
|
||||
highlight1.add(range2);
|
||||
// Make "fir" a spelling error.
|
||||
const range3 = new content.Range();
|
||||
range3.setStart(firstText, 4);
|
||||
range3.setEnd(firstText, 7);
|
||||
spelling.add(range3);
|
||||
// Make "rst" a spelling error.
|
||||
const range4 = new content.Range();
|
||||
range4.setStart(firstText, 6);
|
||||
range4.setEnd(firstText, 9);
|
||||
spelling.add(range4);
|
||||
|
||||
// Highlight the word "phrase".
|
||||
const range5 = new content.Range();
|
||||
range5.setStart(firstText, 10);
|
||||
range5.setEnd(firstText, 16);
|
||||
highlight1.add(range5);
|
||||
// Make "ras" a spelling error.
|
||||
const range6 = new content.Range();
|
||||
range6.setStart(firstText, 12);
|
||||
range6.setEnd(firstText, 15);
|
||||
spelling.add(range6);
|
||||
|
||||
const secondText = content.document.querySelector("#second i").firstChild;
|
||||
// Highlight the word "second".
|
||||
const range7 = new content.Range();
|
||||
range7.setStart(secondText, 0);
|
||||
range7.setEnd(secondText, 6);
|
||||
highlight1.add(range7);
|
||||
// Make "sec" a spelling error.
|
||||
const range8 = new content.Range();
|
||||
range8.setStart(secondText, 0);
|
||||
range8.setEnd(secondText, 3);
|
||||
spelling.add(range8);
|
||||
// Make "nd" a spelling error.
|
||||
const range9 = new content.Range();
|
||||
range9.setStart(secondText, 4);
|
||||
range9.setEnd(secondText, 6);
|
||||
spelling.add(range9);
|
||||
|
||||
const phrase2Text =
|
||||
content.document.querySelector("#second b").firstChild;
|
||||
// Highlight the word "phrase".
|
||||
const range10 = new content.Range();
|
||||
range10.setStart(phrase2Text, 0);
|
||||
range10.setEnd(phrase2Text, 6);
|
||||
highlight1.add(range10);
|
||||
// Highlight "ras" using a different Highlight.
|
||||
const range11 = new content.Range();
|
||||
range11.setStart(phrase2Text, 2);
|
||||
range11.setEnd(phrase2Text, 5);
|
||||
const highlight2 = new content.Highlight(range11);
|
||||
content.CSS.highlights.set("highlight2", highlight2);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1841,6 +1841,7 @@ pref("browser.newtabpage.activity-stream.newtabWallpapers.customWallpaper.enable
|
|||
// Utility preferences for custom wallpaper upload
|
||||
pref("browser.newtabpage.activity-stream.newtabWallpapers.customWallpaper.uuid", "");
|
||||
pref("browser.newtabpage.activity-stream.newtabWallpapers.customWallpaper.fileSize", 0);
|
||||
pref("browser.newtabpage.activity-stream.newtabWallpapers.customWallpaper.fileSize.enabled", false);
|
||||
|
||||
// Current new tab page background images.
|
||||
pref("browser.newtabpage.activity-stream.newtabWallpapers.wallpaper", "");
|
||||
|
@ -2099,10 +2100,10 @@ pref("sidebar.revamp.round-content-area", false);
|
|||
pref("sidebar.animation.enabled", true);
|
||||
pref("sidebar.animation.duration-ms", 200);
|
||||
pref("sidebar.animation.expand-on-hover.duration-ms", 400);
|
||||
// The sidebar.main.tools pref cannot be changed.
|
||||
// Use the sidebar.newTool.migration. pref branch to introduce a new "tool" to the sidebar launcher;
|
||||
// see https://firefox-source-docs.mozilla.org/browser/components/sidebar/docs/index.html for instructions.
|
||||
pref("sidebar.main.tools", "aichat,syncedtabs,history");
|
||||
// This pref is used to store user customized tools in the sidebar launcher and shouldn't be changed.
|
||||
// See https://firefox-source-docs.mozilla.org/browser/components/sidebar/docs/index.html for ways
|
||||
// you can introduce a new tool to the sidebar launcher.
|
||||
pref("sidebar.main.tools", "");
|
||||
pref("sidebar.verticalTabs", false);
|
||||
pref("sidebar.visibility", "always-show");
|
||||
// Sidebar UI state is stored per-window via session restore. Use this pref
|
||||
|
|
|
@ -24,7 +24,7 @@ add_task(async function findbar_test() {
|
|||
await gFindBarPromise;
|
||||
gFindBar.open();
|
||||
|
||||
await new ContentTask.spawn(newTab.linkedBrowser, null, async function () {
|
||||
await ContentTask.spawn(newTab.linkedBrowser, null, async function () {
|
||||
let iframe = content.document.getElementById("iframe");
|
||||
let awaitLoad = ContentTaskUtils.waitForEvent(iframe, "load", false);
|
||||
iframe.src = "https://example.org/";
|
||||
|
|
|
@ -19,7 +19,7 @@ const TEST_CASES = [
|
|||
},
|
||||
{
|
||||
type: "chrome page",
|
||||
testURL: "chrome://global/skin/in-content/info-pages.css",
|
||||
testURL: "chrome://global/content/mozilla.html",
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -75,8 +75,8 @@ var tests = [
|
|||
},
|
||||
{
|
||||
name: "chrome:",
|
||||
location: "chrome://global/skin/in-content/info-pages.css",
|
||||
hostForDisplay: "chrome://global/skin/in-content/info-pages.css",
|
||||
location: "chrome://global/content/mozilla.html",
|
||||
hostForDisplay: "chrome://global/content/mozilla.html",
|
||||
hasSubview: false,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -3698,7 +3698,7 @@ BrowserGlue.prototype = {
|
|||
_migrateUI() {
|
||||
// Use an increasing number to keep track of the current migration state.
|
||||
// Completely unrelated to the current Firefox release number.
|
||||
const UI_VERSION = 152;
|
||||
const UI_VERSION = 153;
|
||||
const BROWSER_DOCURL = AppConstants.BROWSER_CHROME_URL;
|
||||
|
||||
if (!Services.prefs.prefHasUserValue("browser.migration.version")) {
|
||||
|
@ -4501,6 +4501,19 @@ BrowserGlue.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
if (
|
||||
currentUIVersion < 153 &&
|
||||
Services.prefs.getBoolPref("sidebar.revamp") &&
|
||||
!Services.prefs.prefHasUserValue("sidebar.main.tools")
|
||||
) {
|
||||
// This pref will now be a user set branch but we want to preserve the previous
|
||||
// default value for existing sidebar.revamp users who hadn't changed it.
|
||||
Services.prefs.setCharPref(
|
||||
"sidebar.main.tools",
|
||||
"aichat,syncedtabs,history"
|
||||
);
|
||||
}
|
||||
|
||||
// Update the migration version.
|
||||
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
|
||||
},
|
||||
|
|
|
@ -1894,10 +1894,8 @@ const MESSAGES = () => {
|
|||
{
|
||||
type: "action",
|
||||
label: {
|
||||
raw: {
|
||||
string_id:
|
||||
"shopping-callout-not-opted-in-integrated-reminder-do-not-show",
|
||||
},
|
||||
string_id:
|
||||
"shopping-callout-not-opted-in-integrated-reminder-do-not-show",
|
||||
},
|
||||
action: {
|
||||
type: "SET_PREF",
|
||||
|
@ -1914,10 +1912,8 @@ const MESSAGES = () => {
|
|||
{
|
||||
type: "action",
|
||||
label: {
|
||||
raw: {
|
||||
string_id:
|
||||
"shopping-callout-not-opted-in-integrated-reminder-show-fewer",
|
||||
},
|
||||
string_id:
|
||||
"shopping-callout-not-opted-in-integrated-reminder-show-fewer",
|
||||
},
|
||||
action: {
|
||||
type: "MULTI_ACTION",
|
||||
|
@ -1954,10 +1950,8 @@ const MESSAGES = () => {
|
|||
{
|
||||
type: "action",
|
||||
label: {
|
||||
raw: {
|
||||
string_id:
|
||||
"shopping-callout-not-opted-in-integrated-reminder-manage-settings",
|
||||
},
|
||||
string_id:
|
||||
"shopping-callout-not-opted-in-integrated-reminder-manage-settings",
|
||||
},
|
||||
action: {
|
||||
type: "OPEN_ABOUT_PAGE",
|
||||
|
@ -2150,10 +2144,8 @@ const MESSAGES = () => {
|
|||
{
|
||||
type: "action",
|
||||
label: {
|
||||
raw: {
|
||||
string_id:
|
||||
"shopping-callout-not-opted-in-integrated-reminder-do-not-show",
|
||||
},
|
||||
string_id:
|
||||
"shopping-callout-not-opted-in-integrated-reminder-do-not-show",
|
||||
},
|
||||
action: {
|
||||
type: "SET_PREF",
|
||||
|
@ -2170,10 +2162,8 @@ const MESSAGES = () => {
|
|||
{
|
||||
type: "action",
|
||||
label: {
|
||||
raw: {
|
||||
string_id:
|
||||
"shopping-callout-not-opted-in-integrated-reminder-show-fewer",
|
||||
},
|
||||
string_id:
|
||||
"shopping-callout-not-opted-in-integrated-reminder-show-fewer",
|
||||
},
|
||||
action: {
|
||||
type: "MULTI_ACTION",
|
||||
|
@ -2210,10 +2200,8 @@ const MESSAGES = () => {
|
|||
{
|
||||
type: "action",
|
||||
label: {
|
||||
raw: {
|
||||
string_id:
|
||||
"shopping-callout-not-opted-in-integrated-reminder-manage-settings",
|
||||
},
|
||||
string_id:
|
||||
"shopping-callout-not-opted-in-integrated-reminder-manage-settings",
|
||||
},
|
||||
action: {
|
||||
type: "OPEN_ABOUT_PAGE",
|
||||
|
|
|
@ -29,9 +29,13 @@ add_task(async function test_show_chat() {
|
|||
|
||||
Assert.ok(GenAI.canShowChatEntrypoint, "Can show with provider");
|
||||
|
||||
Services.prefs.setStringPref("sidebar.main.tools", "aichat");
|
||||
Services.prefs.setBoolPref("sidebar.revamp", true);
|
||||
|
||||
Assert.ok(GenAI.canShowChatEntrypoint, "Can show with revamp");
|
||||
Assert.ok(
|
||||
GenAI.canShowChatEntrypoint,
|
||||
"Can show with revamp and aichat tool"
|
||||
);
|
||||
|
||||
Services.prefs.setStringPref("sidebar.main.tools", "history");
|
||||
|
||||
|
|
|
@ -341,6 +341,7 @@ newtab:
|
|||
- interaction
|
||||
notification_emails:
|
||||
- nbarrett@mozilla.com
|
||||
- mcrawford@mozilla.com
|
||||
expires: never
|
||||
extra_keys:
|
||||
selected_wallpaper:
|
||||
|
@ -353,28 +354,6 @@ newtab:
|
|||
description: >
|
||||
Whether or not user had a previously set wallpaper
|
||||
type: boolean
|
||||
newtab_visit_id: *newtab_visit_id
|
||||
send_in_pings:
|
||||
- newtab
|
||||
|
||||
wallpaper_upload:
|
||||
type: event
|
||||
description: >
|
||||
Recorded when a user uploads a custom wallpaper
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1943663
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1943663
|
||||
data_sensitivity:
|
||||
- interaction
|
||||
notification_emails:
|
||||
- mcrawford@mozilla.com
|
||||
expires: never
|
||||
extra_keys:
|
||||
had_previous_wallpaper:
|
||||
description: >
|
||||
Whether or not user had a previously set wallpaper
|
||||
type: boolean
|
||||
had_uploaded_previously:
|
||||
description: >
|
||||
Whether or not user had a previously uploaded a custom wallpaper
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<!-- 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/. -->
|
||||
<svg width="120" height="40" viewBox="0 0 120 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect y="16" width="120" height="24" fill="white"/>
|
||||
<rect y="12" width="120" height="4" fill="#F9F9FB"/>
|
||||
<rect width="120" height="12" fill="#F0F0F4"/>
|
||||
<mask id="mask0" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="42" y="0" width="86" height="41">
|
||||
<path d="M76 0.0661167C76 0.0661167 60.5 -1.9339 60.5 16.0661C60.5 37.0662 48.3333 40.0661 42 40.5661L128 40.5662V0.0661167H76Z" fill="#D9D9D9"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0)">
|
||||
<path d="M45 16H120V40H45V16Z" fill="#42414D"/>
|
||||
<path d="M45 0H120V12H45V0Z" fill="#1C1B22"/>
|
||||
<path d="M45 12H120V16H45V12Z" fill="#2B2A33"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 897 B |
|
@ -10,18 +10,28 @@
|
|||
gap: var(--space-xlarge);
|
||||
}
|
||||
|
||||
#profile-content h2[data-l10n-id="edit-profile-page-header"] {
|
||||
margin-block: 0;
|
||||
}
|
||||
|
||||
#header-avatar {
|
||||
-moz-context-properties: fill, stroke;
|
||||
|
||||
width: var(--header-avatar-size);
|
||||
height: var(--header-avatar-size);
|
||||
border-radius: var(--border-radius-circle);
|
||||
margin-inline-end: var(--space-xxlarge);
|
||||
}
|
||||
|
||||
#profile-name-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-xsmall);
|
||||
margin-block: 0 var(--space-large);
|
||||
}
|
||||
|
||||
#profile-name-area label {
|
||||
margin-bottom: var(--space-xsmall);
|
||||
}
|
||||
|
||||
#profile-name {
|
||||
|
@ -68,8 +78,18 @@
|
|||
color: var(--icon-color-success);
|
||||
}
|
||||
|
||||
#themes::part(inputs) {
|
||||
margin-top: var(--space-medium);
|
||||
}
|
||||
|
||||
#avatars::part(inputs) {
|
||||
margin-top: var(--space-medium);
|
||||
}
|
||||
|
||||
#avatars::part(inputs),
|
||||
#themes::part(inputs) {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
row-gap: var(--space-small);
|
||||
column-gap: var(--space-medium);
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ export class NewProfileCard extends EditProfileCard {
|
|||
<span data-l10n-id="new-profile-page-header-description"></span>
|
||||
<a
|
||||
is="moz-support-link"
|
||||
support-page="profiles"
|
||||
support-page="profile-management"
|
||||
data-l10n-id="new-profile-page-learn-more"
|
||||
></a>
|
||||
</p>
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
outline-offset: var(--focus-outline-offset);
|
||||
border: 1px solid var(--border-color-interactive);
|
||||
}
|
||||
|
||||
.wrapper[checked] ::slotted(*:first-of-type) {
|
||||
border: var(--focus-outline);
|
||||
border-width: 1px;
|
||||
border-width: var(--border-width);
|
||||
border-style: solid;
|
||||
border-color: var(--border-color-interactive);
|
||||
}
|
||||
|
||||
.wrapper:focus-within ::slotted(*:first-of-type) {
|
||||
|
|
|
@ -52,7 +52,8 @@ new-profile-card {
|
|||
#delete-profile-card {
|
||||
display: flex;
|
||||
gap: var(--space-xxlarge);
|
||||
padding: var(--space-xxlarge);
|
||||
padding-block: 50px var(--space-xxlarge);
|
||||
padding-inline: var(--space-xxlarge);
|
||||
|
||||
@media only screen and (width <= 830px) {
|
||||
flex-direction: column;
|
||||
|
|
|
@ -20,12 +20,37 @@ export class ProfilesThemeCard extends MozLitElement {
|
|||
imgHolder: ".img-holder",
|
||||
};
|
||||
|
||||
firstUpdated() {
|
||||
super.firstUpdated();
|
||||
this.updateThemeImage();
|
||||
}
|
||||
|
||||
updateThemeImage() {
|
||||
if (!this.theme) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.theme.id === "default-theme@mozilla.org") {
|
||||
// For system theme, we use a special SVG that shows the light/dark wave design
|
||||
this.backgroundImg.src =
|
||||
"chrome://browser/content/profiles/assets/system-theme-background.svg";
|
||||
// Reset any inline styles since the SVG has its own colors
|
||||
this.backgroundImg.style.fill = "";
|
||||
this.backgroundImg.style.stroke = "";
|
||||
this.imgHolder.style.backgroundColor = "";
|
||||
} else {
|
||||
// For other themes, use the standard SVG with dynamic colors
|
||||
this.backgroundImg.src =
|
||||
"chrome://browser/content/profiles/assets/theme-selector-background.svg";
|
||||
this.backgroundImg.style.fill = this.theme.chromeColor;
|
||||
this.backgroundImg.style.stroke = this.theme.toolbarColor;
|
||||
this.imgHolder.style.backgroundColor = this.theme.contentColor;
|
||||
}
|
||||
}
|
||||
|
||||
updated() {
|
||||
super.updated();
|
||||
|
||||
this.backgroundImg.style.fill = this.theme.chromeColor;
|
||||
this.backgroundImg.style.stroke = this.theme.toolbarColor;
|
||||
this.imgHolder.style.backgroundColor = this.theme.contentColor;
|
||||
this.updateThemeImage();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -42,9 +67,7 @@ export class ProfilesThemeCard extends MozLitElement {
|
|||
<moz-card class="theme-card">
|
||||
<div class="theme-content">
|
||||
<div class="img-holder">
|
||||
<img
|
||||
src="chrome://browser/content/profiles/assets/theme-selector-background.svg"
|
||||
/>
|
||||
<img />
|
||||
</div>
|
||||
<div
|
||||
class="theme-name"
|
||||
|
|
|
@ -36,6 +36,7 @@ head = "../unit/head.js head.js"
|
|||
run-if = ["os != 'linux'"] # Linux clients cannot remote themselves.
|
||||
|
||||
["browser_preferences.js"]
|
||||
fail-if = ["a11y_checks"] # Bug 1955503
|
||||
|
||||
["browser_test_db_lazily_created.js"]
|
||||
|
||||
|
|
|
@ -640,8 +640,7 @@ export class ShoppingContainer extends MozLitElement {
|
|||
!RPMGetBoolPref(HAS_SEEN_POSITION_NOTIFICATION_CARD_PREF, true) &&
|
||||
this.isProductPage;
|
||||
let canShowKeepClosedMessage =
|
||||
this.showingKeepClosedMessage &&
|
||||
RPMGetBoolPref(SHOW_KEEP_SIDEBAR_CLOSED_MESSAGE_PREF, true);
|
||||
this.showingKeepClosedMessage && this.isProductPage;
|
||||
|
||||
if (canShowNotificationCard) {
|
||||
return this.newPositionNotificationCardTemplate();
|
||||
|
@ -730,7 +729,9 @@ export class ShoppingContainer extends MozLitElement {
|
|||
|
||||
if (
|
||||
yetToSeeNotificationCard ||
|
||||
!RPMGetBoolPref(SHOW_KEEP_SIDEBAR_CLOSED_MESSAGE_PREF, false)
|
||||
!RPMGetBoolPref(SHOW_KEEP_SIDEBAR_CLOSED_MESSAGE_PREF, false) ||
|
||||
this.showOnboarding ||
|
||||
!this.isProductPage
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
|
||||
/* import-globals-from head.js */
|
||||
|
||||
// withReviewCheckerSidebar calls SpecialPowers.spawn, which injects
|
||||
// ContentTaskUtils in the scope of the callback. Eslint doesn't know about
|
||||
// that.
|
||||
/* global ContentTaskUtils */
|
||||
const CONTENT_PAGE = "https://example.com";
|
||||
|
||||
add_setup(async function setup() {
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
"use strict";
|
||||
|
||||
/* import-globals-from head.js */
|
||||
// withReviewCheckerSidebar calls SpecialPowers.spawn, which injects
|
||||
// ContentTaskUtils in the scope of the callback. Eslint doesn't know about
|
||||
// that.
|
||||
/* global ContentTaskUtils */
|
||||
|
||||
const CONTENT_PAGE = "https://example.com";
|
||||
const NON_PDP_PAGE = "about:about";
|
||||
|
|
|
@ -4,6 +4,12 @@
|
|||
"use strict";
|
||||
|
||||
/* import-globals-from head.js */
|
||||
// withReviewCheckerSidebar calls SpecialPowers.spawn, which injects
|
||||
// ContentTaskUtils in the scope of the callback. Eslint doesn't know about
|
||||
// that.
|
||||
/* global ContentTaskUtils */
|
||||
|
||||
const NON_PDP_PAGE = "about:about";
|
||||
|
||||
async function testNotificationCardThenCloseRC() {
|
||||
await withReviewCheckerSidebar(async _args => {
|
||||
|
@ -228,3 +234,93 @@ add_task(
|
|||
await SpecialPowers.popPrefEnv();
|
||||
}
|
||||
);
|
||||
|
||||
add_task(async function test_keep_closed_message_not_visible_non_pdp() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.shopping.experience2023.newPositionCard.hasSeen", true],
|
||||
["browser.shopping.experience2023.showKeepSidebarClosedMessage", true],
|
||||
// Set to minimum closed counts met, to speed up testing
|
||||
["browser.shopping.experience2023.sidebarClosedCount", 4],
|
||||
],
|
||||
});
|
||||
await BrowserTestUtils.withNewTab(PRODUCT_TEST_URL, async _browser => {
|
||||
await SidebarController.show("viewReviewCheckerSidebar");
|
||||
info("Waiting for sidebar to update.");
|
||||
await reviewCheckerSidebarUpdated(PRODUCT_TEST_URL);
|
||||
|
||||
await TestUtils.waitForTick();
|
||||
|
||||
Assert.ok(SidebarController.isOpen, "Sidebar is open now");
|
||||
|
||||
await withReviewCheckerSidebar(async _args => {
|
||||
let shoppingContainer = await ContentTaskUtils.waitForCondition(
|
||||
() =>
|
||||
content.document.querySelector("shopping-container")?.wrappedJSObject,
|
||||
"Review Checker is loaded."
|
||||
);
|
||||
|
||||
await shoppingContainer.updateComplete;
|
||||
info("Shopping container update complete");
|
||||
|
||||
let keepClosedPromise = ContentTaskUtils.waitForCondition(
|
||||
() => shoppingContainer.keepClosedMessageBarEl,
|
||||
"Keep closed message is visible."
|
||||
);
|
||||
|
||||
shoppingContainer.closeButtonEl.click();
|
||||
|
||||
await keepClosedPromise;
|
||||
});
|
||||
|
||||
let nonPDPTab = BrowserTestUtils.addTab(gBrowser, NON_PDP_PAGE);
|
||||
let nonPDPBrowser = nonPDPTab.linkedBrowser;
|
||||
let browserLoadedPromise = BrowserTestUtils.browserLoaded(
|
||||
nonPDPBrowser,
|
||||
false,
|
||||
NON_PDP_PAGE
|
||||
);
|
||||
await browserLoadedPromise;
|
||||
|
||||
info("Switching tabs now");
|
||||
await BrowserTestUtils.switchTab(gBrowser, nonPDPTab);
|
||||
|
||||
Assert.ok(true, "Browser is loaded");
|
||||
await SidebarController.show("viewReviewCheckerSidebar");
|
||||
|
||||
await withReviewCheckerSidebar(async _args => {
|
||||
let shoppingContainer = await ContentTaskUtils.waitForCondition(
|
||||
() =>
|
||||
content.document.querySelector("shopping-container")?.wrappedJSObject,
|
||||
"Review Checker is loaded."
|
||||
);
|
||||
|
||||
await shoppingContainer.updateComplete;
|
||||
|
||||
Assert.ok(
|
||||
!shoppingContainer.keepClosedMessageBarEl,
|
||||
"'Keep closed' message is not visible before close button click"
|
||||
);
|
||||
|
||||
shoppingContainer.closeButtonEl.click();
|
||||
|
||||
let showKeepSidebarClosedMessage = Services.prefs.getBoolPref(
|
||||
"browser.shopping.experience2023.showKeepSidebarClosedMessage"
|
||||
);
|
||||
|
||||
Assert.ok(
|
||||
showKeepSidebarClosedMessage,
|
||||
"browser.shopping.experience2023.showKeepSidebarClosedMessage is true"
|
||||
);
|
||||
});
|
||||
|
||||
Assert.ok(
|
||||
!SidebarController.isOpen,
|
||||
"'Keep closed' message did not prevent sidebar from closing"
|
||||
);
|
||||
|
||||
await BrowserTestUtils.removeTab(nonPDPTab);
|
||||
});
|
||||
SidebarController.hide();
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
|
|
@ -9,6 +9,10 @@ const BACKUP_STATE_PREF = "sidebar.backupState";
|
|||
const VISIBILITY_SETTING_PREF = "sidebar.visibility";
|
||||
const SIDEBAR_TOOLS = "sidebar.main.tools";
|
||||
|
||||
// New panels that are ready to be introduced to new sidebar users should be added to this list;
|
||||
// ensure your feature flag is enabled at the same time you do this and that its the same value as
|
||||
// what you added to .
|
||||
const DEFAULT_LAUNCHER_TOOLS = "aichat,syncedtabs,history,bookmarks";
|
||||
const lazy = {};
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
|
@ -40,7 +44,15 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
|||
() => SidebarManager.updateDefaultTools()
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(lazy, "sidebarTools", SIDEBAR_TOOLS);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(lazy, "sidebarTools", SIDEBAR_TOOLS, "");
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
lazy,
|
||||
"newSidebarHasBeenUsed",
|
||||
"sidebar.new-sidebar.has-used",
|
||||
false,
|
||||
() => SidebarManager.updateDefaultTools()
|
||||
);
|
||||
|
||||
export const SidebarManager = {
|
||||
/**
|
||||
|
@ -84,7 +96,7 @@ export const SidebarManager = {
|
|||
}
|
||||
};
|
||||
setPref("nimbus", slug);
|
||||
["main.tools", "revamp", "verticalTabs", "visibility"].forEach(pref =>
|
||||
["revamp", "verticalTabs", "visibility"].forEach(pref =>
|
||||
setPref(pref, lazy.NimbusFeatures[featureId].getVariable(pref))
|
||||
);
|
||||
});
|
||||
|
@ -119,16 +131,20 @@ export const SidebarManager = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Appends any new tools defined on the sidebar.newTool.migration pref branch
|
||||
* to the sidebar.main.tools pref one time as a way of introducing a new tool
|
||||
* to the launcher without overwriting what a user had previously customized.
|
||||
* Prepopulates default tools for new sidebar users and appends any new tools defined
|
||||
* on the sidebar.newTool.migration pref branch to the sidebar.main.tools pref.
|
||||
*/
|
||||
updateDefaultTools() {
|
||||
if (!lazy.sidebarRevampEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tools = lazy.sidebarTools;
|
||||
|
||||
// For new sidebar.revamp users, we pre-populate a set of default tools to show in the launcher.
|
||||
if (!tools && !lazy.newSidebarHasBeenUsed) {
|
||||
tools = DEFAULT_LAUNCHER_TOOLS;
|
||||
}
|
||||
|
||||
for (const pref of Services.prefs.getChildList(
|
||||
"sidebar.newTool.migration."
|
||||
)) {
|
||||
|
@ -136,8 +152,7 @@ export const SidebarManager = {
|
|||
let options = JSON.parse(Services.prefs.getStringPref(pref));
|
||||
let newTool = pref.split(".")[3];
|
||||
|
||||
// ensure we only add this tool once
|
||||
if (options?.alreadyShown || lazy.sidebarTools.includes(newTool)) {
|
||||
if (options?.alreadyShown) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -155,7 +170,10 @@ export const SidebarManager = {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
tools = tools + "," + newTool;
|
||||
// avoid adding a tool from the pref branch where it's already been added to the DEFAULT_LAUNCHER_TOOLS (for new users)
|
||||
if (!tools.includes(newTool)) {
|
||||
tools += "," + newTool;
|
||||
}
|
||||
options.alreadyShown = true;
|
||||
Services.prefs.setStringPref(pref, JSON.stringify(options));
|
||||
} catch (ex) {
|
||||
|
|
|
@ -15,12 +15,12 @@ const { DeferredTask } = ChromeUtils.importESModule(
|
|||
"resource://gre/modules/DeferredTask.sys.mjs"
|
||||
);
|
||||
|
||||
const defaultTools = {
|
||||
const toolsNameMap = {
|
||||
viewGenaiChatSidebar: "aichat",
|
||||
viewReviewCheckerSidebar: "reviewchecker",
|
||||
viewTabsSidebar: "syncedtabs",
|
||||
viewHistorySidebar: "history",
|
||||
viewBookmarksSidebar: "bookmarks",
|
||||
viewReviewCheckerSidebar: "reviewchecker",
|
||||
viewCPMSidebar: "passwords",
|
||||
};
|
||||
const EXPAND_ON_HOVER_DEBOUNCE_RATE_MS = 200;
|
||||
|
@ -203,6 +203,7 @@ var SidebarController = {
|
|||
revampL10nId: "sidebar-menu-customize-label",
|
||||
iconUrl: "chrome://global/skin/icons/settings.svg",
|
||||
gleanEvent: Glean.sidebarCustomize.panelToggle,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
return this._sidebars;
|
||||
|
@ -1316,7 +1317,7 @@ var SidebarController = {
|
|||
let changed = false;
|
||||
const tools = new Set(this.sidebarRevampTools.split(","));
|
||||
this.toolsAndExtensions.forEach((tool, commandID) => {
|
||||
const toolID = defaultTools[commandID];
|
||||
const toolID = toolsNameMap[commandID];
|
||||
if (toolID) {
|
||||
const expected = !tools.has(toolID);
|
||||
if (tool.disabled != expected) {
|
||||
|
@ -1346,13 +1347,13 @@ var SidebarController = {
|
|||
// Tools are persisted via a pref.
|
||||
if (!Object.hasOwn(toggledTool, "extensionId")) {
|
||||
const tools = new Set(this.sidebarRevampTools.split(","));
|
||||
const updatedTools = tools.has(defaultTools[commandID])
|
||||
const updatedTools = tools.has(toolsNameMap[commandID])
|
||||
? Array.from(tools).filter(
|
||||
tool => !!tool && tool != defaultTools[commandID]
|
||||
tool => !!tool && tool != toolsNameMap[commandID]
|
||||
)
|
||||
: [
|
||||
...Array.from(tools).filter(tool => !!tool),
|
||||
defaultTools[commandID],
|
||||
toolsNameMap[commandID],
|
||||
];
|
||||
Services.prefs.setStringPref(this.TOOLS_PREF, updatedTools.join());
|
||||
}
|
||||
|
@ -1532,13 +1533,13 @@ var SidebarController = {
|
|||
* @returns {Array}
|
||||
*/
|
||||
getTools() {
|
||||
return Object.keys(defaultTools)
|
||||
return Object.keys(toolsNameMap)
|
||||
.filter(commandID => this.sidebars.get(commandID))
|
||||
.map(commandID => {
|
||||
const sidebar = this.sidebars.get(commandID);
|
||||
const disabled = !this.sidebarRevampTools
|
||||
.split(",")
|
||||
.includes(defaultTools[commandID]);
|
||||
.includes(toolsNameMap[commandID]);
|
||||
return {
|
||||
commandID,
|
||||
view: commandID,
|
||||
|
@ -2117,7 +2118,7 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
|||
SidebarController,
|
||||
"sidebarRevampTools",
|
||||
"sidebar.main.tools",
|
||||
"aichat,syncedtabs,history",
|
||||
"",
|
||||
() => {
|
||||
if (
|
||||
!SidebarController.inSingleTabWindow &&
|
||||
|
|
|
@ -9,12 +9,14 @@ The new sidebar builds on existing legacy sidebar code treating ``browser-sideba
|
|||
Introducing a new panel
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Every panel that is registered and enabled in ``browser-sidebar.js``` and the ```defaultTools``` map will show as an option in the Customize Sidebar menu (which is a sidebar panel that contains settings).
|
||||
Every panel that is registered and enabled in ``browser-sidebar.js``` and the ```toolsNameMap``` map will show as an option in the Customize Sidebar menu (which is a sidebar panel that contains settings).
|
||||
|
||||
The launcher is a container for tools (ie, icons that when clicked open or close the associated panel). Registering a panel - which should be behind a pref until it is ready to be introduced - does not automatically add a new icon to the launcher.
|
||||
|
||||
A tool can be added once for all users by adding it to the designated pref branch ``sidebar.newTool.migration.`` in ``profile/firefox.js``. So an example would be ``pref("sidebar.newTool.migration.bookmarks", '{}')``. The pref suffix (``bookmarks`` in this example) is the ``toolID`` that should match what you added as the value portion of the relevant entry in the ``defaultTools`` map in ``browser-sidebar.js``. It's important to note that if you have a pref governing the visibility of your sidebar panel, it will need to be enabled at the same time in order to be shown in a user's launcher - either via a nimbus rollout or in-tree.
|
||||
A tool can be added once for all users by adding it to the designated pref branch ``sidebar.newTool.migration.`` in ``profile/firefox.js``. So an example would be ``pref("sidebar.newTool.migration.bookmarks", '{}')``. The pref suffix (``bookmarks`` in this example) is the ``toolID`` that should match what you added as the value portion of the relevant entry in the ``toolsNameMap`` map in ``browser-sidebar.js``. It's important to note that if you have a pref governing the visibility of your sidebar panel, it will need to be enabled at the same time in order to be shown in a user's launcher - either via a nimbus rollout or in-tree.
|
||||
|
||||
If you only want to add this item if the pref governing visibility is true, you can pass the pref you want to observe, e.g. ``pref("sidebar.newTool.migration.reviewchecker", '{ "visibilityPref": "browser.shopping.experience2023.integratedSidebar"}')`` where ``browser.shopping.experience2023.integratedSidebar`` is the pref controlling the visibility of the review checker panel.
|
||||
|
||||
In both cases, the tool will be introduced to the launcher one time (appended to a user's customized list of tools) and any customization after that (ie, removing it) takes precedence. If it's not removed, it will persist after that session.
|
||||
|
||||
If you only want to introduce a tool to new users, you can do so by adding it to the ``DEFAULT_LAUNCHER_TOOLS`` list in ``SidebarManager`` and the ``toolsNameMap``. You can do this even if you have previously introduced a tool via a pref branch migration as there is logic that will prevent a tool from being added twice, however the expectation is that when adding it to ``defaultTools`` the pref governing panel visibility is also enabled in-tree.
|
||||
|
|
|
@ -23,12 +23,6 @@ skip-if = [
|
|||
["browser_extensions_sidebar.js"]
|
||||
|
||||
["browser_glean_sidebar.js"]
|
||||
skip-if = [
|
||||
"os == 'linux' && os_version == '18.04' && processor == 'x86_64'", # Bug 1919183
|
||||
"os == 'mac' && os_version == '10.15' && processor == 'x86_64'", # Bug 1919183
|
||||
"os == 'mac' && os_version == '11.20' && arch == 'aarch64' && opt", # Bug 1919183
|
||||
"os == 'win' && os_version == '11.26100'", # Bug 1919183
|
||||
]
|
||||
|
||||
["browser_hide_sidebar_on_popup.js"]
|
||||
|
||||
|
@ -66,15 +60,6 @@ run-if = ["os == 'mac'"] # Mac only feature
|
|||
["browser_syncedtabs_sidebar.js"]
|
||||
|
||||
["browser_toolbar_sidebar_button.js"]
|
||||
skip-if = [
|
||||
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && asan && swgl", # Bug 1898739
|
||||
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && opt", # Bug 1898739
|
||||
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && debug && swgl", # Bug 1898739
|
||||
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && debug && socketprocess_networking", # Bug 1898739
|
||||
"os == 'mac' && os_version == '10.15' && processor == 'x86_64' && opt", # Bug 1898739
|
||||
"os == 'mac' && os_version == '11.20' && arch == 'aarch64' && opt", # Bug 1898739
|
||||
"os == 'win' && os_version == '11.26100' && opt", # Bug 1898739
|
||||
]
|
||||
|
||||
["browser_tools_migration.js"]
|
||||
|
||||
|
|
|
@ -66,13 +66,7 @@ add_task(async function test_customize_sidebar_actions() {
|
|||
4,
|
||||
"Four default tools are shown in the customize menu"
|
||||
);
|
||||
let bookmarksInput = Array.from(customizeComponent.toolInputs).find(
|
||||
input => input.name === "viewBookmarksSidebar"
|
||||
);
|
||||
ok(
|
||||
!bookmarksInput.checked,
|
||||
"The bookmarks input is unchecked initally as Bookmarks are disabled initially."
|
||||
);
|
||||
|
||||
for (const toolInput of customizeComponent.toolInputs) {
|
||||
let toolDisabledInitialState = !toolInput.checked;
|
||||
toolInput.click();
|
||||
|
|
|
@ -361,8 +361,7 @@ add_task(async function test_customize_history_enabled() {
|
|||
add_task(async function test_customize_bookmarks_enabled() {
|
||||
await testCustomizeToggle(
|
||||
"viewBookmarksSidebar",
|
||||
Glean.sidebarCustomize.bookmarksEnabled,
|
||||
false
|
||||
Glean.sidebarCustomize.bookmarksEnabled
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -589,7 +588,6 @@ async function testIconClick(expanded) {
|
|||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.ml.chat.enabled", true],
|
||||
["sidebar.main.tools", "aichat,syncedtabs,history,bookmarks"],
|
||||
[TAB_DIRECTION_PREF, true],
|
||||
],
|
||||
});
|
||||
|
|
|
@ -9,11 +9,9 @@ const { ExperimentFakes } = ChromeUtils.importESModule(
|
|||
* Check that enrolling into sidebar experiments sets user prefs
|
||||
*/
|
||||
add_task(async function test_nimbus_user_prefs() {
|
||||
const main = "sidebar.main.tools";
|
||||
const nimbus = "sidebar.nimbus";
|
||||
const vertical = "sidebar.verticalTabs";
|
||||
|
||||
Assert.ok(!Services.prefs.prefHasUserValue(main), "No user main pref yet");
|
||||
Assert.ok(
|
||||
!Services.prefs.prefHasUserValue(nimbus),
|
||||
"No user nimbus pref yet"
|
||||
|
@ -22,55 +20,13 @@ add_task(async function test_nimbus_user_prefs() {
|
|||
let cleanup = await ExperimentFakes.enrollWithFeatureConfig({
|
||||
featureId: "sidebar",
|
||||
value: {
|
||||
"main.tools": "bar",
|
||||
},
|
||||
});
|
||||
|
||||
Assert.equal(
|
||||
Services.prefs.getStringPref(main),
|
||||
"bar",
|
||||
"Set user pref with experiment"
|
||||
);
|
||||
Assert.ok(Services.prefs.prefHasUserValue(main), "main pref has user value");
|
||||
const nimbusValue = Services.prefs.getStringPref(nimbus);
|
||||
Assert.ok(nimbusValue, "Set some nimbus slug");
|
||||
Assert.ok(
|
||||
Services.prefs.prefHasUserValue(nimbus),
|
||||
"nimbus pref has user value"
|
||||
);
|
||||
|
||||
cleanup();
|
||||
|
||||
Assert.equal(
|
||||
Services.prefs.getStringPref(main),
|
||||
"bar",
|
||||
"main pref still set"
|
||||
);
|
||||
Assert.equal(
|
||||
Services.prefs.getStringPref(nimbus),
|
||||
nimbusValue,
|
||||
"nimbus pref still set"
|
||||
);
|
||||
Assert.ok(!Services.prefs.getBoolPref(vertical), "vertical is default value");
|
||||
Assert.ok(
|
||||
!Services.prefs.prefHasUserValue(vertical),
|
||||
"vertical used default value"
|
||||
);
|
||||
|
||||
cleanup = await ExperimentFakes.enrollWithFeatureConfig({
|
||||
featureId: "sidebar",
|
||||
value: {
|
||||
"main.tools": "aichat,syncedtabs,history",
|
||||
verticalTabs: true,
|
||||
},
|
||||
});
|
||||
|
||||
Assert.ok(!Services.prefs.prefHasUserValue(main), "main pref no longer set");
|
||||
Assert.notEqual(
|
||||
Services.prefs.getStringPref(nimbus),
|
||||
nimbusValue,
|
||||
"nimbus pref changed"
|
||||
);
|
||||
const nimbusValue = Services.prefs.getStringPref(nimbus);
|
||||
|
||||
Assert.ok(nimbusValue, "Set some nimbus slug");
|
||||
Assert.ok(Services.prefs.getBoolPref(vertical), "vertical set to true");
|
||||
Assert.ok(
|
||||
Services.prefs.prefHasUserValue(vertical),
|
||||
|
@ -123,13 +79,11 @@ add_task(async function test_nimbus_rollout_experiment() {
|
|||
});
|
||||
|
||||
/**
|
||||
* Check that multi-feature sidebar and chatbot sets prefs
|
||||
* Check that multi-feature chatbot sets prefs
|
||||
*/
|
||||
add_task(async function test_nimbus_multi_feature() {
|
||||
const chatbot = "browser.ml.chat.test";
|
||||
const sidebar = "sidebar.main.tools";
|
||||
Assert.ok(!Services.prefs.prefHasUserValue(chatbot), "chatbot is default");
|
||||
Assert.ok(!Services.prefs.prefHasUserValue(sidebar), "sidebar is default");
|
||||
|
||||
const cleanup = await ExperimentFakes.enrollmentHelper(
|
||||
ExperimentFakes.recipe("foo", {
|
||||
|
@ -137,10 +91,6 @@ add_task(async function test_nimbus_multi_feature() {
|
|||
{
|
||||
slug: "variant",
|
||||
features: [
|
||||
{
|
||||
featureId: "sidebar",
|
||||
value: { "main.tools": "syncedtabs,history" },
|
||||
},
|
||||
{
|
||||
featureId: "chatbot",
|
||||
value: { prefs: { test: { value: true } } },
|
||||
|
@ -152,15 +102,12 @@ add_task(async function test_nimbus_multi_feature() {
|
|||
);
|
||||
|
||||
Assert.ok(Services.prefs.prefHasUserValue(chatbot), "chatbot user pref set");
|
||||
Assert.ok(Services.prefs.prefHasUserValue(sidebar), "sidebar user pref set");
|
||||
|
||||
cleanup();
|
||||
|
||||
Assert.ok(Services.prefs.prefHasUserValue(chatbot), "chatbot pref still set");
|
||||
Assert.ok(Services.prefs.prefHasUserValue(sidebar), "sidebar pref still set");
|
||||
|
||||
Services.prefs.clearUserPref(chatbot);
|
||||
Services.prefs.clearUserPref(sidebar);
|
||||
Services.prefs.clearUserPref("browser.ml.chat.nimbus");
|
||||
Services.prefs.clearUserPref("sidebar.nimbus");
|
||||
});
|
||||
|
|
|
@ -7,12 +7,11 @@ add_task(async function test_tools_prefs() {
|
|||
const win = await BrowserTestUtils.openNewBrowserWindow();
|
||||
const { document } = win;
|
||||
const sidebar = document.querySelector("sidebar-main");
|
||||
ok(sidebar, "Sidebar is shown.");
|
||||
await sidebar.updateComplete;
|
||||
|
||||
is(
|
||||
Services.prefs.getStringPref("sidebar.main.tools"),
|
||||
"aichat,syncedtabs,history",
|
||||
"aichat,syncedtabs,history,bookmarks",
|
||||
"Default tools pref unchanged"
|
||||
);
|
||||
|
||||
|
@ -35,11 +34,14 @@ add_task(async function test_tools_prefs() {
|
|||
input => input.name === "viewBookmarksSidebar"
|
||||
);
|
||||
ok(
|
||||
!bookmarksInput.checked,
|
||||
"The bookmarks input is unchecked initially as Bookmarks are disabled initially."
|
||||
bookmarksInput.checked,
|
||||
"The bookmarks input is checked initially as Bookmarks is a default tool."
|
||||
);
|
||||
for (const toolInput of customizeComponent.toolInputs) {
|
||||
let toolDisabledInitialState = !toolInput.checked;
|
||||
if (toolInput.name == "viewBookmarksSidebar") {
|
||||
continue;
|
||||
}
|
||||
toolInput.click();
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() => {
|
||||
|
@ -67,7 +69,7 @@ add_task(async function test_tools_prefs() {
|
|||
is(
|
||||
updatedTools,
|
||||
"bookmarks",
|
||||
"History, aichat and syncedtabs have been removed from the pref, and bookmarks added"
|
||||
"All tools have been removed from the launcher except bookmarks"
|
||||
);
|
||||
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
|
@ -143,11 +145,13 @@ add_task(async function test_tool_pref_change() {
|
|||
});
|
||||
is(sidebar.toolButtons.length, origCount - 1, "Removed tool");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({ set: [["sidebar.main.tools", origTools]] });
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["sidebar.main.tools", origTools]],
|
||||
});
|
||||
is(sidebar.toolButtons.length, origCount, "Restored tool");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({ clear: [["sidebar.main.tools"]] });
|
||||
is(sidebar.toolButtons.length, 3, "Restored default tools");
|
||||
is(sidebar.toolButtons.length, 0, "Cleared default tools");
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
add_setup(async () => {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["sidebar.main.tools", "syncedtabs,history"],
|
||||
["sidebar.main.tools", "syncedtabs,bookmarks,history"],
|
||||
["sidebar.newTool.migration.bookmarks", "{}"],
|
||||
["browser.ml.chat.enabled", false],
|
||||
[
|
||||
|
@ -19,7 +19,29 @@ add_setup(async () => {
|
|||
});
|
||||
});
|
||||
|
||||
add_task(async function test_duplicate_tool() {
|
||||
const sidebar = document.querySelector("sidebar-main");
|
||||
let tools = Services.prefs.getStringPref("sidebar.main.tools").split(",");
|
||||
is(tools.length, 3, "Three tools are in the sidebar.main.tools pref");
|
||||
is(
|
||||
tools.filter(tool => tool == "bookmarks").length,
|
||||
1,
|
||||
"Bookmarks has only been added once"
|
||||
);
|
||||
is(
|
||||
sidebar.toolButtons.length,
|
||||
3,
|
||||
"Three default tools are visible in the launcher"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_one_time_tool_migration() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["sidebar.main.tools", "syncedtabs,history"],
|
||||
["sidebar.newTool.migration.bookmarks", "{}"],
|
||||
],
|
||||
});
|
||||
const sidebar = document.querySelector("sidebar-main");
|
||||
let tools = Services.prefs.getStringPref("sidebar.main.tools");
|
||||
is(
|
||||
|
|
|
@ -19,13 +19,11 @@ const kPrefCustomizationHorizontalTabstrip =
|
|||
"browser.uiCustomization.horizontalTabstrip";
|
||||
const kPrefCustomizationNavBarWhenVerticalTabs =
|
||||
"browser.uiCustomization.navBarWhenVerticalTabs";
|
||||
const kPrefSidebarTools = "sidebar.main.tools";
|
||||
|
||||
const MODIFIED_PREFS = Object.freeze([
|
||||
kPrefCustomizationState,
|
||||
kPrefCustomizationHorizontalTabstrip,
|
||||
kPrefCustomizationNavBarWhenVerticalTabs,
|
||||
kPrefSidebarTools,
|
||||
]);
|
||||
|
||||
// Ensure we clear any previous pref values
|
||||
|
|
|
@ -678,7 +678,7 @@ export class SmartTabGroupingManager {
|
|||
|
||||
const UPDATE_THRESHOLD_PERCENTAGE = 0.5;
|
||||
const ONE_MB = 1024 * 1024;
|
||||
const START_THRESHOLD_BYTES = ONE_MB;
|
||||
const START_THRESHOLD_BYTES = ONE_MB * 0.2;
|
||||
|
||||
const mutliProgressAggregator = new lazy.MultiProgressAggregator({
|
||||
progressCallback: ({ progress, totalLoaded, metadata }) => {
|
||||
|
|
|
@ -5967,6 +5967,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug 1955388 - prevent pinned tabs from commingling with non-pinned tabs
|
||||
* when there are hidden tabs present
|
||||
*/
|
||||
if (tab.pinned && !targetElement?.pinned) {
|
||||
// prevent pinned tab from being dragged past a non-pinned tab
|
||||
targetElement = this.tabs[this.pinnedTabCount - 1];
|
||||
moveBefore = false;
|
||||
}
|
||||
|
||||
let getContainer = () => {
|
||||
if (tab.pinned && this.tabContainer.verticalMode) {
|
||||
return this.tabContainer.verticalPinnedTabsContainer;
|
||||
|
|
|
@ -1017,8 +1017,29 @@
|
|||
}
|
||||
|
||||
#setMovingTabMode(movingTab) {
|
||||
if (movingTab == this.#isMovingTab()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.toggleAttribute("movingtab", movingTab);
|
||||
gNavToolbox.toggleAttribute("movingtab", movingTab);
|
||||
|
||||
if (movingTab) {
|
||||
// This is a bit of an escape hatch in case a tab drag & drop session
|
||||
// wasn't ended properly, leaving behind the movingtab attribute, which
|
||||
// may break the UI (bug 1954163). We don't get mousemove events while
|
||||
// dragging tabs, so at that point it should be safe to assume that we
|
||||
// should not be in drag and drop mode, and clean things up if needed.
|
||||
requestAnimationFrame(() => {
|
||||
this.addEventListener(
|
||||
"mousemove",
|
||||
() => {
|
||||
this.finishAnimateTabMove();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#isMovingTab() {
|
||||
|
@ -1137,7 +1158,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
let shouldTranslate = !gReduceMotion && !shouldCreateGroupOnDrop;
|
||||
let shouldTranslate =
|
||||
!gReduceMotion &&
|
||||
!shouldCreateGroupOnDrop &&
|
||||
!isTabGroupLabel(draggedTab);
|
||||
if (this.#isContainerVerticalPinnedExpanded(draggedTab)) {
|
||||
shouldTranslate &&=
|
||||
(oldTranslateX && oldTranslateX != newTranslateX) ||
|
||||
|
@ -1161,6 +1185,7 @@
|
|||
} else {
|
||||
gBrowser.moveTabsAfter(movingTabs, dropElement);
|
||||
}
|
||||
this.#expandGroupOnDrop(draggedTab);
|
||||
};
|
||||
|
||||
if (shouldTranslate) {
|
||||
|
@ -1313,7 +1338,6 @@
|
|||
}
|
||||
|
||||
if (draggedTab) {
|
||||
this.#expandGroupOnDrop(draggedTab);
|
||||
delete draggedTab._dragData;
|
||||
}
|
||||
}
|
||||
|
@ -1336,17 +1360,13 @@
|
|||
if (
|
||||
dt.mozUserCancelled ||
|
||||
dt.dropEffect != "none" ||
|
||||
!Services.prefs.getBoolPref("browser.tabs.allowTabDetach") ||
|
||||
this._isCustomizing
|
||||
) {
|
||||
delete draggedTab._dragData;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if tab detaching is enabled
|
||||
if (!Services.prefs.getBoolPref("browser.tabs.allowTabDetach")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable detach within the browser toolbox
|
||||
let [tabAxisPos, tabAxisStart, tabAxisEnd] = this.verticalMode
|
||||
? [event.screenY, window.screenY, window.screenY + window.outerHeight]
|
||||
|
|
|
@ -61,7 +61,14 @@ let currentTab = () =>
|
|||
lazy.BrowserWindowTracker.getTopWindow()?.gBrowser.selectedTab;
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "gFluentStrings", function () {
|
||||
return new Localization(["branding/brand.ftl", "browser/browser.ftl"], true);
|
||||
return new Localization(
|
||||
[
|
||||
"branding/brand.ftl",
|
||||
"browser/browser.ftl",
|
||||
"toolkit/branding/brandings.ftl",
|
||||
],
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
const DEFAULT_ACTIONS = {
|
||||
|
@ -83,10 +90,10 @@ const DEFAULT_ACTIONS = {
|
|||
},
|
||||
clear: {
|
||||
l10nCommands: [
|
||||
"quickactions-cmd-clearhistory",
|
||||
"quickactions-clearhistory",
|
||||
"quickactions-cmd-clearrecenthistory",
|
||||
"quickactions-clearrecenthistory",
|
||||
],
|
||||
label: "quickactions-clearhistory",
|
||||
label: "quickactions-clearrecenthistory",
|
||||
onPick: () => {
|
||||
lazy.BrowserWindowTracker.getTopWindow()
|
||||
.document.getElementById("Tools:Sanitize")
|
||||
|
@ -105,6 +112,22 @@ const DEFAULT_ACTIONS = {
|
|||
label: "quickactions-extensions",
|
||||
onPick: openAddonsUrl("addons://list/extension"),
|
||||
},
|
||||
help: {
|
||||
l10nCommands: ["quickactions-cmd-help"],
|
||||
icon: "chrome://global/skin/icons/help.svg",
|
||||
label: "quickactions-help",
|
||||
onPick: openUrlFun(
|
||||
"https://support.mozilla.org/products/firefox?as=u&utm_source=inproduct"
|
||||
),
|
||||
},
|
||||
firefoxview: {
|
||||
l10nCommands: ["quickactions-cmd-firefoxview"],
|
||||
icon: "chrome://browser/skin/firefox-view.svg",
|
||||
label: "quickactions-firefoxview",
|
||||
onPick: () => {
|
||||
lazy.BrowserWindowTracker.getTopWindow().FirefoxViewHandler.openTab();
|
||||
},
|
||||
},
|
||||
inspect: {
|
||||
l10nCommands: ["quickactions-cmd-inspector"],
|
||||
icon: "chrome://devtools/skin/images/open-inspector.svg",
|
||||
|
|
|
@ -125,6 +125,7 @@ add_task(async function test_viewsource() {
|
|||
"view-source:https://example.com/"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Tab", {}, window);
|
||||
EventUtils.synthesizeKey("KEY_Tab", {}, window);
|
||||
assertAccessibilityWhenSelected("viewsource");
|
||||
EventUtils.synthesizeKey("KEY_Enter", {}, window);
|
||||
const viewSourceTab = await onLoad;
|
||||
|
@ -136,8 +137,10 @@ add_task(async function test_viewsource() {
|
|||
});
|
||||
|
||||
Assert.equal(
|
||||
hasQuickActions(window),
|
||||
false,
|
||||
window.document.querySelector(
|
||||
`.urlbarView-action-btn[data-action=viewsource]`
|
||||
),
|
||||
null,
|
||||
"Result for quick actions is hidden"
|
||||
);
|
||||
|
||||
|
|
|
@ -16,7 +16,26 @@ add_setup(async function setup() {
|
|||
});
|
||||
});
|
||||
|
||||
const LOAD_TYPE = {
|
||||
CURRENT_TAB: 1,
|
||||
NEW_TAB: 2,
|
||||
PRE_LOADED: 3,
|
||||
};
|
||||
|
||||
let COMMANDS_TESTS = [
|
||||
{
|
||||
cmd: "open view",
|
||||
uri: "about:firefoxview",
|
||||
loadType: LOAD_TYPE.PRE_LOADED,
|
||||
testFun: async () => {
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
return (
|
||||
window.gBrowser.selectedBrowser.currentURI.spec == "about:firefoxview"
|
||||
);
|
||||
});
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
cmd: "add-ons",
|
||||
uri: "about:addons",
|
||||
|
@ -52,7 +71,7 @@ let COMMANDS_TESTS = [
|
|||
await onLoad;
|
||||
},
|
||||
uri: "about:addons",
|
||||
isNewTab: true,
|
||||
loadType: LOAD_TYPE.NEW_TAB,
|
||||
testFun: async () => isSelected("button[name=discover]"),
|
||||
},
|
||||
{
|
||||
|
@ -70,7 +89,7 @@ let COMMANDS_TESTS = [
|
|||
await onLoad;
|
||||
},
|
||||
uri: "about:addons",
|
||||
isNewTab: true,
|
||||
loadType: LOAD_TYPE.NEW_TAB,
|
||||
testFun: async () => isSelected("button[name=plugin]"),
|
||||
},
|
||||
{
|
||||
|
@ -88,7 +107,7 @@ let COMMANDS_TESTS = [
|
|||
await onLoad;
|
||||
},
|
||||
uri: "about:addons",
|
||||
isNewTab: true,
|
||||
loadType: LOAD_TYPE.NEW_TAB,
|
||||
testFun: async () => isSelected("button[name=extension]"),
|
||||
},
|
||||
{
|
||||
|
@ -106,7 +125,7 @@ let COMMANDS_TESTS = [
|
|||
await onLoad;
|
||||
},
|
||||
uri: "about:addons",
|
||||
isNewTab: true,
|
||||
loadType: LOAD_TYPE.NEW_TAB,
|
||||
testFun: async () => isSelected("button[name=theme]"),
|
||||
},
|
||||
];
|
||||
|
@ -119,7 +138,7 @@ let isSelected = async selector =>
|
|||
});
|
||||
|
||||
add_task(async function test_pages() {
|
||||
for (const { cmd, uri, setup, isNewTab, testFun } of COMMANDS_TESTS) {
|
||||
for (const { cmd, uri, setup, loadType, testFun } of COMMANDS_TESTS) {
|
||||
info(`Testing ${cmd} command is triggered`);
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
|
||||
|
@ -128,9 +147,10 @@ add_task(async function test_pages() {
|
|||
await setup();
|
||||
}
|
||||
|
||||
let onLoad = isNewTab
|
||||
? BrowserTestUtils.waitForNewTab(gBrowser, uri, true)
|
||||
: BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, uri);
|
||||
let onLoad =
|
||||
loadType == LOAD_TYPE.NEW_TAB
|
||||
? BrowserTestUtils.waitForNewTab(gBrowser, uri, true)
|
||||
: BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, uri);
|
||||
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window,
|
||||
|
@ -139,14 +159,15 @@ add_task(async function test_pages() {
|
|||
EventUtils.synthesizeKey("KEY_Tab", {}, window);
|
||||
EventUtils.synthesizeKey("KEY_Enter", {}, window);
|
||||
|
||||
const newTab = await onLoad;
|
||||
const newTab =
|
||||
loadType == LOAD_TYPE.PRE_LOADED ? gBrowser.selectedTab : await onLoad;
|
||||
|
||||
Assert.ok(
|
||||
await testFun(),
|
||||
`The command "${cmd}" passed completed its test`
|
||||
);
|
||||
|
||||
if (isNewTab) {
|
||||
if ([LOAD_TYPE.NEW_TAB, LOAD_TYPE.PRE_LOADED].includes(loadType)) {
|
||||
await BrowserTestUtils.removeTab(newTab);
|
||||
}
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
|
|
|
@ -131,7 +131,10 @@ const HELP_URL =
|
|||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.search.suggest.enabled", false]],
|
||||
set: [
|
||||
["browser.search.suggest.enabled", false],
|
||||
["browser.urlbar.suggest.quickactions", false],
|
||||
],
|
||||
});
|
||||
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
|
|
|
@ -30,6 +30,8 @@ skip-if = [
|
|||
|
||||
["browser_autofill_address_housenumber.js"]
|
||||
|
||||
["browser_autofill_address_level.js"]
|
||||
|
||||
["browser_autofill_address_select.js"]
|
||||
|
||||
["browser_autofill_address_select_inexact.js"]
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_PROFILE_BR = {
|
||||
"given-name": "Carlos",
|
||||
"family-name": "Alves",
|
||||
"street-address": "160 Rua Acores\nApartment 300",
|
||||
"address-level1": "São Paulo",
|
||||
"address-level2": "Sampletown",
|
||||
"address-level3": "Somewhere",
|
||||
"postal-code": "04829-310",
|
||||
};
|
||||
|
||||
add_autofill_heuristic_tests([
|
||||
{
|
||||
description: "Test autofill with address-level3 autocomplete",
|
||||
fixtureData: `<form>
|
||||
<input id="name"/>
|
||||
<input id="address-line1"/>
|
||||
<input id="address-line2">
|
||||
<input id="address-level1">
|
||||
<input id="address-level2">
|
||||
<input id="postcode">
|
||||
<input id="extrainfo" autocomplete="address-level3">
|
||||
</form>`,
|
||||
profile: TEST_PROFILE_BR,
|
||||
expectedResult: [
|
||||
{
|
||||
default: {
|
||||
reason: "regex-heuristic",
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
fieldName: "name",
|
||||
autofill:
|
||||
TEST_PROFILE_BR["given-name"] +
|
||||
" " +
|
||||
TEST_PROFILE_BR["family-name"],
|
||||
},
|
||||
{ fieldName: "address-line1", autofill: "160 Rua Acores" },
|
||||
{
|
||||
fieldName: "address-line2",
|
||||
autofill: "Apartment 300",
|
||||
reason: "update-heuristic",
|
||||
},
|
||||
{
|
||||
fieldName: "address-level1",
|
||||
autofill: TEST_PROFILE_BR["address-level1"],
|
||||
},
|
||||
{
|
||||
fieldName: "address-level2",
|
||||
autofill: TEST_PROFILE_BR["address-level2"],
|
||||
},
|
||||
{
|
||||
fieldName: "postal-code",
|
||||
autofill: TEST_PROFILE_BR["postal-code"],
|
||||
},
|
||||
{
|
||||
fieldName: "address-level3",
|
||||
autofill: TEST_PROFILE_BR["address-level3"],
|
||||
reason: "autocomplete",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
description: "Test autofill with address-level3",
|
||||
fixtureData: `<form>
|
||||
<input id="name"/>
|
||||
<input id="address-line1"/>
|
||||
<input id="address-line2">
|
||||
<input id="address-level1">
|
||||
<input id="address-level2">
|
||||
<input id="address-level3">
|
||||
<input id="postcode">
|
||||
</form>`,
|
||||
profile: TEST_PROFILE_BR,
|
||||
expectedResult: [
|
||||
{
|
||||
default: {
|
||||
reason: "regex-heuristic",
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
fieldName: "name",
|
||||
autofill:
|
||||
TEST_PROFILE_BR["given-name"] +
|
||||
" " +
|
||||
TEST_PROFILE_BR["family-name"],
|
||||
},
|
||||
{ fieldName: "address-line1", autofill: "160 Rua Acores" },
|
||||
{
|
||||
fieldName: "address-line2",
|
||||
autofill: "Apartment 300",
|
||||
reason: "update-heuristic",
|
||||
},
|
||||
{
|
||||
fieldName: "address-level1",
|
||||
autofill: TEST_PROFILE_BR["address-level1"],
|
||||
},
|
||||
{
|
||||
fieldName: "address-level2",
|
||||
autofill: TEST_PROFILE_BR["address-level2"],
|
||||
},
|
||||
{
|
||||
fieldName: "address-level3",
|
||||
autofill: TEST_PROFILE_BR["address-level3"],
|
||||
},
|
||||
{
|
||||
fieldName: "postal-code",
|
||||
autofill: TEST_PROFILE_BR["postal-code"],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
description: "Test autofill with label for neighbourhood",
|
||||
fixtureData: `<form>
|
||||
<label>Nome <input id="field1"/></label>
|
||||
<label>Endereço <input id="field2"/></label>
|
||||
<label>Apartamento<input id="field3" autocomplete="address-line2"></label>
|
||||
<label>CEP <input id="field4"></label>
|
||||
<label>Bairro <input id="field5"></label>
|
||||
<label>Cidade <input id="field6"></label>
|
||||
<label>Estado <input id="field7"></label>
|
||||
</form>`,
|
||||
profile: TEST_PROFILE_BR,
|
||||
expectedResult: [
|
||||
{
|
||||
default: {
|
||||
reason: "regex-heuristic",
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
fieldName: "name",
|
||||
autofill:
|
||||
TEST_PROFILE_BR["given-name"] +
|
||||
" " +
|
||||
TEST_PROFILE_BR["family-name"],
|
||||
},
|
||||
{ fieldName: "address-line1", autofill: "160 Rua Acores" },
|
||||
{
|
||||
fieldName: "address-line2",
|
||||
autofill: "Apartment 300",
|
||||
reason: "autocomplete",
|
||||
},
|
||||
{
|
||||
fieldName: "postal-code",
|
||||
autofill: TEST_PROFILE_BR["postal-code"],
|
||||
},
|
||||
{
|
||||
fieldName: "address-level3",
|
||||
autofill: TEST_PROFILE_BR["address-level3"],
|
||||
},
|
||||
{
|
||||
fieldName: "address-level2",
|
||||
autofill: TEST_PROFILE_BR["address-level2"],
|
||||
},
|
||||
{
|
||||
fieldName: "address-level1",
|
||||
autofill: TEST_PROFILE_BR["address-level1"],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
|
@ -46,6 +46,8 @@ skip-if = ["os == 'mac' && os_version == '11.20' && arch == 'aarch64' && !debug"
|
|||
|
||||
["browser_parse_street_address_fields.js"]
|
||||
|
||||
["browser_parse_tel_fields.js"]
|
||||
|
||||
["browser_section_validation_address.js"]
|
||||
|
||||
["browser_sections_by_name.js"]
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* global add_heuristic_tests */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_heuristic_tests([
|
||||
{
|
||||
description:
|
||||
"Address form with tel-country-code select element (Bug 1951890).",
|
||||
fixtureData: `
|
||||
<form>
|
||||
<input type="text" id="name" autocomplete="name"/>
|
||||
<input type="text" id="country" autocomplete="country"/>
|
||||
<input type="text" id="street-address" autocomplete="street-address"/>
|
||||
<input type="text" id="address-line1" autocomplete="address-line1"/>
|
||||
<input type="tel" id="tel" autocomplete="tel"/>
|
||||
<select name="phone_country_select">
|
||||
</form>`,
|
||||
expectedResult: [
|
||||
{
|
||||
default: {
|
||||
reason: "autocomplete",
|
||||
},
|
||||
fields: [
|
||||
{ fieldName: "name" },
|
||||
{ fieldName: "country" },
|
||||
{ fieldName: "street-address" },
|
||||
{ fieldName: "address-line1" },
|
||||
{ fieldName: "tel" },
|
||||
{ fieldName: "tel-country-code", reason: "regex-heuristic" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
|
@ -61,7 +61,7 @@ add_heuristic_tests(
|
|||
{
|
||||
invalid: true,
|
||||
fields: [
|
||||
{ fieldName: "address-level2", reason: "regex-heuristic" },
|
||||
{ fieldName: "postal-code", reason: "regex-heuristic" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -147,7 +147,7 @@ add_heuristic_tests(
|
|||
{
|
||||
invalid: true,
|
||||
fields: [
|
||||
{ fieldName: "address-level2", reason: "regex-heuristic" },
|
||||
{ fieldName: "postal-code", reason: "regex-heuristic" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -616,9 +616,6 @@ export class BaseContent extends React.PureComponent {
|
|||
const enabledSections = {
|
||||
topSitesEnabled: prefs["feeds.topsites"],
|
||||
pocketEnabled: prefs["feeds.section.topstories"],
|
||||
highlightsEnabled: prefs["feeds.section.highlights"],
|
||||
showSponsoredTopSitesEnabled: prefs.showSponsoredTopSites,
|
||||
showSponsoredPocketEnabled: prefs.showSponsored,
|
||||
showInferredPersonalizationEnabled:
|
||||
prefs[PREF_INFERRED_PERSONALIZATION_USER],
|
||||
showRecentSavesEnabled: prefs.showRecentSaves,
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
import React from "react";
|
||||
import { actionCreators as ac } from "common/Actions.mjs";
|
||||
import { SectionsMgmtPanel } from "../SectionsMgmtPanel/SectionsMgmtPanel";
|
||||
import { SafeAnchor } from "../../DiscoveryStreamComponents/SafeAnchor/SafeAnchor";
|
||||
import { WallpapersSection } from "../../WallpapersSection/WallpapersSection";
|
||||
import { WallpaperCategories } from "../../WallpapersSection/WallpaperCategories";
|
||||
|
||||
|
@ -30,7 +29,7 @@ export class ContentSection extends React.PureComponent {
|
|||
}
|
||||
|
||||
onPreferenceSelect(e) {
|
||||
// eventSource: TOP_SITES | TOP_STORIES | HIGHLIGHTS | WEATHER
|
||||
// eventSource: WEATHER | TOP_SITES | TOP_STORIES
|
||||
const { preference, eventSource } = e.target.dataset;
|
||||
let value;
|
||||
if (e.target.nodeName === "SELECT") {
|
||||
|
@ -86,7 +85,7 @@ export class ContentSection extends React.PureComponent {
|
|||
if (isOpen) {
|
||||
drawerRef.style.marginTop = "var(--space-large)";
|
||||
} else {
|
||||
drawerRef.style.marginTop = `-${drawerHeight}px`;
|
||||
drawerRef.style.marginTop = `-${drawerHeight + 3}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,14 +93,11 @@ export class ContentSection extends React.PureComponent {
|
|||
render() {
|
||||
const {
|
||||
enabledSections,
|
||||
mayHaveSponsoredTopSites,
|
||||
pocketRegion,
|
||||
mayHaveSponsoredStories,
|
||||
mayHaveInferredPersonalization,
|
||||
mayHaveRecentSaves,
|
||||
mayHaveWeather,
|
||||
openPreferences,
|
||||
spocMessageVariant,
|
||||
wallpapersEnabled,
|
||||
wallpapersV2Enabled,
|
||||
activeWallpaper,
|
||||
|
@ -112,10 +108,7 @@ export class ContentSection extends React.PureComponent {
|
|||
const {
|
||||
topSitesEnabled,
|
||||
pocketEnabled,
|
||||
highlightsEnabled,
|
||||
weatherEnabled,
|
||||
showSponsoredTopSitesEnabled,
|
||||
showSponsoredPocketEnabled,
|
||||
showInferredPersonalizationEnabled,
|
||||
showRecentSavesEnabled,
|
||||
topSitesRowsCount,
|
||||
|
@ -144,6 +137,19 @@ export class ContentSection extends React.PureComponent {
|
|||
</>
|
||||
)}
|
||||
<div className="settings-toggles">
|
||||
{mayHaveWeather && (
|
||||
<div id="weather-section" className="section">
|
||||
<moz-toggle
|
||||
id="weather-toggle"
|
||||
pressed={weatherEnabled || null}
|
||||
onToggle={this.onPreferenceSelect}
|
||||
data-preference="showWeather"
|
||||
data-eventSource="WEATHER"
|
||||
data-l10n-id="newtab-custom-weather-toggle"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div id="shortcuts-section" className="section">
|
||||
<moz-toggle
|
||||
id="shortcuts-toggle"
|
||||
|
@ -190,25 +196,6 @@ export class ContentSection extends React.PureComponent {
|
|||
data-l10n-args='{"num": 4}'
|
||||
/>
|
||||
</select>
|
||||
{mayHaveSponsoredTopSites && (
|
||||
<div className="check-wrapper" role="presentation">
|
||||
<input
|
||||
id="sponsored-shortcuts"
|
||||
className="customize-menu-checkbox"
|
||||
disabled={!topSitesEnabled}
|
||||
checked={showSponsoredTopSitesEnabled}
|
||||
type="checkbox"
|
||||
onChange={this.onPreferenceSelect}
|
||||
data-preference="showSponsoredTopSites"
|
||||
data-eventSource="SPONSORED_TOP_SITES"
|
||||
/>
|
||||
<label
|
||||
className="customize-menu-checkbox-label"
|
||||
htmlFor="sponsored-shortcuts"
|
||||
data-l10n-id="newtab-custom-sponsored-sites"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -227,31 +214,14 @@ export class ContentSection extends React.PureComponent {
|
|||
data-l10n-id="newtab-custom-stories-toggle"
|
||||
>
|
||||
<div slot="nested">
|
||||
{(mayHaveSponsoredStories || mayHaveRecentSaves) && (
|
||||
{(mayHaveRecentSaves ||
|
||||
mayHaveInferredPersonalization ||
|
||||
mayHaveTopicSections) && (
|
||||
<div className="more-info-pocket-wrapper">
|
||||
<div
|
||||
className="more-information"
|
||||
ref={this.pocketDrawerRef}
|
||||
>
|
||||
{mayHaveSponsoredStories && (
|
||||
<div className="check-wrapper" role="presentation">
|
||||
<input
|
||||
id="sponsored-pocket"
|
||||
className="customize-menu-checkbox"
|
||||
disabled={!pocketEnabled}
|
||||
checked={showSponsoredPocketEnabled}
|
||||
type="checkbox"
|
||||
onChange={this.onPreferenceSelect}
|
||||
data-preference="showSponsored"
|
||||
data-eventSource="POCKET_SPOCS"
|
||||
/>
|
||||
<label
|
||||
className="customize-menu-checkbox-label"
|
||||
htmlFor="sponsored-pocket"
|
||||
data-l10n-id="newtab-custom-pocket-sponsored"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{mayHaveInferredPersonalization && (
|
||||
<div className="check-wrapper" role="presentation">
|
||||
<input
|
||||
|
@ -302,47 +272,6 @@ export class ContentSection extends React.PureComponent {
|
|||
</moz-toggle>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div id="recent-section" className="section">
|
||||
<moz-toggle
|
||||
id="highlights-toggle"
|
||||
pressed={highlightsEnabled || null}
|
||||
onToggle={this.onPreferenceSelect}
|
||||
data-preference="feeds.section.highlights"
|
||||
data-eventSource="HIGHLIGHTS"
|
||||
data-l10n-id="newtab-custom-recent-toggle"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{mayHaveWeather && (
|
||||
<div id="weather-section" className="section">
|
||||
<moz-toggle
|
||||
id="weather-toggle"
|
||||
pressed={weatherEnabled || null}
|
||||
onToggle={this.onPreferenceSelect}
|
||||
data-preference="showWeather"
|
||||
data-eventSource="WEATHER"
|
||||
data-l10n-id="newtab-custom-weather-toggle"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pocketRegion &&
|
||||
mayHaveSponsoredStories &&
|
||||
spocMessageVariant === "variant-c" && (
|
||||
<div className="sponsored-content-info">
|
||||
<div className="icon icon-help"></div>
|
||||
<div>
|
||||
Sponsored content supports our mission to build a better web.{" "}
|
||||
<SafeAnchor
|
||||
dispatch={this.props.dispatch}
|
||||
url="https://support.mozilla.org/kb/pocket-sponsored-stories-new-tabs"
|
||||
>
|
||||
Find out how
|
||||
</SafeAnchor>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<span className="divider" role="separator"></span>
|
||||
|
|
|
@ -83,14 +83,11 @@ export class _CustomizeMenu extends React.PureComponent {
|
|||
activeWallpaper={this.props.activeWallpaper}
|
||||
pocketRegion={this.props.pocketRegion}
|
||||
mayHaveTopicSections={this.props.mayHaveTopicSections}
|
||||
mayHaveSponsoredTopSites={this.props.mayHaveSponsoredTopSites}
|
||||
mayHaveSponsoredStories={this.props.mayHaveSponsoredStories}
|
||||
mayHaveInferredPersonalization={
|
||||
this.props.mayHaveInferredPersonalization
|
||||
}
|
||||
mayHaveRecentSaves={this.props.DiscoveryStream.recentSavesEnabled}
|
||||
mayHaveWeather={this.props.mayHaveWeather}
|
||||
spocMessageVariant={this.props.spocMessageVariant}
|
||||
dispatch={this.props.dispatch}
|
||||
exitEventFired={this.state.exitEventFired}
|
||||
/>
|
||||
|
|
|
@ -5,8 +5,21 @@
|
|||
import React from "react";
|
||||
import { SafeAnchor } from "../SafeAnchor/SafeAnchor";
|
||||
import { ImpressionStats } from "../../DiscoveryStreamImpressionStats/ImpressionStats";
|
||||
import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs";
|
||||
import { actionCreators as ac } from "common/Actions.mjs";
|
||||
import { AdBannerContextMenu } from "../AdBannerContextMenu/AdBannerContextMenu";
|
||||
|
||||
/**
|
||||
* A new banner ad that appears between rows of stories: leaderboard or billboard size.
|
||||
*
|
||||
* @param spoc
|
||||
* @param dispatch
|
||||
* @param firstVisibleTimestamp
|
||||
* @param row
|
||||
* @param type
|
||||
* @param prefs
|
||||
* @returns {Element}
|
||||
* @constructor
|
||||
*/
|
||||
export const AdBanner = ({
|
||||
spoc,
|
||||
dispatch,
|
||||
|
@ -39,39 +52,12 @@ export const AdBanner = ({
|
|||
|
||||
const { width: imgWidth, height: imgHeight } = getDimensions(spoc.format);
|
||||
|
||||
const handleDismissClick = () => {
|
||||
dispatch(
|
||||
ac.AlsoToMain({
|
||||
type: at.BLOCK_URL,
|
||||
data: [
|
||||
{
|
||||
block_key: spoc.block_key,
|
||||
fetchTimestamp: spoc.fetchTimestamp,
|
||||
flight_id: spoc.flight_id,
|
||||
format: spoc.format,
|
||||
id: spoc.id,
|
||||
card_type: "spoc",
|
||||
is_pocket_card: true,
|
||||
position: row,
|
||||
sponsor: spoc.sponsor,
|
||||
title: spoc.title,
|
||||
url: spoc.url || spoc.shim.url,
|
||||
personalization_models: spoc.personalization_models,
|
||||
priority: spoc.priority,
|
||||
score: spoc.score,
|
||||
alt_text: spoc.alt_text,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const onLinkClick = () => {
|
||||
dispatch(
|
||||
ac.DiscoveryStreamUserEvent({
|
||||
event: "CLICK",
|
||||
source: type.toUpperCase(),
|
||||
// Banner ads dont have a position, but a row number
|
||||
// Banner ads don't have a position, but a row number
|
||||
action_position: row,
|
||||
value: {
|
||||
card_type: "spoc",
|
||||
|
@ -98,13 +84,12 @@ export const AdBanner = ({
|
|||
return (
|
||||
<aside className="ad-banner-wrapper" style={{ gridRow: clampedRow }}>
|
||||
<div className={`ad-banner-inner ${spoc.format}`}>
|
||||
<div className="ad-banner-dismiss">
|
||||
<button
|
||||
className="icon icon-dismiss"
|
||||
onClick={handleDismissClick}
|
||||
data-l10n-id="newtab-toast-dismiss-button"
|
||||
></button>
|
||||
</div>
|
||||
<AdBannerContextMenu
|
||||
dispatch={dispatch}
|
||||
spoc={spoc}
|
||||
position={row}
|
||||
type={type}
|
||||
/>
|
||||
<SafeAnchor
|
||||
className="ad-banner-link"
|
||||
url={spoc.url}
|
||||
|
|
|
@ -33,24 +33,6 @@
|
|||
.ad-banner-inner {
|
||||
margin-inline: auto;
|
||||
|
||||
.ad-banner-dismiss {
|
||||
// Contrast fix for users who have wallpapers set
|
||||
@include wallpaper-contrast-fix;
|
||||
|
||||
margin-block: 0 var(--space-small);
|
||||
margin-inline: 0 var(--space-xxsmall);
|
||||
text-align: end;
|
||||
|
||||
.icon-dismiss {
|
||||
background-size: var(--size-item-small);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.icon-dismiss:hover {
|
||||
background-color: var(--newtab-button-hover-background);
|
||||
}
|
||||
}
|
||||
|
||||
&.leaderboard {
|
||||
max-width: var(--leaderboard-width);
|
||||
|
||||
|
@ -74,7 +56,7 @@
|
|||
}
|
||||
|
||||
&.billboard {
|
||||
min-width: var(--billboard-width);
|
||||
width: var(--billboard-width);
|
||||
|
||||
.ad-banner-content {
|
||||
height: var(--billboard-height);
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/* 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 React, { useState } from "react";
|
||||
import { actionCreators as ac } from "common/Actions.mjs";
|
||||
import { LinkMenu } from "../../LinkMenu/LinkMenu";
|
||||
|
||||
/**
|
||||
* A context menu for IAB banners (e.g. billboard, leaderboard).
|
||||
*
|
||||
* Note: MREC ad formats and sponsored stories share the context menu with
|
||||
* other cards: make sure you also look at DSLinkMenu component
|
||||
* to keep any updates to ad-related context menu items in sync.
|
||||
*
|
||||
* @param dispatch
|
||||
* @param spoc
|
||||
* @param position
|
||||
* @param type
|
||||
* @returns {Element}
|
||||
* @constructor
|
||||
*/
|
||||
export function AdBannerContextMenu({ dispatch, spoc, position, type }) {
|
||||
const ADBANNER_CONTEXT_MENU_OPTIONS = [
|
||||
"BlockAdUrl",
|
||||
"ManageSponsoredContent",
|
||||
"OurSponsorsAndYourPrivacy",
|
||||
];
|
||||
|
||||
const [showContextMenu, setShowContextMenu] = useState(false);
|
||||
|
||||
const onClick = e => {
|
||||
e.preventDefault();
|
||||
setShowContextMenu(!showContextMenu);
|
||||
};
|
||||
|
||||
const onUpdate = () => {
|
||||
setShowContextMenu(!showContextMenu);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ads-context-menu-wrapper">
|
||||
<div className="ads-context-menu">
|
||||
<moz-button
|
||||
type="icon"
|
||||
size="default"
|
||||
iconsrc="chrome://global/skin/icons/more.svg"
|
||||
onClick={onClick}
|
||||
/>
|
||||
{showContextMenu && (
|
||||
<LinkMenu
|
||||
onUpdate={onUpdate}
|
||||
dispatch={dispatch}
|
||||
options={ADBANNER_CONTEXT_MENU_OPTIONS}
|
||||
shouldSendImpressionStats={true}
|
||||
userEvent={ac.DiscoveryStreamUserEvent}
|
||||
site={{
|
||||
// Props we want to pass on for new ad types that come from Unified Ads API
|
||||
block_key: spoc.block_key,
|
||||
fetchTimestamp: spoc.fetchTimestamp,
|
||||
flight_id: spoc.flight_id,
|
||||
format: spoc.format,
|
||||
id: spoc.id,
|
||||
guid: spoc.guid,
|
||||
card_type: "spoc",
|
||||
// required to record telemetry for an action, see handleBlockUrl in TelemetryFeed.sys.mjs
|
||||
is_pocket_card: true,
|
||||
position,
|
||||
sponsor: spoc.sponsor,
|
||||
title: spoc.title,
|
||||
url: spoc.url || spoc.shim.url,
|
||||
personalization_models: spoc.personalization_models,
|
||||
priority: spoc.priority,
|
||||
score: spoc.score,
|
||||
alt_text: spoc.alt_text,
|
||||
shim: spoc.shim,
|
||||
}}
|
||||
index={position}
|
||||
source={type.toUpperCase()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
.ads-context-menu-wrapper {
|
||||
// Contrast fix for users who have wallpapers set
|
||||
@include wallpaper-contrast-fix;
|
||||
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.ads-context-menu {
|
||||
float: right;
|
||||
position: relative;
|
||||
|
||||
> moz-button {
|
||||
padding-block-end: var(--space-small);
|
||||
}
|
||||
|
||||
// Transparent on light backgrounds as specified in Figma designs
|
||||
// and default moz-button colours on dark backgrounds.
|
||||
> moz-button::part(button) {
|
||||
background-color: light-dark(transparent, var(--button-background-color));
|
||||
}
|
||||
|
||||
.context-menu {
|
||||
width: auto;
|
||||
|
||||
/* Position the menu just under and to the right of the context menu button */
|
||||
top: calc(2.25 * var(--size-item-small));
|
||||
inset-inline-start: calc(-12.25 * var(--size-item-small));
|
||||
}
|
||||
}
|
|
@ -11,6 +11,12 @@ import { CSSTransition } from "react-transition-group";
|
|||
const PREF_WALLPAPER_UPLOADED_PREVIOUSLY =
|
||||
"newtabWallpapers.customWallpaper.uploadedPreviously";
|
||||
|
||||
const PREF_WALLPAPER_UPLOAD_MAX_FILE_SIZE =
|
||||
"newtabWallpapers.customWallpaper.fileSize";
|
||||
|
||||
const PREF_WALLPAPER_UPLOAD_MAX_FILE_SIZE_ENABLED =
|
||||
"newtabWallpapers.customWallpaper.fileSize.enabled";
|
||||
|
||||
// Returns a function will not be continuously triggered when called. The
|
||||
// function will be triggered if called again after `wait` milliseconds.
|
||||
function debounce(func, wait) {
|
||||
|
@ -52,6 +58,7 @@ export class _WallpaperCategories extends React.PureComponent {
|
|||
showColorPicker: false,
|
||||
inputType: "radio",
|
||||
activeId: null,
|
||||
isCustomWallpaperError: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -121,9 +128,13 @@ export class _WallpaperCategories extends React.PureComponent {
|
|||
|
||||
this.props.setPref("newtabWallpapers.wallpaper", id);
|
||||
|
||||
const uploadedPreviously =
|
||||
this.props.Prefs.values[PREF_WALLPAPER_UPLOADED_PREVIOUSLY];
|
||||
|
||||
this.handleUserEvent(at.WALLPAPER_CLICK, {
|
||||
selected_wallpaper: id,
|
||||
had_previous_wallpaper: !!this.props.activeWallpaper,
|
||||
had_uploaded_previously: !!uploadedPreviously,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -210,13 +221,14 @@ export class _WallpaperCategories extends React.PureComponent {
|
|||
}
|
||||
|
||||
handleReset() {
|
||||
this.props.setPref("newtabWallpapers.wallpaper", "");
|
||||
|
||||
const uploadedPreviously =
|
||||
this.props.Prefs.values[PREF_WALLPAPER_UPLOADED_PREVIOUSLY];
|
||||
|
||||
if (uploadedPreviously) {
|
||||
this.props.setPref(PREF_WALLPAPER_UPLOADED_PREVIOUSLY, false);
|
||||
const selectedWallpaper =
|
||||
this.props.Prefs.values["newtabWallpapers.wallpaper"];
|
||||
|
||||
// If a custom wallpaper is set, remove it
|
||||
if (selectedWallpaper === "custom") {
|
||||
this.props.dispatch(
|
||||
ac.OnlyToMain({
|
||||
type: at.WALLPAPER_REMOVE_UPLOAD,
|
||||
|
@ -224,9 +236,14 @@ export class _WallpaperCategories extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
// Reset active wallpaper
|
||||
this.props.setPref("newtabWallpapers.wallpaper", "");
|
||||
|
||||
// Fire WALLPAPER_CLICK telemetry event
|
||||
this.handleUserEvent(at.WALLPAPER_CLICK, {
|
||||
selected_wallpaper: "none",
|
||||
had_previous_wallpaper: !!this.props.activeWallpaper,
|
||||
had_uploaded_previously: !!uploadedPreviously,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -255,31 +272,42 @@ export class _WallpaperCategories extends React.PureComponent {
|
|||
|
||||
// Custom wallpaper image upload
|
||||
async handleUpload() {
|
||||
// TODO: Bug 1943663: Add telemetry
|
||||
const wallpaperUploadMaxFileSizeEnabled =
|
||||
this.props.Prefs.values[PREF_WALLPAPER_UPLOAD_MAX_FILE_SIZE_ENABLED];
|
||||
|
||||
// TODO: Bug 1947813: Add image upload error states/UI
|
||||
const wallpaperUploadMaxFileSize =
|
||||
this.props.Prefs.values[PREF_WALLPAPER_UPLOAD_MAX_FILE_SIZE];
|
||||
|
||||
// TODO: Once Bug 1947813 has landed, we may need a separate event
|
||||
// for selecting previously uploaded wallpaper, rather than uploading a new one.
|
||||
// The plan would be to reuse at.WALLPAPER_CLICK for this use case
|
||||
const uploadedPreviously =
|
||||
this.props.Prefs.values[PREF_WALLPAPER_UPLOADED_PREVIOUSLY];
|
||||
|
||||
this.handleUserEvent(at.WALLPAPER_UPLOAD, {
|
||||
had_uploaded_previously: !!uploadedPreviously,
|
||||
had_previous_wallpaper: !!this.props.activeWallpaper,
|
||||
});
|
||||
|
||||
this.props.setPref(PREF_WALLPAPER_UPLOADED_PREVIOUSLY, true);
|
||||
|
||||
// Create a file input since category buttons are radio inputs
|
||||
const fileInput = document.createElement("input");
|
||||
fileInput.type = "file";
|
||||
fileInput.accept = "image/*"; // only allow image files
|
||||
|
||||
// Catch cancel events
|
||||
fileInput.oncancel = async () => {
|
||||
this.setState({ isCustomWallpaperError: false });
|
||||
};
|
||||
|
||||
// Reset error state when user begins file selection
|
||||
this.setState({ isCustomWallpaperError: false });
|
||||
|
||||
// Fire when user selects a file
|
||||
fileInput.onchange = async event => {
|
||||
const [file] = event.target.files;
|
||||
|
||||
// Limit image uploaded to a maximum file size if enabled
|
||||
// Note: The max file size pref (customWallpaper.fileSize) is converted to megabytes (MB)
|
||||
// Example: if pref value is 5, max file size is 5 MB
|
||||
const maxSize = wallpaperUploadMaxFileSize * 1024 * 1024;
|
||||
if (wallpaperUploadMaxFileSizeEnabled && file && file.size > maxSize) {
|
||||
console.error("File size exceeds limit");
|
||||
this.setState({ isCustomWallpaperError: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (file) {
|
||||
this.props.dispatch(
|
||||
ac.OnlyToMain({
|
||||
|
@ -287,6 +315,19 @@ export class _WallpaperCategories extends React.PureComponent {
|
|||
data: file,
|
||||
})
|
||||
);
|
||||
|
||||
// Set active wallpaper ID to "custom"
|
||||
this.props.setPref("newtabWallpapers.wallpaper", "custom");
|
||||
|
||||
// Update the uploadedPreviously pref to TRUE
|
||||
// Note: this pref used for telemetry. Do not reset to false.
|
||||
this.props.setPref(PREF_WALLPAPER_UPLOADED_PREVIOUSLY, true);
|
||||
|
||||
this.handleUserEvent(at.WALLPAPER_CLICK, {
|
||||
selected_wallpaper: "custom",
|
||||
had_previous_wallpaper: !!this.props.activeWallpaper,
|
||||
had_uploaded_previously: !!uploadedPreviously,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -332,6 +373,8 @@ export class _WallpaperCategories extends React.PureComponent {
|
|||
let filteredWallpapers = wallpaperList.filter(
|
||||
wallpaper => wallpaper.category === activeCategory
|
||||
);
|
||||
const wallpaperUploadMaxFileSize =
|
||||
this.props.Prefs.values[PREF_WALLPAPER_UPLOAD_MAX_FILE_SIZE];
|
||||
|
||||
function reduceColorsToFitCustomColorInput(arr) {
|
||||
// Reduce the amount of custom colors to make space for the custom color picker
|
||||
|
@ -491,6 +534,9 @@ export class _WallpaperCategories extends React.PureComponent {
|
|||
: `wallpaper-input theme-custom-wallpaper`
|
||||
}
|
||||
tabIndex={index === 0 ? 0 : -1}
|
||||
{...(category === "custom-wallpaper"
|
||||
? { "aria-errormessage": "customWallpaperError" }
|
||||
: {})}
|
||||
/>
|
||||
<label htmlFor={category} data-l10n-id={fluent_id}>
|
||||
{fluent_id}
|
||||
|
@ -499,6 +545,15 @@ export class _WallpaperCategories extends React.PureComponent {
|
|||
);
|
||||
})}
|
||||
</fieldset>
|
||||
{this.state.isCustomWallpaperError && (
|
||||
<div className="custom-wallpaper-error" id="customWallpaperError">
|
||||
<span className="icon icon-info"></span>
|
||||
<span
|
||||
data-l10n-id="newtab-wallpaper-error-max-file-size"
|
||||
data-l10n-args={`{"file_size": ${wallpaperUploadMaxFileSize}}`}
|
||||
></span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<CSSTransition
|
||||
|
|
|
@ -287,3 +287,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Bug 1947813 - Custom Upload Error Message
|
||||
.custom-wallpaper-error {
|
||||
padding-block-start: var(--space-xlarge);
|
||||
font-size: var(--font-size-small);
|
||||
display: flex;
|
||||
gap: var(--space-small);
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
width: var(--button-size-icon-small);
|
||||
height: var(--button-size-icon-small);
|
||||
fill: var(--icon-color-critical);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,6 +151,28 @@ export const LinkMenuOptions = {
|
|||
userEvent: "BLOCK",
|
||||
}),
|
||||
|
||||
// This is the "Dismiss" action for leaderboard/billboard ads.
|
||||
BlockAdUrl: (site, pos, eventSource) => ({
|
||||
id: "newtab-menu-dismiss",
|
||||
icon: "dismiss",
|
||||
action: ac.AlsoToMain({
|
||||
type: at.BLOCK_URL,
|
||||
data: [site],
|
||||
}),
|
||||
impression: ac.ImpressionStats({
|
||||
source: eventSource,
|
||||
block: 0,
|
||||
tiles: [
|
||||
{
|
||||
id: site.guid,
|
||||
pos,
|
||||
...(site.shim && site.shim.save ? { shim: site.shim.save } : {}),
|
||||
},
|
||||
],
|
||||
}),
|
||||
userEvent: "BLOCK",
|
||||
}),
|
||||
|
||||
// This is an option for web extentions which will result in remove items from
|
||||
// memory and notify the web extenion, rather than using the built-in block list.
|
||||
WebExtDismiss: (site, index, eventSource) => ({
|
||||
|
|
|
@ -194,6 +194,7 @@ input {
|
|||
@import '../components/DiscoveryStreamComponents/TopicSelection/TopicSelection';
|
||||
@import '../components/DiscoveryStreamComponents/ListFeed/ListFeed';
|
||||
@import '../components/DiscoveryStreamComponents/AdBanner/AdBanner';
|
||||
@import '../components/DiscoveryStreamComponents/AdBannerContextMenu/AdBannerContextMenu';
|
||||
@import '../components/DiscoveryStreamComponents/SectionContextMenu/SectionContextMenu';
|
||||
@import '../components/DiscoveryStreamComponents/InterestPicker/InterestPicker';
|
||||
|
||||
|
|
|
@ -2791,6 +2791,19 @@ main section {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.custom-wallpaper-error {
|
||||
padding-block-start: var(--space-xlarge);
|
||||
font-size: var(--font-size-small);
|
||||
display: flex;
|
||||
gap: var(--space-small);
|
||||
align-items: center;
|
||||
}
|
||||
.custom-wallpaper-error .icon {
|
||||
width: var(--button-size-icon-small);
|
||||
height: var(--button-size-icon-small);
|
||||
fill: var(--icon-color-critical);
|
||||
}
|
||||
|
||||
:root {
|
||||
--newtab-weather-content-font-size: 11px;
|
||||
--newtab-weather-sponsor-font-size: 8px;
|
||||
|
@ -8024,24 +8037,6 @@ main section {
|
|||
.ad-banner-wrapper .ad-banner-inner {
|
||||
margin-inline: auto;
|
||||
}
|
||||
.ad-banner-wrapper .ad-banner-inner .ad-banner-dismiss {
|
||||
margin-block: 0 var(--space-small);
|
||||
margin-inline: 0 var(--space-xxsmall);
|
||||
text-align: end;
|
||||
}
|
||||
.lightWallpaper .ad-banner-wrapper .ad-banner-inner .ad-banner-dismiss {
|
||||
color-scheme: light;
|
||||
}
|
||||
.darkWallpaper .ad-banner-wrapper .ad-banner-inner .ad-banner-dismiss {
|
||||
color-scheme: dark;
|
||||
}
|
||||
.ad-banner-wrapper .ad-banner-inner .ad-banner-dismiss .icon-dismiss {
|
||||
background-size: var(--size-item-small);
|
||||
border: 0;
|
||||
}
|
||||
.ad-banner-wrapper .ad-banner-inner .ad-banner-dismiss .icon-dismiss:hover {
|
||||
background-color: var(--newtab-button-hover-background);
|
||||
}
|
||||
.ad-banner-wrapper .ad-banner-inner.leaderboard {
|
||||
max-width: var(--leaderboard-width);
|
||||
}
|
||||
|
@ -8062,7 +8057,7 @@ main section {
|
|||
}
|
||||
}
|
||||
.ad-banner-wrapper .ad-banner-inner.billboard {
|
||||
min-width: var(--billboard-width);
|
||||
width: var(--billboard-width);
|
||||
}
|
||||
.ad-banner-wrapper .ad-banner-inner.billboard .ad-banner-content {
|
||||
height: var(--billboard-height);
|
||||
|
@ -8092,6 +8087,33 @@ main section {
|
|||
color: var(--newtab-contextual-text-secondary-color);
|
||||
}
|
||||
|
||||
.ads-context-menu-wrapper {
|
||||
text-align: end;
|
||||
}
|
||||
.lightWallpaper .ads-context-menu-wrapper {
|
||||
color-scheme: light;
|
||||
}
|
||||
.darkWallpaper .ads-context-menu-wrapper {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
.ads-context-menu {
|
||||
float: right;
|
||||
position: relative;
|
||||
}
|
||||
.ads-context-menu > moz-button {
|
||||
padding-block-end: var(--space-small);
|
||||
}
|
||||
.ads-context-menu > moz-button::part(button) {
|
||||
background-color: light-dark(transparent, var(--button-background-color));
|
||||
}
|
||||
.ads-context-menu .context-menu {
|
||||
width: auto;
|
||||
/* Position the menu just under and to the right of the context menu button */
|
||||
top: calc(2.25 * var(--size-item-small));
|
||||
inset-inline-start: calc(-12.25 * var(--size-item-small));
|
||||
}
|
||||
|
||||
.section-context-menu {
|
||||
position: relative;
|
||||
}
|
||||
|
|
|
@ -1828,6 +1828,28 @@ const LinkMenuOptions = {
|
|||
userEvent: "BLOCK",
|
||||
}),
|
||||
|
||||
// This is the "Dismiss" action for leaderboard/billboard ads.
|
||||
BlockAdUrl: (site, pos, eventSource) => ({
|
||||
id: "newtab-menu-dismiss",
|
||||
icon: "dismiss",
|
||||
action: actionCreators.AlsoToMain({
|
||||
type: actionTypes.BLOCK_URL,
|
||||
data: [site],
|
||||
}),
|
||||
impression: actionCreators.ImpressionStats({
|
||||
source: eventSource,
|
||||
block: 0,
|
||||
tiles: [
|
||||
{
|
||||
id: site.guid,
|
||||
pos,
|
||||
...(site.shim && site.shim.save ? { shim: site.shim.save } : {}),
|
||||
},
|
||||
],
|
||||
}),
|
||||
userEvent: "BLOCK",
|
||||
}),
|
||||
|
||||
// This is an option for web extentions which will result in remove items from
|
||||
// memory and notify the web extenion, rather than using the built-in block list.
|
||||
WebExtDismiss: (site, index, eventSource) => ({
|
||||
|
@ -4284,6 +4306,84 @@ function ListFeed({
|
|||
}, ctaCopy)))));
|
||||
}
|
||||
|
||||
;// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/AdBannerContextMenu/AdBannerContextMenu.jsx
|
||||
/* 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/. */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A context menu for IAB banners (e.g. billboard, leaderboard).
|
||||
*
|
||||
* Note: MREC ad formats and sponsored stories share the context menu with
|
||||
* other cards: make sure you also look at DSLinkMenu component
|
||||
* to keep any updates to ad-related context menu items in sync.
|
||||
*
|
||||
* @param dispatch
|
||||
* @param spoc
|
||||
* @param position
|
||||
* @param type
|
||||
* @returns {Element}
|
||||
* @constructor
|
||||
*/
|
||||
function AdBannerContextMenu({
|
||||
dispatch,
|
||||
spoc,
|
||||
position,
|
||||
type
|
||||
}) {
|
||||
const ADBANNER_CONTEXT_MENU_OPTIONS = ["BlockAdUrl", "ManageSponsoredContent", "OurSponsorsAndYourPrivacy"];
|
||||
const [showContextMenu, setShowContextMenu] = (0,external_React_namespaceObject.useState)(false);
|
||||
const onClick = e => {
|
||||
e.preventDefault();
|
||||
setShowContextMenu(!showContextMenu);
|
||||
};
|
||||
const onUpdate = () => {
|
||||
setShowContextMenu(!showContextMenu);
|
||||
};
|
||||
return /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "ads-context-menu-wrapper"
|
||||
}, /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "ads-context-menu"
|
||||
}, /*#__PURE__*/external_React_default().createElement("moz-button", {
|
||||
type: "icon",
|
||||
size: "default",
|
||||
iconsrc: "chrome://global/skin/icons/more.svg",
|
||||
onClick: onClick
|
||||
}), showContextMenu && /*#__PURE__*/external_React_default().createElement(LinkMenu, {
|
||||
onUpdate: onUpdate,
|
||||
dispatch: dispatch,
|
||||
options: ADBANNER_CONTEXT_MENU_OPTIONS,
|
||||
shouldSendImpressionStats: true,
|
||||
userEvent: actionCreators.DiscoveryStreamUserEvent,
|
||||
site: {
|
||||
// Props we want to pass on for new ad types that come from Unified Ads API
|
||||
block_key: spoc.block_key,
|
||||
fetchTimestamp: spoc.fetchTimestamp,
|
||||
flight_id: spoc.flight_id,
|
||||
format: spoc.format,
|
||||
id: spoc.id,
|
||||
guid: spoc.guid,
|
||||
card_type: "spoc",
|
||||
// required to record telemetry for an action, see handleBlockUrl in TelemetryFeed.sys.mjs
|
||||
is_pocket_card: true,
|
||||
position,
|
||||
sponsor: spoc.sponsor,
|
||||
title: spoc.title,
|
||||
url: spoc.url || spoc.shim.url,
|
||||
personalization_models: spoc.personalization_models,
|
||||
priority: spoc.priority,
|
||||
score: spoc.score,
|
||||
alt_text: spoc.alt_text,
|
||||
shim: spoc.shim
|
||||
},
|
||||
index: position,
|
||||
source: type.toUpperCase()
|
||||
})));
|
||||
}
|
||||
;// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/AdBanner/AdBanner.jsx
|
||||
/* 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,
|
||||
|
@ -4293,6 +4393,20 @@ function ListFeed({
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A new banner ad that appears between rows of stories: leaderboard or billboard size.
|
||||
*
|
||||
* @param spoc
|
||||
* @param dispatch
|
||||
* @param firstVisibleTimestamp
|
||||
* @param row
|
||||
* @param type
|
||||
* @param prefs
|
||||
* @returns {Element}
|
||||
* @constructor
|
||||
*/
|
||||
const AdBanner = ({
|
||||
spoc,
|
||||
dispatch,
|
||||
|
@ -4325,33 +4439,11 @@ const AdBanner = ({
|
|||
width: imgWidth,
|
||||
height: imgHeight
|
||||
} = getDimensions(spoc.format);
|
||||
const handleDismissClick = () => {
|
||||
dispatch(actionCreators.AlsoToMain({
|
||||
type: actionTypes.BLOCK_URL,
|
||||
data: [{
|
||||
block_key: spoc.block_key,
|
||||
fetchTimestamp: spoc.fetchTimestamp,
|
||||
flight_id: spoc.flight_id,
|
||||
format: spoc.format,
|
||||
id: spoc.id,
|
||||
card_type: "spoc",
|
||||
is_pocket_card: true,
|
||||
position: row,
|
||||
sponsor: spoc.sponsor,
|
||||
title: spoc.title,
|
||||
url: spoc.url || spoc.shim.url,
|
||||
personalization_models: spoc.personalization_models,
|
||||
priority: spoc.priority,
|
||||
score: spoc.score,
|
||||
alt_text: spoc.alt_text
|
||||
}]
|
||||
}));
|
||||
};
|
||||
const onLinkClick = () => {
|
||||
dispatch(actionCreators.DiscoveryStreamUserEvent({
|
||||
event: "CLICK",
|
||||
source: type.toUpperCase(),
|
||||
// Banner ads dont have a position, but a row number
|
||||
// Banner ads don't have a position, but a row number
|
||||
action_position: row,
|
||||
value: {
|
||||
card_type: "spoc",
|
||||
|
@ -4380,13 +4472,12 @@ const AdBanner = ({
|
|||
}
|
||||
}, /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: `ad-banner-inner ${spoc.format}`
|
||||
}, /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "ad-banner-dismiss"
|
||||
}, /*#__PURE__*/external_React_default().createElement("button", {
|
||||
className: "icon icon-dismiss",
|
||||
onClick: handleDismissClick,
|
||||
"data-l10n-id": "newtab-toast-dismiss-button"
|
||||
})), /*#__PURE__*/external_React_default().createElement(SafeAnchor, {
|
||||
}, /*#__PURE__*/external_React_default().createElement(AdBannerContextMenu, {
|
||||
dispatch: dispatch,
|
||||
spoc: spoc,
|
||||
position: row,
|
||||
type: type
|
||||
}), /*#__PURE__*/external_React_default().createElement(SafeAnchor, {
|
||||
className: "ad-banner-link",
|
||||
url: spoc.url,
|
||||
title: spoc.title,
|
||||
|
@ -11351,6 +11442,7 @@ const WallpapersSection = (0,external_ReactRedux_namespaceObject.connect)(state
|
|||
};
|
||||
})(_WallpapersSection);
|
||||
;// CONCATENATED MODULE: ./content-src/components/WallpapersSection/WallpaperCategories.jsx
|
||||
function WallpaperCategories_extends() { WallpaperCategories_extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return WallpaperCategories_extends.apply(this, arguments); }
|
||||
/* 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/. */
|
||||
|
@ -11361,6 +11453,8 @@ const WallpapersSection = (0,external_ReactRedux_namespaceObject.connect)(state
|
|||
// eslint-disable-next-line no-shadow
|
||||
|
||||
const PREF_WALLPAPER_UPLOADED_PREVIOUSLY = "newtabWallpapers.customWallpaper.uploadedPreviously";
|
||||
const PREF_WALLPAPER_UPLOAD_MAX_FILE_SIZE = "newtabWallpapers.customWallpaper.fileSize";
|
||||
const PREF_WALLPAPER_UPLOAD_MAX_FILE_SIZE_ENABLED = "newtabWallpapers.customWallpaper.fileSize.enabled";
|
||||
|
||||
// Returns a function will not be continuously triggered when called. The
|
||||
// function will be triggered if called again after `wait` milliseconds.
|
||||
|
@ -11399,7 +11493,8 @@ class _WallpaperCategories extends (external_React_default()).PureComponent {
|
|||
activeCategoryFluentID: null,
|
||||
showColorPicker: false,
|
||||
inputType: "radio",
|
||||
activeId: null
|
||||
activeId: null,
|
||||
isCustomWallpaperError: false
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
|
@ -11456,9 +11551,11 @@ class _WallpaperCategories extends (external_React_default()).PureComponent {
|
|||
id = `solid-color-picker-${event.target.value}`;
|
||||
}
|
||||
this.props.setPref("newtabWallpapers.wallpaper", id);
|
||||
const uploadedPreviously = this.props.Prefs.values[PREF_WALLPAPER_UPLOADED_PREVIOUSLY];
|
||||
this.handleUserEvent(actionTypes.WALLPAPER_CLICK, {
|
||||
selected_wallpaper: id,
|
||||
had_previous_wallpaper: !!this.props.activeWallpaper
|
||||
had_previous_wallpaper: !!this.props.activeWallpaper,
|
||||
had_uploaded_previously: !!uploadedPreviously
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -11525,17 +11622,24 @@ class _WallpaperCategories extends (external_React_default()).PureComponent {
|
|||
this.wallpaperRef[nextIndex].click();
|
||||
}
|
||||
handleReset() {
|
||||
this.props.setPref("newtabWallpapers.wallpaper", "");
|
||||
const uploadedPreviously = this.props.Prefs.values[PREF_WALLPAPER_UPLOADED_PREVIOUSLY];
|
||||
if (uploadedPreviously) {
|
||||
this.props.setPref(PREF_WALLPAPER_UPLOADED_PREVIOUSLY, false);
|
||||
const selectedWallpaper = this.props.Prefs.values["newtabWallpapers.wallpaper"];
|
||||
|
||||
// If a custom wallpaper is set, remove it
|
||||
if (selectedWallpaper === "custom") {
|
||||
this.props.dispatch(actionCreators.OnlyToMain({
|
||||
type: actionTypes.WALLPAPER_REMOVE_UPLOAD
|
||||
}));
|
||||
}
|
||||
|
||||
// Reset active wallpaper
|
||||
this.props.setPref("newtabWallpapers.wallpaper", "");
|
||||
|
||||
// Fire WALLPAPER_CLICK telemetry event
|
||||
this.handleUserEvent(actionTypes.WALLPAPER_CLICK, {
|
||||
selected_wallpaper: "none",
|
||||
had_previous_wallpaper: !!this.props.activeWallpaper
|
||||
had_previous_wallpaper: !!this.props.activeWallpaper,
|
||||
had_uploaded_previously: !!uploadedPreviously
|
||||
});
|
||||
}
|
||||
handleCategory = event => {
|
||||
|
@ -11564,32 +11668,59 @@ class _WallpaperCategories extends (external_React_default()).PureComponent {
|
|||
|
||||
// Custom wallpaper image upload
|
||||
async handleUpload() {
|
||||
// TODO: Bug 1943663: Add telemetry
|
||||
|
||||
// TODO: Bug 1947813: Add image upload error states/UI
|
||||
|
||||
// TODO: Once Bug 1947813 has landed, we may need a separate event
|
||||
// for selecting previously uploaded wallpaper, rather than uploading a new one.
|
||||
// The plan would be to reuse at.WALLPAPER_CLICK for this use case
|
||||
const wallpaperUploadMaxFileSizeEnabled = this.props.Prefs.values[PREF_WALLPAPER_UPLOAD_MAX_FILE_SIZE_ENABLED];
|
||||
const wallpaperUploadMaxFileSize = this.props.Prefs.values[PREF_WALLPAPER_UPLOAD_MAX_FILE_SIZE];
|
||||
const uploadedPreviously = this.props.Prefs.values[PREF_WALLPAPER_UPLOADED_PREVIOUSLY];
|
||||
this.handleUserEvent(actionTypes.WALLPAPER_UPLOAD, {
|
||||
had_uploaded_previously: !!uploadedPreviously,
|
||||
had_previous_wallpaper: !!this.props.activeWallpaper
|
||||
});
|
||||
this.props.setPref(PREF_WALLPAPER_UPLOADED_PREVIOUSLY, true);
|
||||
|
||||
// Create a file input since category buttons are radio inputs
|
||||
const fileInput = document.createElement("input");
|
||||
fileInput.type = "file";
|
||||
fileInput.accept = "image/*"; // only allow image files
|
||||
|
||||
// Catch cancel events
|
||||
fileInput.oncancel = async () => {
|
||||
this.setState({
|
||||
isCustomWallpaperError: false
|
||||
});
|
||||
};
|
||||
|
||||
// Reset error state when user begins file selection
|
||||
this.setState({
|
||||
isCustomWallpaperError: false
|
||||
});
|
||||
|
||||
// Fire when user selects a file
|
||||
fileInput.onchange = async event => {
|
||||
const [file] = event.target.files;
|
||||
|
||||
// Limit image uploaded to a maximum file size if enabled
|
||||
// Note: The max file size pref (customWallpaper.fileSize) is converted to megabytes (MB)
|
||||
// Example: if pref value is 5, max file size is 5 MB
|
||||
const maxSize = wallpaperUploadMaxFileSize * 1024 * 1024;
|
||||
if (wallpaperUploadMaxFileSizeEnabled && file && file.size > maxSize) {
|
||||
console.error("File size exceeds limit");
|
||||
this.setState({
|
||||
isCustomWallpaperError: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (file) {
|
||||
this.props.dispatch(actionCreators.OnlyToMain({
|
||||
type: actionTypes.WALLPAPER_UPLOAD,
|
||||
data: file
|
||||
}));
|
||||
|
||||
// Set active wallpaper ID to "custom"
|
||||
this.props.setPref("newtabWallpapers.wallpaper", "custom");
|
||||
|
||||
// Update the uploadedPreviously pref to TRUE
|
||||
// Note: this pref used for telemetry. Do not reset to false.
|
||||
this.props.setPref(PREF_WALLPAPER_UPLOADED_PREVIOUSLY, true);
|
||||
this.handleUserEvent(actionTypes.WALLPAPER_CLICK, {
|
||||
selected_wallpaper: "custom",
|
||||
had_previous_wallpaper: !!this.props.activeWallpaper,
|
||||
had_uploaded_previously: !!uploadedPreviously
|
||||
});
|
||||
}
|
||||
};
|
||||
fileInput.click();
|
||||
|
@ -11642,6 +11773,7 @@ class _WallpaperCategories extends (external_React_default()).PureComponent {
|
|||
activeCategoryFluentID
|
||||
} = this.state;
|
||||
let filteredWallpapers = wallpaperList.filter(wallpaper => wallpaper.category === activeCategory);
|
||||
const wallpaperUploadMaxFileSize = this.props.Prefs.values[PREF_WALLPAPER_UPLOAD_MAX_FILE_SIZE];
|
||||
function reduceColorsToFitCustomColorInput(arr) {
|
||||
// Reduce the amount of custom colors to make space for the custom color picker
|
||||
while (arr.length % 3 !== 2) {
|
||||
|
@ -11755,7 +11887,7 @@ class _WallpaperCategories extends (external_React_default()).PureComponent {
|
|||
}
|
||||
return /*#__PURE__*/external_React_default().createElement("div", {
|
||||
key: category
|
||||
}, /*#__PURE__*/external_React_default().createElement("input", {
|
||||
}, /*#__PURE__*/external_React_default().createElement("input", WallpaperCategories_extends({
|
||||
ref: el => {
|
||||
if (el) {
|
||||
this.categoryRef[index] = el;
|
||||
|
@ -11770,10 +11902,20 @@ class _WallpaperCategories extends (external_React_default()).PureComponent {
|
|||
onClick: category !== "custom-wallpaper" ? this.handleCategory : this.handleUpload,
|
||||
className: category !== "custom-wallpaper" ? `wallpaper-input` : `wallpaper-input theme-custom-wallpaper`,
|
||||
tabIndex: index === 0 ? 0 : -1
|
||||
}), /*#__PURE__*/external_React_default().createElement("label", {
|
||||
}, category === "custom-wallpaper" ? {
|
||||
"aria-errormessage": "customWallpaperError"
|
||||
} : {})), /*#__PURE__*/external_React_default().createElement("label", {
|
||||
htmlFor: category,
|
||||
"data-l10n-id": fluent_id
|
||||
}, fluent_id));
|
||||
})), this.state.isCustomWallpaperError && /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "custom-wallpaper-error",
|
||||
id: "customWallpaperError"
|
||||
}, /*#__PURE__*/external_React_default().createElement("span", {
|
||||
className: "icon icon-info"
|
||||
}), /*#__PURE__*/external_React_default().createElement("span", {
|
||||
"data-l10n-id": "newtab-wallpaper-error-max-file-size",
|
||||
"data-l10n-args": `{"file_size": ${wallpaperUploadMaxFileSize}}`
|
||||
}))), /*#__PURE__*/external_React_default().createElement(external_ReactTransitionGroup_namespaceObject.CSSTransition, {
|
||||
in: !!activeCategory,
|
||||
timeout: 300,
|
||||
|
@ -11847,7 +11989,6 @@ const WallpaperCategories = (0,external_ReactRedux_namespaceObject.connect)(stat
|
|||
|
||||
|
||||
|
||||
|
||||
class ContentSection extends (external_React_default()).PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -11868,7 +12009,7 @@ class ContentSection extends (external_React_default()).PureComponent {
|
|||
}));
|
||||
}
|
||||
onPreferenceSelect(e) {
|
||||
// eventSource: TOP_SITES | TOP_STORIES | HIGHLIGHTS | WEATHER
|
||||
// eventSource: WEATHER | TOP_SITES | TOP_STORIES
|
||||
const {
|
||||
preference,
|
||||
eventSource
|
||||
|
@ -11913,21 +12054,18 @@ class ContentSection extends (external_React_default()).PureComponent {
|
|||
if (isOpen) {
|
||||
drawerRef.style.marginTop = "var(--space-large)";
|
||||
} else {
|
||||
drawerRef.style.marginTop = `-${drawerHeight}px`;
|
||||
drawerRef.style.marginTop = `-${drawerHeight + 3}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const {
|
||||
enabledSections,
|
||||
mayHaveSponsoredTopSites,
|
||||
pocketRegion,
|
||||
mayHaveSponsoredStories,
|
||||
mayHaveInferredPersonalization,
|
||||
mayHaveRecentSaves,
|
||||
mayHaveWeather,
|
||||
openPreferences,
|
||||
spocMessageVariant,
|
||||
wallpapersEnabled,
|
||||
wallpapersV2Enabled,
|
||||
activeWallpaper,
|
||||
|
@ -11938,10 +12076,7 @@ class ContentSection extends (external_React_default()).PureComponent {
|
|||
const {
|
||||
topSitesEnabled,
|
||||
pocketEnabled,
|
||||
highlightsEnabled,
|
||||
weatherEnabled,
|
||||
showSponsoredTopSitesEnabled,
|
||||
showSponsoredPocketEnabled,
|
||||
showInferredPersonalizationEnabled,
|
||||
showRecentSavesEnabled,
|
||||
topSitesRowsCount
|
||||
|
@ -11964,7 +12099,17 @@ class ContentSection extends (external_React_default()).PureComponent {
|
|||
role: "separator"
|
||||
})), /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "settings-toggles"
|
||||
}, /*#__PURE__*/external_React_default().createElement("div", {
|
||||
}, mayHaveWeather && /*#__PURE__*/external_React_default().createElement("div", {
|
||||
id: "weather-section",
|
||||
className: "section"
|
||||
}, /*#__PURE__*/external_React_default().createElement("moz-toggle", {
|
||||
id: "weather-toggle",
|
||||
pressed: weatherEnabled || null,
|
||||
onToggle: this.onPreferenceSelect,
|
||||
"data-preference": "showWeather",
|
||||
"data-eventSource": "WEATHER",
|
||||
"data-l10n-id": "newtab-custom-weather-toggle"
|
||||
})), /*#__PURE__*/external_React_default().createElement("div", {
|
||||
id: "shortcuts-section",
|
||||
className: "section"
|
||||
}, /*#__PURE__*/external_React_default().createElement("moz-toggle", {
|
||||
|
@ -12006,22 +12151,6 @@ class ContentSection extends (external_React_default()).PureComponent {
|
|||
value: "4",
|
||||
"data-l10n-id": "newtab-custom-row-selector",
|
||||
"data-l10n-args": "{\"num\": 4}"
|
||||
})), mayHaveSponsoredTopSites && /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "check-wrapper",
|
||||
role: "presentation"
|
||||
}, /*#__PURE__*/external_React_default().createElement("input", {
|
||||
id: "sponsored-shortcuts",
|
||||
className: "customize-menu-checkbox",
|
||||
disabled: !topSitesEnabled,
|
||||
checked: showSponsoredTopSitesEnabled,
|
||||
type: "checkbox",
|
||||
onChange: this.onPreferenceSelect,
|
||||
"data-preference": "showSponsoredTopSites",
|
||||
"data-eventSource": "SPONSORED_TOP_SITES"
|
||||
}), /*#__PURE__*/external_React_default().createElement("label", {
|
||||
className: "customize-menu-checkbox-label",
|
||||
htmlFor: "sponsored-shortcuts",
|
||||
"data-l10n-id": "newtab-custom-sponsored-sites"
|
||||
}))))))), pocketRegion && /*#__PURE__*/external_React_default().createElement("div", {
|
||||
id: "pocket-section",
|
||||
className: "section"
|
||||
|
@ -12035,28 +12164,12 @@ class ContentSection extends (external_React_default()).PureComponent {
|
|||
"data-l10n-id": "newtab-custom-stories-toggle"
|
||||
}, /*#__PURE__*/external_React_default().createElement("div", {
|
||||
slot: "nested"
|
||||
}, (mayHaveSponsoredStories || mayHaveRecentSaves) && /*#__PURE__*/external_React_default().createElement("div", {
|
||||
}, (mayHaveRecentSaves || mayHaveInferredPersonalization || mayHaveTopicSections) && /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "more-info-pocket-wrapper"
|
||||
}, /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "more-information",
|
||||
ref: this.pocketDrawerRef
|
||||
}, mayHaveSponsoredStories && /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "check-wrapper",
|
||||
role: "presentation"
|
||||
}, /*#__PURE__*/external_React_default().createElement("input", {
|
||||
id: "sponsored-pocket",
|
||||
className: "customize-menu-checkbox",
|
||||
disabled: !pocketEnabled,
|
||||
checked: showSponsoredPocketEnabled,
|
||||
type: "checkbox",
|
||||
onChange: this.onPreferenceSelect,
|
||||
"data-preference": "showSponsored",
|
||||
"data-eventSource": "POCKET_SPOCS"
|
||||
}), /*#__PURE__*/external_React_default().createElement("label", {
|
||||
className: "customize-menu-checkbox-label",
|
||||
htmlFor: "sponsored-pocket",
|
||||
"data-l10n-id": "newtab-custom-pocket-sponsored"
|
||||
})), mayHaveInferredPersonalization && /*#__PURE__*/external_React_default().createElement("div", {
|
||||
}, mayHaveInferredPersonalization && /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "check-wrapper",
|
||||
role: "presentation"
|
||||
}, /*#__PURE__*/external_React_default().createElement("input", {
|
||||
|
@ -12089,34 +12202,7 @@ class ContentSection extends (external_React_default()).PureComponent {
|
|||
className: "customize-menu-checkbox-label",
|
||||
htmlFor: "recent-saves-pocket",
|
||||
"data-l10n-id": "newtab-custom-pocket-show-recent-saves"
|
||||
}))))))), /*#__PURE__*/external_React_default().createElement("div", {
|
||||
id: "recent-section",
|
||||
className: "section"
|
||||
}, /*#__PURE__*/external_React_default().createElement("moz-toggle", {
|
||||
id: "highlights-toggle",
|
||||
pressed: highlightsEnabled || null,
|
||||
onToggle: this.onPreferenceSelect,
|
||||
"data-preference": "feeds.section.highlights",
|
||||
"data-eventSource": "HIGHLIGHTS",
|
||||
"data-l10n-id": "newtab-custom-recent-toggle"
|
||||
})), mayHaveWeather && /*#__PURE__*/external_React_default().createElement("div", {
|
||||
id: "weather-section",
|
||||
className: "section"
|
||||
}, /*#__PURE__*/external_React_default().createElement("moz-toggle", {
|
||||
id: "weather-toggle",
|
||||
pressed: weatherEnabled || null,
|
||||
onToggle: this.onPreferenceSelect,
|
||||
"data-preference": "showWeather",
|
||||
"data-eventSource": "WEATHER",
|
||||
"data-l10n-id": "newtab-custom-weather-toggle"
|
||||
})), pocketRegion && mayHaveSponsoredStories && spocMessageVariant === "variant-c" && /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "sponsored-content-info"
|
||||
}, /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "icon icon-help"
|
||||
}), /*#__PURE__*/external_React_default().createElement("div", null, "Sponsored content supports our mission to build a better web.", " ", /*#__PURE__*/external_React_default().createElement(SafeAnchor, {
|
||||
dispatch: this.props.dispatch,
|
||||
url: "https://support.mozilla.org/kb/pocket-sponsored-stories-new-tabs"
|
||||
}, "Find out how")))), /*#__PURE__*/external_React_default().createElement("span", {
|
||||
})))))))), /*#__PURE__*/external_React_default().createElement("span", {
|
||||
className: "divider",
|
||||
role: "separator"
|
||||
}), /*#__PURE__*/external_React_default().createElement("div", null, /*#__PURE__*/external_React_default().createElement("button", {
|
||||
|
@ -12205,12 +12291,9 @@ class _CustomizeMenu extends (external_React_default()).PureComponent {
|
|||
activeWallpaper: this.props.activeWallpaper,
|
||||
pocketRegion: this.props.pocketRegion,
|
||||
mayHaveTopicSections: this.props.mayHaveTopicSections,
|
||||
mayHaveSponsoredTopSites: this.props.mayHaveSponsoredTopSites,
|
||||
mayHaveSponsoredStories: this.props.mayHaveSponsoredStories,
|
||||
mayHaveInferredPersonalization: this.props.mayHaveInferredPersonalization,
|
||||
mayHaveRecentSaves: this.props.DiscoveryStream.recentSavesEnabled,
|
||||
mayHaveWeather: this.props.mayHaveWeather,
|
||||
spocMessageVariant: this.props.spocMessageVariant,
|
||||
dispatch: this.props.dispatch,
|
||||
exitEventFired: this.state.exitEventFired
|
||||
}))));
|
||||
|
@ -13825,9 +13908,6 @@ class BaseContent extends (external_React_default()).PureComponent {
|
|||
const enabledSections = {
|
||||
topSitesEnabled: prefs["feeds.topsites"],
|
||||
pocketEnabled: prefs["feeds.section.topstories"],
|
||||
highlightsEnabled: prefs["feeds.section.highlights"],
|
||||
showSponsoredTopSitesEnabled: prefs.showSponsoredTopSites,
|
||||
showSponsoredPocketEnabled: prefs.showSponsored,
|
||||
showInferredPersonalizationEnabled: prefs[PREF_INFERRED_PERSONALIZATION_USER],
|
||||
showRecentSavesEnabled: prefs.showRecentSaves,
|
||||
topSitesRowsCount: prefs.topSitesRows,
|
||||
|
|
|
@ -219,6 +219,13 @@ module.exports = function (config) {
|
|||
{
|
||||
branches: 60,
|
||||
},
|
||||
"content-src/components/DiscoveryStreamComponents/AdBannerContextMenu/AdBannerContextMenu.jsx":
|
||||
{
|
||||
statements: 87.5,
|
||||
lines: 87.5,
|
||||
functions: 66.67,
|
||||
branches: 0,
|
||||
},
|
||||
"content-src/components/DiscoveryStreamComponents/**/*.jsx": {
|
||||
statements: 90.48,
|
||||
lines: 90.48,
|
||||
|
|
|
@ -25,7 +25,25 @@ const PREFS_BEFORE_SECTIONS = () => [
|
|||
feed: "showSearch",
|
||||
titleString: "home-prefs-search-header",
|
||||
},
|
||||
icon: "chrome://global/skin/icons/search-glass.svg",
|
||||
},
|
||||
{
|
||||
id: "weather",
|
||||
pref: {
|
||||
feed: "showWeather",
|
||||
titleString: "home-prefs-weather-header",
|
||||
descString: "home-prefs-weather-description",
|
||||
learnMore: {
|
||||
link: {
|
||||
href: "https://support.mozilla.org/kb/customize-items-on-firefox-new-tab-page",
|
||||
id: "home-prefs-weather-learn-more-link",
|
||||
},
|
||||
},
|
||||
},
|
||||
eventSource: "WEATHER",
|
||||
shouldHidePref: !Services.prefs.getBoolPref(
|
||||
"browser.newtabpage.activity-stream.system.showWeather",
|
||||
false
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "topsites",
|
||||
|
@ -45,31 +63,10 @@ const PREFS_BEFORE_SECTIONS = () => [
|
|||
: [];
|
||||
},
|
||||
},
|
||||
icon: "chrome://browser/skin/topsites.svg",
|
||||
maxRows: 4,
|
||||
rowsPref: "topSitesRows",
|
||||
eventSource: "TOP_SITES",
|
||||
},
|
||||
{
|
||||
id: "weather",
|
||||
icon: "chrome://browser/skin/weather/sunny.svg",
|
||||
pref: {
|
||||
feed: "showWeather",
|
||||
titleString: "home-prefs-weather-header",
|
||||
descString: "home-prefs-weather-description",
|
||||
learnMore: {
|
||||
link: {
|
||||
href: "https://support.mozilla.org/kb/customize-items-on-firefox-new-tab-page",
|
||||
id: "home-prefs-weather-learn-more-link",
|
||||
},
|
||||
},
|
||||
},
|
||||
eventSource: "WEATHER",
|
||||
shouldHidePref: !Services.prefs.getBoolPref(
|
||||
"browser.newtabpage.activity-stream.system.showWeather",
|
||||
false
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export class AboutPreferences {
|
||||
|
@ -192,7 +189,6 @@ export class AboutPreferences {
|
|||
const {
|
||||
id,
|
||||
pref: prefData,
|
||||
icon = "webextension",
|
||||
maxRows,
|
||||
rowsPref,
|
||||
shouldHidePref,
|
||||
|
@ -210,17 +206,11 @@ export class AboutPreferences {
|
|||
return;
|
||||
}
|
||||
|
||||
// Use full icon spec for certain protocols or fall back to packaged icon
|
||||
const iconUrl = !icon.search(/^(chrome|moz-extension|resource):/)
|
||||
? icon
|
||||
: `chrome://newtab/content/data/content/assets/glyph-${icon}-16.svg`;
|
||||
|
||||
// Add the main preference for turning on/off a section
|
||||
const sectionVbox = createAppend("vbox", contentsGroup);
|
||||
sectionVbox.setAttribute("data-subcategory", id);
|
||||
const checkbox = createAppend("checkbox", sectionVbox);
|
||||
checkbox.classList.add("section-checkbox");
|
||||
checkbox.setAttribute("src", iconUrl);
|
||||
// Setup a user event if we have an event source for this pref.
|
||||
if (eventSource) {
|
||||
this.setupUserEvent(checkbox, eventSource);
|
||||
|
|
|
@ -465,10 +465,24 @@ export const PREFS_CONFIG = new Map([
|
|||
"newtabWallpapers.customWallpaper.uploadedPreviously",
|
||||
{
|
||||
title:
|
||||
"Boolean flag to track if a user has previously uploaded a custom wallpaper",
|
||||
"Boolean flag used for telemetry to track if a user has previously uploaded a custom wallpaper",
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
[
|
||||
"newtabWallpapers.customWallpaper.fileSize.enabled",
|
||||
{
|
||||
title: "Boolean flag to enforce a maximum file size for uploaded images",
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
[
|
||||
"newtabWallpapers.customWallpaper.fileSize",
|
||||
{
|
||||
title: "Number pref of maximum file size (in MB) a user can upload",
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
[
|
||||
"newtabAdSize.variant-a",
|
||||
{
|
||||
|
|
|
@ -1151,13 +1151,18 @@ export class TelemetryFeed {
|
|||
break;
|
||||
case "WALLPAPER_CLICK":
|
||||
{
|
||||
const { selected_wallpaper, had_previous_wallpaper } = data;
|
||||
const {
|
||||
selected_wallpaper,
|
||||
had_previous_wallpaper,
|
||||
had_uploaded_previously,
|
||||
} = data;
|
||||
|
||||
// if either of the wallpaper prefs are truthy, they had a previous wallpaper
|
||||
Glean.newtab.wallpaperClick.record({
|
||||
newtab_visit_id: session.session_id,
|
||||
selected_wallpaper,
|
||||
had_previous_wallpaper,
|
||||
had_uploaded_previously,
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
@ -1171,18 +1176,6 @@ export class TelemetryFeed {
|
|||
newtab_visit_id: session.session_id,
|
||||
});
|
||||
break;
|
||||
case "WALLPAPER_UPLOAD":
|
||||
{
|
||||
const { had_uploaded_previously, had_previous_wallpaper } = data;
|
||||
|
||||
Glean.newtab.wallpaperUpload.record({
|
||||
newtab_visit_id: session.session_id,
|
||||
had_previous_wallpaper,
|
||||
had_uploaded_previously,
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
// test_tab calls SpecialPowers.spawn, which injects ContentTaskUtils in the
|
||||
// scope of the callback. Eslint doesn't know about that.
|
||||
/* global ContentTaskUtils */
|
||||
|
||||
// Test that we do not set icons in individual tile and card context menus on
|
||||
// newtab page.
|
||||
test_newtab({
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
// test_newtab calls SpecialPowers.spawn, which injects ContentTaskUtils in the
|
||||
// scope of the callback. Eslint doesn't know about that.
|
||||
/* global ContentTaskUtils */
|
||||
|
||||
const { WeatherFeed } = ChromeUtils.importESModule(
|
||||
"resource://newtab/lib/WeatherFeed.sys.mjs"
|
||||
);
|
||||
|
@ -14,8 +18,7 @@ test_newtab({
|
|||
async before({ pushPrefs }) {
|
||||
await pushPrefs(
|
||||
["browser.newtabpage.activity-stream.feeds.topsites", false],
|
||||
["browser.newtabpage.activity-stream.feeds.section.topstories", false],
|
||||
["browser.newtabpage.activity-stream.feeds.section.highlights", false]
|
||||
["browser.newtabpage.activity-stream.feeds.section.topstories", false]
|
||||
);
|
||||
},
|
||||
test: async function test_render_customizeMenu() {
|
||||
|
@ -32,8 +35,6 @@ test_newtab({
|
|||
);
|
||||
}
|
||||
const TOPSITES_PREF = "browser.newtabpage.activity-stream.feeds.topsites";
|
||||
const HIGHLIGHTS_PREF =
|
||||
"browser.newtabpage.activity-stream.feeds.section.highlights";
|
||||
const TOPSTORIES_PREF =
|
||||
"browser.newtabpage.activity-stream.feeds.section.topstories";
|
||||
|
||||
|
@ -93,26 +94,6 @@ test_newtab({
|
|||
await sectionShownPromise;
|
||||
|
||||
Assert.ok(getSection("topstories"), "Pocket section is rendered");
|
||||
|
||||
// Test that clicking the recent activity toggle will make the
|
||||
// recent activity section appear on the newtab page.
|
||||
//
|
||||
// We waive XRay wrappers because we want to call the click()
|
||||
// method defined on the toggle from this context.
|
||||
let highlightsSwitch = Cu.waiveXrays(
|
||||
content.document.querySelector("#recent-section moz-toggle")
|
||||
);
|
||||
Assert.ok(
|
||||
!Services.prefs.getBoolPref(HIGHLIGHTS_PREF),
|
||||
"Highlights pref is turned off"
|
||||
);
|
||||
Assert.ok(!getSection("highlights"), "Highlights section is not rendered");
|
||||
|
||||
sectionShownPromise = promiseSectionShown("highlights");
|
||||
highlightsSwitch.click();
|
||||
await sectionShownPromise;
|
||||
|
||||
Assert.ok(getSection("highlights"), "Highlights section is rendered");
|
||||
},
|
||||
async after() {
|
||||
Services.prefs.clearUserPref(
|
||||
|
@ -121,9 +102,6 @@ test_newtab({
|
|||
Services.prefs.clearUserPref(
|
||||
"browser.newtabpage.activity-stream.feeds.section.topstories"
|
||||
);
|
||||
Services.prefs.clearUserPref(
|
||||
"browser.newtabpage.activity-stream.feeds.section.highlights"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
// test_newtab calls SpecialPowers.spawn, which injects ContentTaskUtils in the
|
||||
// scope of the callback. Eslint doesn't know about that.
|
||||
/* global ContentTaskUtils */
|
||||
|
||||
// Test that the customization menu is rendered.
|
||||
test_newtab({
|
||||
async before() {
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
// test_newtab calls SpecialPowers.spawn, which injects ContentTaskUtils in the
|
||||
// scope of the callback. Eslint doesn't know about that.
|
||||
/* global ContentTaskUtils */
|
||||
|
||||
// Test that the customization menu is rendered.
|
||||
test_newtab({
|
||||
test: async function test_render_customizeMenu() {
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// test_newtab calls SpecialPowers.spawn, which injects ContentTaskUtils in the
|
||||
// scope of the callback. Eslint doesn't know about that.
|
||||
/* global ContentTaskUtils */
|
||||
|
||||
// If this fails it could be because of schema changes.
|
||||
// `topstories.json` defines the stories shown
|
||||
test_newtab({
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
// test_newtab calls SpecialPowers.spawn, which injects ContentTaskUtils in the
|
||||
// scope of the callback. Eslint doesn't know about that.
|
||||
/* global ContentTaskUtils */
|
||||
|
||||
async function before({ pushPrefs }) {
|
||||
await pushPrefs([
|
||||
"browser.newtabpage.activity-stream.discoverystream.config",
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
// test_newtab calls SpecialPowers.spawn, which injects ContentTaskUtils in the
|
||||
// scope of the callback. Eslint doesn't know about that.
|
||||
/* global ContentTaskUtils */
|
||||
|
||||
// Tests that:
|
||||
// 1. Top sites header is hidden and the topsites section is not collapsed on load.
|
||||
// 2. Pocket header and section are visible and not collapsed on load.
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
// test_newtab calls SpecialPowers.spawn, which injects ContentTaskUtils in the
|
||||
// scope of the callback. Eslint doesn't know about that.
|
||||
/* global ContentTaskUtils */
|
||||
|
||||
test_newtab({
|
||||
async before() {
|
||||
// Some reason test-linux1804-64-qr/debug can end up with example.com, so
|
||||
|
|
|
@ -47,31 +47,19 @@ describe("ContentSection", () => {
|
|||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it("should have data-eventSource attributes on relevent pref changing inputs", () => {
|
||||
it("should have data-eventSource attributes on relevant pref changing inputs", () => {
|
||||
wrapper = mount(<ContentSection {...DEFAULT_PROPS} />);
|
||||
assert.equal(
|
||||
wrapper.find("#weather-toggle").prop("data-eventSource"),
|
||||
"WEATHER"
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find("#shortcuts-toggle").prop("data-eventSource"),
|
||||
"TOP_SITES"
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find("#sponsored-shortcuts").prop("data-eventSource"),
|
||||
"SPONSORED_TOP_SITES"
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find("#pocket-toggle").prop("data-eventSource"),
|
||||
"TOP_STORIES"
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find("#sponsored-pocket").prop("data-eventSource"),
|
||||
"POCKET_SPOCS"
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find("#highlights-toggle").prop("data-eventSource"),
|
||||
"HIGHLIGHTS"
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find("#weather-toggle").prop("data-eventSource"),
|
||||
"WEATHER"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -121,15 +121,12 @@ describe("Discovery Stream <AdBanner>", () => {
|
|||
assert.deepEqual(aside.prop("style"), { gridRow: clampedRow });
|
||||
});
|
||||
|
||||
it("should have the dismiss button be visible", () => {
|
||||
const dismiss = wrapper.find(".ad-banner-dismiss .icon-dismiss");
|
||||
it("should have the context menu button be visible", () => {
|
||||
const dismiss = wrapper.find("moz-button");
|
||||
assert.ok(dismiss.exists());
|
||||
|
||||
dismiss.simulate("click");
|
||||
|
||||
let [action] = dispatch.secondCall.args;
|
||||
assert.equal(action.type, "BLOCK_URL");
|
||||
assert.equal(action.data[0].id, DEFAULT_PROPS.spoc.id);
|
||||
// The rest of the context menu functionality is now tested in
|
||||
// AdBannerContextMenu.test.jsx
|
||||
});
|
||||
|
||||
it("should call onLinkClick when banner is clicked", () => {
|
||||
|
@ -142,7 +139,7 @@ describe("Discovery Stream <AdBanner>", () => {
|
|||
ac.DiscoveryStreamUserEvent({
|
||||
event: "CLICK",
|
||||
source: "FOO",
|
||||
// Banner ads dont have a position, but a row number
|
||||
// Banner ads don't have a position, but a row number
|
||||
action_position: DEFAULT_PROPS.row,
|
||||
value: {
|
||||
card_type: "spoc",
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import { shallow } from "enzyme";
|
||||
import { AdBannerContextMenu } from "content-src/components/DiscoveryStreamComponents/AdBannerContextMenu/AdBannerContextMenu";
|
||||
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
|
||||
import React from "react";
|
||||
|
||||
describe("<AdBannerContextMenu>", () => {
|
||||
let wrapper;
|
||||
|
||||
describe("Ad banner context menu options", () => {
|
||||
const props = {
|
||||
spoc: { url: "https://www.test.com/", shim: "aaabbbcccddd" },
|
||||
position: 1,
|
||||
type: "billboard",
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<AdBannerContextMenu {...props} />);
|
||||
});
|
||||
|
||||
it("should render a context menu button", () => {
|
||||
assert.ok(wrapper.exists());
|
||||
assert.ok(
|
||||
wrapper.find("moz-button").exists(),
|
||||
"context menu button exists"
|
||||
);
|
||||
});
|
||||
|
||||
it("should render LinkMenu when context menu button is clicked", () => {
|
||||
let button = wrapper.find("moz-button");
|
||||
button.simulate("click", {
|
||||
preventDefault: () => {},
|
||||
});
|
||||
assert.equal(wrapper.find(LinkMenu).length, 1);
|
||||
});
|
||||
|
||||
it("should pass props to LinkMenu", () => {
|
||||
wrapper.find("moz-button").simulate("click", {
|
||||
preventDefault: () => {},
|
||||
});
|
||||
const linkMenuProps = wrapper.find(LinkMenu).props();
|
||||
[
|
||||
"onUpdate",
|
||||
"dispatch",
|
||||
"options",
|
||||
"shouldSendImpressionStats",
|
||||
"userEvent",
|
||||
"site",
|
||||
"index",
|
||||
"source",
|
||||
].forEach(prop => assert.property(linkMenuProps, prop));
|
||||
});
|
||||
|
||||
it("should pass through the correct menu options to LinkMenu for ad banners", () => {
|
||||
wrapper.find("moz-button").simulate("click", {
|
||||
preventDefault: () => {},
|
||||
});
|
||||
const linkMenuProps = wrapper.find(LinkMenu).props();
|
||||
assert.deepEqual(linkMenuProps.options, [
|
||||
"BlockAdUrl",
|
||||
"ManageSponsoredContent",
|
||||
"OurSponsorsAndYourPrivacy",
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -125,8 +125,8 @@ describe("AboutPreferences Feed", () => {
|
|||
assert.calledOnce(stub);
|
||||
const [, structure] = stub.firstCall.args;
|
||||
assert.equal(structure[0].id, "search");
|
||||
assert.equal(structure[1].id, "topsites");
|
||||
assert.equal(structure[2].id, "weather");
|
||||
assert.equal(structure[1].id, "weather");
|
||||
assert.equal(structure[2].id, "topsites");
|
||||
assert.equal(structure[3].id, "topstories");
|
||||
assert.isEmpty(structure[3].rowsPref);
|
||||
});
|
||||
|
@ -228,38 +228,6 @@ describe("AboutPreferences Feed", () => {
|
|||
assert.notCalled(Preferences.add);
|
||||
});
|
||||
});
|
||||
describe("pref icon", () => {
|
||||
it("should default to webextension icon", () => {
|
||||
prefStructure = [{ pref: { feed: "feed" } }];
|
||||
|
||||
testRender();
|
||||
|
||||
assert.calledWith(
|
||||
node.setAttribute,
|
||||
"src",
|
||||
"chrome://newtab/content/data/content/assets/glyph-webextension-16.svg"
|
||||
);
|
||||
});
|
||||
it("should use desired glyph icon", () => {
|
||||
prefStructure = [{ icon: "mail", pref: { feed: "feed" } }];
|
||||
|
||||
testRender();
|
||||
|
||||
assert.calledWith(
|
||||
node.setAttribute,
|
||||
"src",
|
||||
"chrome://newtab/content/data/content/assets/glyph-mail-16.svg"
|
||||
);
|
||||
});
|
||||
it("should use specified chrome icon", () => {
|
||||
const icon = "chrome://the/icon.svg";
|
||||
prefStructure = [{ icon, pref: { feed: "feed" } }];
|
||||
|
||||
testRender();
|
||||
|
||||
assert.calledWith(node.setAttribute, "src", icon);
|
||||
});
|
||||
});
|
||||
describe("title line", () => {
|
||||
it("should render a title", () => {
|
||||
const titleString = "the_title";
|
||||
|
|
|
@ -290,8 +290,8 @@ quickactions-bookmarks2 = Manage bookmarks
|
|||
quickactions-cmd-bookmarks = bookmarks
|
||||
|
||||
# Opens a SUMO article explaining how to clear history
|
||||
quickactions-clearhistory = Clear History
|
||||
quickactions-cmd-clearhistory = clear history
|
||||
quickactions-clearrecenthistory = Clear recent history
|
||||
quickactions-cmd-clearrecenthistory = clear recent history, history
|
||||
|
||||
# Opens about:downloads page
|
||||
quickactions-downloads2 = View downloads
|
||||
|
@ -301,6 +301,17 @@ quickactions-cmd-downloads = downloads
|
|||
quickactions-extensions = Manage extensions
|
||||
quickactions-cmd-extensions = extensions
|
||||
|
||||
# Opens Firefox View
|
||||
quickactions-firefoxview = Open { -firefoxview-brand-name }
|
||||
# English is using "view" and "open view", since the feature name is
|
||||
# "Firefox View". If you have translated the name in your language, you
|
||||
# should use a word related to the existing translation.
|
||||
quickactions-cmd-firefoxview = open { -firefoxview-brand-name }, { -firefoxview-brand-name }, open view, view
|
||||
|
||||
# Opens SUMO home page
|
||||
quickactions-help = { -brand-product-name } help
|
||||
quickactions-cmd-help = help, support
|
||||
|
||||
# Opens the devtools web inspector
|
||||
quickactions-inspector2 = Open Developer Tools
|
||||
quickactions-cmd-inspector = inspector, devtools
|
||||
|
|
|
@ -142,7 +142,7 @@ actions-callout-title = Complete common tasks or access basic settings
|
|||
# These example text inputs correlate to the the following strings
|
||||
# (either matching the whole string, or the first word of the string).
|
||||
# "print" - quickactions-cmd-print
|
||||
# "clear" - quickactions-cmd-clearhistory
|
||||
# "clear" - quickactions-cmd-clearrecenthistory
|
||||
# When localizing, ensure the translations match to ensure the action button appears as expected.
|
||||
actions-callout-subtitle = Try typing an action like “print” to print a page, or “clear” to clear your history.
|
||||
|
||||
|
|
|
@ -285,15 +285,10 @@ newtab-custom-row-selector =
|
|||
[one] { $num } row
|
||||
*[other] { $num } rows
|
||||
}
|
||||
newtab-custom-sponsored-sites = Sponsored shortcuts
|
||||
newtab-custom-stories-toggle =
|
||||
.label = Recommended stories
|
||||
.description = Exceptional content curated by the { -brand-product-name } family
|
||||
newtab-custom-pocket-sponsored = Sponsored stories
|
||||
newtab-custom-pocket-show-recent-saves = Show recent saves
|
||||
newtab-custom-recent-toggle =
|
||||
.label = Recent activity
|
||||
.description = A selection of recent sites and content
|
||||
newtab-custom-weather-toggle =
|
||||
.label = Weather
|
||||
.description = Today’s forecast at a glance
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"af": {
|
||||
"pin": false,
|
||||
|
@ -37,7 +37,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"an": {
|
||||
"pin": false,
|
||||
|
@ -57,7 +57,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ar": {
|
||||
"pin": false,
|
||||
|
@ -77,7 +77,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ast": {
|
||||
"pin": false,
|
||||
|
@ -97,7 +97,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"az": {
|
||||
"pin": false,
|
||||
|
@ -117,7 +117,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"be": {
|
||||
"pin": false,
|
||||
|
@ -137,7 +137,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"bg": {
|
||||
"pin": false,
|
||||
|
@ -157,7 +157,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"bn": {
|
||||
"pin": false,
|
||||
|
@ -177,7 +177,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"bo": {
|
||||
"pin": false,
|
||||
|
@ -197,7 +197,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"br": {
|
||||
"pin": false,
|
||||
|
@ -217,7 +217,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"brx": {
|
||||
"pin": false,
|
||||
|
@ -237,7 +237,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"bs": {
|
||||
"pin": false,
|
||||
|
@ -257,7 +257,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ca": {
|
||||
"pin": false,
|
||||
|
@ -277,7 +277,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ca-valencia": {
|
||||
"pin": false,
|
||||
|
@ -297,7 +297,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"cak": {
|
||||
"pin": false,
|
||||
|
@ -317,7 +317,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ckb": {
|
||||
"pin": false,
|
||||
|
@ -337,7 +337,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"cs": {
|
||||
"pin": false,
|
||||
|
@ -357,7 +357,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"cy": {
|
||||
"pin": false,
|
||||
|
@ -377,7 +377,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"da": {
|
||||
"pin": false,
|
||||
|
@ -397,7 +397,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"de": {
|
||||
"pin": false,
|
||||
|
@ -417,7 +417,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"dsb": {
|
||||
"pin": false,
|
||||
|
@ -437,7 +437,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"el": {
|
||||
"pin": false,
|
||||
|
@ -457,7 +457,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"en-CA": {
|
||||
"pin": false,
|
||||
|
@ -477,7 +477,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"en-GB": {
|
||||
"pin": false,
|
||||
|
@ -497,7 +497,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"eo": {
|
||||
"pin": false,
|
||||
|
@ -517,7 +517,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"es-AR": {
|
||||
"pin": false,
|
||||
|
@ -537,7 +537,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"es-CL": {
|
||||
"pin": false,
|
||||
|
@ -557,7 +557,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"es-ES": {
|
||||
"pin": false,
|
||||
|
@ -577,7 +577,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"es-MX": {
|
||||
"pin": false,
|
||||
|
@ -597,7 +597,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"et": {
|
||||
"pin": false,
|
||||
|
@ -617,7 +617,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"eu": {
|
||||
"pin": false,
|
||||
|
@ -637,7 +637,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"fa": {
|
||||
"pin": false,
|
||||
|
@ -657,7 +657,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ff": {
|
||||
"pin": false,
|
||||
|
@ -677,7 +677,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"fi": {
|
||||
"pin": false,
|
||||
|
@ -697,7 +697,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"fr": {
|
||||
"pin": false,
|
||||
|
@ -717,7 +717,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"fur": {
|
||||
"pin": false,
|
||||
|
@ -737,7 +737,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"fy-NL": {
|
||||
"pin": false,
|
||||
|
@ -757,7 +757,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ga-IE": {
|
||||
"pin": false,
|
||||
|
@ -777,7 +777,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"gd": {
|
||||
"pin": false,
|
||||
|
@ -797,7 +797,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"gl": {
|
||||
"pin": false,
|
||||
|
@ -817,7 +817,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"gn": {
|
||||
"pin": false,
|
||||
|
@ -837,7 +837,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"gu-IN": {
|
||||
"pin": false,
|
||||
|
@ -857,7 +857,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"he": {
|
||||
"pin": false,
|
||||
|
@ -877,7 +877,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"hi-IN": {
|
||||
"pin": false,
|
||||
|
@ -897,7 +897,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"hr": {
|
||||
"pin": false,
|
||||
|
@ -917,7 +917,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"hsb": {
|
||||
"pin": false,
|
||||
|
@ -937,7 +937,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"hu": {
|
||||
"pin": false,
|
||||
|
@ -957,7 +957,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"hy-AM": {
|
||||
"pin": false,
|
||||
|
@ -977,7 +977,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"hye": {
|
||||
"pin": false,
|
||||
|
@ -997,7 +997,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ia": {
|
||||
"pin": false,
|
||||
|
@ -1017,7 +1017,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"id": {
|
||||
"pin": false,
|
||||
|
@ -1037,7 +1037,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"is": {
|
||||
"pin": false,
|
||||
|
@ -1057,7 +1057,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"it": {
|
||||
"pin": false,
|
||||
|
@ -1077,7 +1077,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ja": {
|
||||
"pin": false,
|
||||
|
@ -1095,7 +1095,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ja-JP-mac": {
|
||||
"pin": false,
|
||||
|
@ -1103,7 +1103,7 @@
|
|||
"macosx64",
|
||||
"macosx64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ka": {
|
||||
"pin": false,
|
||||
|
@ -1123,7 +1123,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"kab": {
|
||||
"pin": false,
|
||||
|
@ -1143,7 +1143,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"kk": {
|
||||
"pin": false,
|
||||
|
@ -1163,7 +1163,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"km": {
|
||||
"pin": false,
|
||||
|
@ -1183,7 +1183,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"kn": {
|
||||
"pin": false,
|
||||
|
@ -1203,7 +1203,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ko": {
|
||||
"pin": false,
|
||||
|
@ -1223,7 +1223,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"lij": {
|
||||
"pin": false,
|
||||
|
@ -1243,7 +1243,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"lo": {
|
||||
"pin": false,
|
||||
|
@ -1263,7 +1263,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"lt": {
|
||||
"pin": false,
|
||||
|
@ -1283,7 +1283,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ltg": {
|
||||
"pin": false,
|
||||
|
@ -1303,7 +1303,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"lv": {
|
||||
"pin": false,
|
||||
|
@ -1323,7 +1323,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"meh": {
|
||||
"pin": false,
|
||||
|
@ -1343,7 +1343,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"mk": {
|
||||
"pin": false,
|
||||
|
@ -1363,7 +1363,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ml": {
|
||||
"pin": false,
|
||||
|
@ -1383,7 +1383,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"mr": {
|
||||
"pin": false,
|
||||
|
@ -1403,7 +1403,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ms": {
|
||||
"pin": false,
|
||||
|
@ -1423,7 +1423,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"my": {
|
||||
"pin": false,
|
||||
|
@ -1443,7 +1443,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"nb-NO": {
|
||||
"pin": false,
|
||||
|
@ -1463,7 +1463,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ne-NP": {
|
||||
"pin": false,
|
||||
|
@ -1483,7 +1483,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"nl": {
|
||||
"pin": false,
|
||||
|
@ -1503,7 +1503,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"nn-NO": {
|
||||
"pin": false,
|
||||
|
@ -1523,7 +1523,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"oc": {
|
||||
"pin": false,
|
||||
|
@ -1543,7 +1543,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"pa-IN": {
|
||||
"pin": false,
|
||||
|
@ -1563,7 +1563,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"pl": {
|
||||
"pin": false,
|
||||
|
@ -1583,7 +1583,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"pt-BR": {
|
||||
"pin": false,
|
||||
|
@ -1603,7 +1603,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"pt-PT": {
|
||||
"pin": false,
|
||||
|
@ -1623,7 +1623,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"rm": {
|
||||
"pin": false,
|
||||
|
@ -1643,7 +1643,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ro": {
|
||||
"pin": false,
|
||||
|
@ -1663,7 +1663,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ru": {
|
||||
"pin": false,
|
||||
|
@ -1683,7 +1683,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"sat": {
|
||||
"pin": false,
|
||||
|
@ -1703,7 +1703,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"sc": {
|
||||
"pin": false,
|
||||
|
@ -1723,7 +1723,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"scn": {
|
||||
"pin": false,
|
||||
|
@ -1743,7 +1743,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"sco": {
|
||||
"pin": false,
|
||||
|
@ -1763,7 +1763,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"si": {
|
||||
"pin": false,
|
||||
|
@ -1783,7 +1783,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"sk": {
|
||||
"pin": false,
|
||||
|
@ -1803,7 +1803,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"skr": {
|
||||
"pin": false,
|
||||
|
@ -1823,7 +1823,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"sl": {
|
||||
"pin": false,
|
||||
|
@ -1843,7 +1843,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"son": {
|
||||
"pin": false,
|
||||
|
@ -1863,7 +1863,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"sq": {
|
||||
"pin": false,
|
||||
|
@ -1883,7 +1883,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"sr": {
|
||||
"pin": false,
|
||||
|
@ -1903,7 +1903,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"sv-SE": {
|
||||
"pin": false,
|
||||
|
@ -1923,7 +1923,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"szl": {
|
||||
"pin": false,
|
||||
|
@ -1943,7 +1943,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ta": {
|
||||
"pin": false,
|
||||
|
@ -1963,7 +1963,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"te": {
|
||||
"pin": false,
|
||||
|
@ -1983,7 +1983,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"tg": {
|
||||
"pin": false,
|
||||
|
@ -2003,7 +2003,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"th": {
|
||||
"pin": false,
|
||||
|
@ -2023,7 +2023,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"tl": {
|
||||
"pin": false,
|
||||
|
@ -2043,7 +2043,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"tr": {
|
||||
"pin": false,
|
||||
|
@ -2063,7 +2063,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"trs": {
|
||||
"pin": false,
|
||||
|
@ -2083,7 +2083,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"uk": {
|
||||
"pin": false,
|
||||
|
@ -2103,7 +2103,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"ur": {
|
||||
"pin": false,
|
||||
|
@ -2123,7 +2123,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"uz": {
|
||||
"pin": false,
|
||||
|
@ -2143,7 +2143,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"vi": {
|
||||
"pin": false,
|
||||
|
@ -2163,7 +2163,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"wo": {
|
||||
"pin": false,
|
||||
|
@ -2183,7 +2183,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"xh": {
|
||||
"pin": false,
|
||||
|
@ -2203,7 +2203,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"zh-CN": {
|
||||
"pin": false,
|
||||
|
@ -2223,7 +2223,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
},
|
||||
"zh-TW": {
|
||||
"pin": false,
|
||||
|
@ -2243,6 +2243,6 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "dd8d34b0244b4db61494c64d78c4b96bf367f2df"
|
||||
"revision": "e234130176d2813fec2397bbf3a313909565006e"
|
||||
}
|
||||
}
|
|
@ -221,7 +221,7 @@
|
|||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
#tabbrowser-tabs[movingtab] &[fadein]:not([selected]):not([multiselected]),
|
||||
#tabbrowser-tabs[movingtab] :not(tab-group:active) > &[fadein]:not(:active, [multiselected]),
|
||||
&[multiselected-move-together],
|
||||
&[tabdrop-samewindow] {
|
||||
transition: var(--tab-dragover-transition);
|
||||
|
@ -1028,9 +1028,7 @@
|
|||
pointer-events: none; /* avoid blocking dragover events on scroll buttons */
|
||||
}
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
#tabbrowser-tabs[movingtab] &,
|
||||
&[multiselected-move-together],
|
||||
&[tabdrop-samewindow] {
|
||||
#tabbrowser-tabs[movingtab] &:not(:active) {
|
||||
transition: var(--tab-dragover-transition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,12 @@
|
|||
@media (-moz-windows-mica) {
|
||||
&:not([lwtheme]) {
|
||||
background-color: transparent;
|
||||
|
||||
/* stylelint-disable-next-line media-query-no-invalid */
|
||||
@media -moz-pref("widget.windows.mica.toplevel-backdrop", 2) {
|
||||
/* For acrylic, do the same we do for popups to guarantee some contrast */
|
||||
background-color: light-dark(rgba(255, 255, 255, .6), rgba(0, 0, 0, .6));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ buildscript {
|
|||
repositories {
|
||||
gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
|
||||
maven {
|
||||
url repository
|
||||
url = repository
|
||||
if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
|
||||
allowInsecureProtocol = true
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ allprojects {
|
|||
repositories {
|
||||
gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
|
||||
maven {
|
||||
url repository
|
||||
url = repository
|
||||
if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
|
||||
allowInsecureProtocol = true
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ allprojects {
|
|||
}
|
||||
|
||||
task downloadDependencies() {
|
||||
description 'Download all dependencies to the Gradle cache'
|
||||
description = 'Download all dependencies to the Gradle cache'
|
||||
doLast {
|
||||
configurations.each { configuration ->
|
||||
if (configuration.canBeResolved) {
|
||||
|
|
|
@ -58,7 +58,7 @@ def android_sdk_version():
|
|||
# If you think you can't handle the whole set of changes, please reach out to the Release
|
||||
# Engineering team.
|
||||
return namespace(
|
||||
build_tools_version="35.0.0",
|
||||
build_tools_version="35.0.1",
|
||||
compile_sdk_version="35",
|
||||
target_sdk_version="35",
|
||||
min_sdk_version="21",
|
||||
|
|
|
@ -577,6 +577,10 @@ netmonitor.toolbar.priority=Priority
|
|||
# in the network table toolbar, above the "file" column.
|
||||
netmonitor.toolbar.file=File
|
||||
|
||||
# LOCALIZATION NOTE (netmonitor.toolbar.path): This is the label displayed
|
||||
# in the network table toolbar, above the "Path" column.
|
||||
netmonitor.toolbar.path=Path
|
||||
|
||||
# LOCALIZATION NOTE (netmonitor.toolbar.url): This is the label displayed
|
||||
# in the network table toolbar, above the "url" column.
|
||||
netmonitor.toolbar.url=URL
|
||||
|
|
|
@ -70,6 +70,14 @@ perftools-button-add-directory = Add a directory
|
|||
perftools-button-remove-directory = Remove selected
|
||||
perftools-button-edit-settings = Edit Settings…
|
||||
|
||||
## More actions menu
|
||||
|
||||
perftools-menu-more-actions-button =
|
||||
.title = More actions
|
||||
perftools-menu-more-actions-restart-with-profiling = Restart { -brand-shorter-name } with startup profiling enabled
|
||||
perftools-menu-more-actions-copy-for-startup = Copy environment variables for startup profiling
|
||||
perftools-menu-more-actions-copy-for-perf-tests = Copy parameters for performance tests
|
||||
|
||||
## These messages are descriptions of the threads that can be enabled for the profiler.
|
||||
|
||||
perftools-thread-gecko-main =
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
Component,
|
||||
} = require("resource://devtools/client/shared/vendor/react.js");
|
||||
const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
|
||||
const {
|
||||
L10N,
|
||||
} = require("resource://devtools/client/netmonitor/src/utils/l10n.js");
|
||||
const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
|
||||
const {
|
||||
connect,
|
||||
} = require("resource://devtools/client/shared/vendor/react-redux.js");
|
||||
const {
|
||||
propertiesEqual,
|
||||
} = require("resource://devtools/client/netmonitor/src/utils/request-utils.js");
|
||||
const { truncateString } = require("resource://devtools/shared/string.js");
|
||||
const {
|
||||
MAX_UI_STRING_LENGTH,
|
||||
} = require("resource://devtools/client/netmonitor/src/constants.js");
|
||||
const {
|
||||
getOverriddenUrl,
|
||||
} = require("resource://devtools/client/netmonitor/src/selectors/index.js");
|
||||
|
||||
const UPDATED_FILE_PROPS = ["urlDetails", "waitingTime"];
|
||||
|
||||
class RequestListColumnPath extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
item: PropTypes.object.isRequired,
|
||||
onWaterfallMouseDown: PropTypes.func,
|
||||
isOverridden: PropTypes.bool.isRequired,
|
||||
overriddenUrl: PropTypes.string,
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return (
|
||||
!propertiesEqual(UPDATED_FILE_PROPS, this.props.item, nextProps.item) ||
|
||||
nextProps.overriddenUrl !== this.props.overriddenUrl
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
item: { urlDetails },
|
||||
isOverridden,
|
||||
overriddenUrl,
|
||||
} = this.props;
|
||||
|
||||
const originalFileURL = urlDetails.url;
|
||||
const decodedFileURL = urlDetails.unicodeUrl;
|
||||
const ORIGINAL_FILE_URL = L10N.getFormatStr(
|
||||
"netRequest.originalFileURL.tooltip",
|
||||
originalFileURL
|
||||
);
|
||||
const DECODED_FILE_URL = L10N.getFormatStr(
|
||||
"netRequest.decodedFileURL.tooltip",
|
||||
decodedFileURL
|
||||
);
|
||||
const requestedPath = urlDetails.path;
|
||||
const fileToolTip =
|
||||
originalFileURL === decodedFileURL
|
||||
? originalFileURL
|
||||
: ORIGINAL_FILE_URL + "\n\n" + DECODED_FILE_URL;
|
||||
|
||||
// Build extra content for the title if the request is overridden.
|
||||
const overrideTitle = isOverridden ? ` → ${overriddenUrl}` : "";
|
||||
|
||||
return dom.td(
|
||||
{
|
||||
className: "requests-list-column requests-list-path",
|
||||
title:
|
||||
truncateString(fileToolTip, MAX_UI_STRING_LENGTH) + overrideTitle,
|
||||
},
|
||||
dom.div({}, truncateString(requestedPath, MAX_UI_STRING_LENGTH))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(
|
||||
(state, props) => {
|
||||
const overriddenUrl = getOverriddenUrl(state, props.item.urlDetails?.url);
|
||||
return {
|
||||
isOverridden: !!overriddenUrl,
|
||||
overriddenUrl,
|
||||
};
|
||||
},
|
||||
{},
|
||||
undefined,
|
||||
{ storeKey: "toolbox-store" }
|
||||
)(RequestListColumnPath);
|
|
@ -26,6 +26,7 @@ const {
|
|||
RequestListColumnCookies,
|
||||
RequestListColumnDomain,
|
||||
RequestListColumnFile,
|
||||
RequestListColumnPath,
|
||||
RequestListColumnMethod,
|
||||
RequestListColumnProtocol,
|
||||
RequestListColumnRemoteIP,
|
||||
|
@ -65,6 +66,11 @@ loader.lazyGetter(this, "RequestListColumnFile", function () {
|
|||
require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnFile.js")
|
||||
);
|
||||
});
|
||||
loader.lazyGetter(this, "RequestListColumnPath", function () {
|
||||
return createFactory(
|
||||
require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnPath.js")
|
||||
);
|
||||
});
|
||||
loader.lazyGetter(this, "RequestListColumnUrl", function () {
|
||||
return createFactory(
|
||||
require("resource://devtools/client/netmonitor/src/components/request-list/RequestListColumnUrl.js")
|
||||
|
@ -199,6 +205,11 @@ const COLUMN_COMPONENTS = [
|
|||
ColumnComponent: RequestListColumnFile,
|
||||
props: ["onWaterfallMouseDown", "slowLimit"],
|
||||
},
|
||||
{
|
||||
column: "path",
|
||||
ColumnComponent: RequestListColumnPath,
|
||||
props: ["onWaterfallMouseDown"],
|
||||
},
|
||||
{
|
||||
column: "url",
|
||||
ColumnComponent: RequestListColumnUrl,
|
||||
|
|
|
@ -11,6 +11,7 @@ DevToolsModules(
|
|||
"RequestListColumnInitiator.js",
|
||||
"RequestListColumnMethod.js",
|
||||
"RequestListColumnOverride.js",
|
||||
"RequestListColumnPath.js",
|
||||
"RequestListColumnPriority.js",
|
||||
"RequestListColumnProtocol.js",
|
||||
"RequestListColumnRemoteIP.js",
|
||||
|
|
|
@ -299,6 +299,10 @@ const HEADERS = [
|
|||
name: "file",
|
||||
canFilter: false,
|
||||
},
|
||||
{
|
||||
name: "path",
|
||||
canFilter: false,
|
||||
},
|
||||
{
|
||||
name: "url",
|
||||
canFilter: true,
|
||||
|
|
|
@ -33,6 +33,7 @@ const cols = {
|
|||
method: true,
|
||||
domain: true,
|
||||
file: true,
|
||||
path: false,
|
||||
url: false,
|
||||
protocol: false,
|
||||
scheme: false,
|
||||
|
|
|
@ -292,6 +292,18 @@ function getUrlScheme(url) {
|
|||
return protocol.replace(":", "").toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpers for getting the full path portion of a url.
|
||||
*
|
||||
* @param {string|URL} url - unvalidated url string or URL instance
|
||||
* @return {string} string path of a url
|
||||
*/
|
||||
function getUrlPath(url) {
|
||||
const href = getUrlProperty(url, "href");
|
||||
const origin = getUrlProperty(url, "origin");
|
||||
return href.replace(origin, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract several details fields from a URL at once.
|
||||
*/
|
||||
|
@ -302,6 +314,7 @@ function getUrlDetails(url) {
|
|||
const hostname = getUrlHostName(urlObject);
|
||||
const unicodeUrl = getUnicodeUrl(urlObject);
|
||||
const scheme = getUrlScheme(urlObject);
|
||||
const path = getUrlPath(urlObject);
|
||||
|
||||
// If the hostname contains unreadable ASCII characters, we need to do the
|
||||
// following two steps:
|
||||
|
@ -338,6 +351,7 @@ function getUrlDetails(url) {
|
|||
unicodeUrl,
|
||||
isLocal,
|
||||
url,
|
||||
path,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -181,6 +181,8 @@ skip-if = [
|
|||
|
||||
["browser_net_column_headers_tooltips.js"]
|
||||
|
||||
["browser_net_column_path.js"]
|
||||
|
||||
["browser_net_column_slow-request-indicator.js"]
|
||||
|
||||
["browser_net_columns_last_column.js"]
|
||||
|
|
46
devtools/client/netmonitor/test/browser_net_column_path.js
Normal file
46
devtools/client/netmonitor/test/browser_net_column_path.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests for path column. Note that the column
|
||||
* header is visible only if there are requests in the list.
|
||||
*/
|
||||
add_task(async function () {
|
||||
const { monitor, tab } = await initNetMonitor(SIMPLE_URL, {
|
||||
requestCount: 1,
|
||||
});
|
||||
const { document } = monitor.panelWin;
|
||||
info("Starting test... ");
|
||||
|
||||
const onNetworkEvents = waitForNetworkEvents(monitor, 2);
|
||||
await reloadBrowser();
|
||||
await ContentTask.spawn(tab.linkedBrowser, null, () => {
|
||||
content.wrappedJSObject.fetch("data:text/plain,some_text");
|
||||
});
|
||||
await onNetworkEvents;
|
||||
|
||||
await showColumn(monitor, "path");
|
||||
|
||||
const pathColumn = document.querySelector(`.requests-list-path`);
|
||||
const requestList = document.querySelectorAll(
|
||||
".network-monitor .request-list-item"
|
||||
);
|
||||
|
||||
ok(pathColumn, "Path column should be visible");
|
||||
is(
|
||||
requestList[0].querySelector(".requests-list-path div:first-child")
|
||||
.textContent,
|
||||
"/browser/devtools/client/netmonitor/test/html_simple-test-page.html",
|
||||
"Path content should contain the request url without origin"
|
||||
);
|
||||
is(
|
||||
requestList[1].querySelector(".requests-list-path div:first-child")
|
||||
.textContent,
|
||||
"data:text/plain,some_text",
|
||||
"Path content should contain the data url"
|
||||
);
|
||||
|
||||
await teardown(monitor);
|
||||
});
|
15
devtools/client/performance-new/@types/perf.d.ts
vendored
15
devtools/client/performance-new/@types/perf.d.ts
vendored
|
@ -186,15 +186,6 @@ export type ReceiveProfile = (
|
|||
getSymbolTableCallback: GetSymbolTableCallback
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* This is the type signature for a function to restart the browser with a given
|
||||
* environment variable. Currently only implemented for the popup.
|
||||
*/
|
||||
export type RestartBrowserWithEnvironmentVariable = (
|
||||
envName: string,
|
||||
value: string
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* This is the type signature for the event listener that's called once the
|
||||
* profile has been obtained.
|
||||
|
@ -397,6 +388,12 @@ export interface PerformancePref {
|
|||
* button in the customization palette.
|
||||
*/
|
||||
PopupFeatureFlag: "devtools.performance.popup.feature-flag";
|
||||
/**
|
||||
* This preference controls whether about:profiling contains some Firefox
|
||||
* developer-specific options. For example when true the "more actions" menu
|
||||
* contains items to copy parameters to use with mach try perf.
|
||||
*/
|
||||
AboutProfilingHasDeveloperOptions: "devtools.performance.aboutprofiling.has-developer-options";
|
||||
}
|
||||
|
||||
/* The next 2 types bring some duplication from gecko.d.ts, but this is simpler
|
||||
|
|
|
@ -4,22 +4,8 @@
|
|||
// @ts-check
|
||||
|
||||
/**
|
||||
* @template P
|
||||
* @typedef {import("react-redux").ResolveThunks<P>} ResolveThunks<P>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} StateProps
|
||||
* @property {boolean?} isSupportedPlatform
|
||||
* @property {PageContext} pageContext
|
||||
* @property {string | null} promptEnvRestart
|
||||
* @property {(() => void) | undefined} openRemoteDevTools
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {StateProps} Props
|
||||
* @typedef {import("../../@types/perf").State} StoreState
|
||||
* @typedef {import("../../@types/perf").PageContext} PageContext
|
||||
* @typedef {import("../../@types/perf").PerformancePref} PerformancePref
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
@ -27,6 +13,9 @@
|
|||
const {
|
||||
PureComponent,
|
||||
createFactory,
|
||||
createElement: h,
|
||||
Fragment,
|
||||
createRef,
|
||||
} = require("resource://devtools/client/shared/vendor/react.mjs");
|
||||
const {
|
||||
connect,
|
||||
|
@ -51,6 +40,209 @@ const {
|
|||
restartBrowserWithEnvironmentVariable,
|
||||
} = require("resource://devtools/client/performance-new/shared/browser.js");
|
||||
|
||||
/** @type {PerformancePref["AboutProfilingHasDeveloperOptions"]} */
|
||||
const ABOUTPROFILING_HAS_DEVELOPER_OPTIONS_PREF =
|
||||
"devtools.performance.aboutprofiling.has-developer-options";
|
||||
|
||||
/**
|
||||
* This function encodes the parameter so that it can be used as an environment
|
||||
* variable value.
|
||||
* Basically it uses single quotes, but replacing any single quote by '"'"':
|
||||
* 1. close the previous single-quoted string,
|
||||
* 2. add a double-quoted string containing only a single quote
|
||||
* 3. start a single-quoted string again.
|
||||
* so that it's properly retained.
|
||||
*
|
||||
* @param {string} value
|
||||
* @returns {string}
|
||||
*/
|
||||
function encodeShellValue(value) {
|
||||
return "'" + value.replaceAll("'", `'"'"'`) + "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import("../../@types/perf").RecordingSettings} RecordingSettings
|
||||
*
|
||||
* @typedef {Object} ButtonStateProps
|
||||
* @property {RecordingSettings} recordingSettings
|
||||
*
|
||||
* @typedef {ButtonStateProps} ButtonProps
|
||||
*
|
||||
* @typedef {Object} ButtonState
|
||||
* @property {boolean} hasDeveloperOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* This component implements the button that triggers the menu that makes it
|
||||
* possible to show more actions.
|
||||
* @extends {React.PureComponent<ButtonProps, ButtonState>}
|
||||
*/
|
||||
class MoreActionsButtonImpl extends PureComponent {
|
||||
state = {
|
||||
hasDeveloperOptions: Services.prefs.getBoolPref(
|
||||
ABOUTPROFILING_HAS_DEVELOPER_OPTIONS_PREF,
|
||||
false
|
||||
),
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
Services.prefs.addObserver(
|
||||
ABOUTPROFILING_HAS_DEVELOPER_OPTIONS_PREF,
|
||||
this.onHasDeveloperOptionsPrefChanges
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
Services.prefs.removeObserver(
|
||||
ABOUTPROFILING_HAS_DEVELOPER_OPTIONS_PREF,
|
||||
this.onHasDeveloperOptionsPrefChanges
|
||||
);
|
||||
}
|
||||
_menuRef = createRef();
|
||||
|
||||
onHasDeveloperOptionsPrefChanges = () => {
|
||||
this.setState({
|
||||
hasDeveloperOptions: Services.prefs.getBoolPref(
|
||||
ABOUTPROFILING_HAS_DEVELOPER_OPTIONS_PREF,
|
||||
false
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* See the part "Showing the menu" in
|
||||
* https://searchfox.org/mozilla-central/rev/4bacdbc8ac088f2ee516daf42c535fab2bc24a04/toolkit/content/widgets/panel-list/README.stories.md
|
||||
* Strangely our React's type doesn't have the `detail` property for
|
||||
* MouseEvent, so we're defining it manually.
|
||||
* @param {React.MouseEvent & { detail: number }} e
|
||||
*/
|
||||
handleClickOrMousedown = e => {
|
||||
// The menu is toggled either for a "mousedown", or for a keyboard enter
|
||||
// (which triggers a "click" event with 0 clicks (detail == 0)).
|
||||
if (this._menuRef.current && (e.type == "mousedown" || e.detail === 0)) {
|
||||
this._menuRef.current.toggle(e.nativeEvent, e.currentTarget);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {Record<string, string>}
|
||||
*/
|
||||
getEnvironmentVariablesForStartupFromRecordingSettings = () => {
|
||||
const { interval, entries, threads, features } =
|
||||
this.props.recordingSettings;
|
||||
return {
|
||||
MOZ_PROFILER_STARTUP: "1",
|
||||
MOZ_PROFILER_STARTUP_INTERVAL: String(interval),
|
||||
MOZ_PROFILER_STARTUP_ENTRIES: String(entries),
|
||||
MOZ_PROFILER_STARTUP_FEATURES: features.join(","),
|
||||
MOZ_PROFILER_STARTUP_FILTERS: threads.join(","),
|
||||
};
|
||||
};
|
||||
|
||||
onRestartWithProfiling = () => {
|
||||
const envVariables =
|
||||
this.getEnvironmentVariablesForStartupFromRecordingSettings();
|
||||
restartBrowserWithEnvironmentVariable(envVariables);
|
||||
};
|
||||
|
||||
onCopyEnvVariables = async () => {
|
||||
const envVariables =
|
||||
this.getEnvironmentVariablesForStartupFromRecordingSettings();
|
||||
const envString = Object.entries(envVariables)
|
||||
.map(([key, value]) => `${key}=${encodeShellValue(value)}`)
|
||||
.join(" ");
|
||||
await navigator.clipboard.writeText(envString);
|
||||
};
|
||||
|
||||
onCopyTestVariables = async () => {
|
||||
const { interval, entries, threads, features } =
|
||||
this.props.recordingSettings;
|
||||
|
||||
const envString =
|
||||
"--gecko-profile" +
|
||||
` --gecko-profile-interval ${interval}` +
|
||||
` --gecko-profile-entries ${entries}` +
|
||||
` --gecko-profile-features ${encodeShellValue(features.join(","))}` +
|
||||
` --gecko-profile-threads ${encodeShellValue(threads.join(","))}`;
|
||||
await navigator.clipboard.writeText(envString);
|
||||
};
|
||||
|
||||
render() {
|
||||
return h(
|
||||
Fragment,
|
||||
null,
|
||||
Localized(
|
||||
{
|
||||
id: "perftools-menu-more-actions-button",
|
||||
attrs: { title: true },
|
||||
},
|
||||
h("moz-button", {
|
||||
iconsrc: "chrome://global/skin/icons/more.svg",
|
||||
"aria-expanded": "false",
|
||||
"aria-haspopup": "menu",
|
||||
onClick: this.handleClickOrMousedown,
|
||||
onMouseDown: this.handleClickOrMousedown,
|
||||
})
|
||||
),
|
||||
h(
|
||||
"panel-list",
|
||||
{ ref: this._menuRef },
|
||||
Localized(
|
||||
{ id: "perftools-menu-more-actions-restart-with-profiling" },
|
||||
h(
|
||||
"panel-item",
|
||||
{ onClick: this.onRestartWithProfiling },
|
||||
"Restart Firefox with startup profiling enabled"
|
||||
)
|
||||
),
|
||||
this.state.hasDeveloperOptions
|
||||
? Localized(
|
||||
{ id: "perftools-menu-more-actions-copy-for-startup" },
|
||||
h(
|
||||
"panel-item",
|
||||
{ onClick: this.onCopyEnvVariables },
|
||||
"Copy environment variables for startup profiling"
|
||||
)
|
||||
)
|
||||
: null,
|
||||
this.state.hasDeveloperOptions
|
||||
? Localized(
|
||||
{ id: "perftools-menu-more-actions-copy-for-perf-tests" },
|
||||
h(
|
||||
"panel-item",
|
||||
{ onClick: this.onCopyTestVariables },
|
||||
"Copy parameters for mach try perf"
|
||||
)
|
||||
)
|
||||
: null
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {StoreState} state
|
||||
* @returns {ButtonStateProps}
|
||||
*/
|
||||
function mapStateToButtonProps(state) {
|
||||
return {
|
||||
recordingSettings: selectors.getRecordingSettings(state),
|
||||
};
|
||||
}
|
||||
const MoreActionsButton = connect(mapStateToButtonProps)(MoreActionsButtonImpl);
|
||||
|
||||
/**
|
||||
* @typedef {import("../../@types/perf").PageContext} PageContext
|
||||
*
|
||||
* @typedef {Object} StateProps
|
||||
* @property {boolean?} isSupportedPlatform
|
||||
* @property {PageContext} pageContext
|
||||
* @property {string | null} promptEnvRestart
|
||||
* @property {(() => void) | undefined} openRemoteDevTools
|
||||
*
|
||||
* @typedef {StateProps} Props
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is the top level component for the about:profiling page. It shares components
|
||||
* with the popup and DevTools page.
|
||||
|
@ -58,16 +250,6 @@ const {
|
|||
* @extends {React.PureComponent<Props>}
|
||||
*/
|
||||
class AboutProfiling extends PureComponent {
|
||||
handleRestart = () => {
|
||||
const { promptEnvRestart } = this.props;
|
||||
if (!promptEnvRestart) {
|
||||
throw new Error(
|
||||
"handleRestart() should only be called when promptEnvRestart exists."
|
||||
);
|
||||
}
|
||||
restartBrowserWithEnvironmentVariable(promptEnvRestart, "1");
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
isSupportedPlatform,
|
||||
|
@ -97,7 +279,11 @@ class AboutProfiling extends PureComponent {
|
|||
{
|
||||
className: "perf-photon-button perf-photon-button-micro",
|
||||
type: "button",
|
||||
onClick: this.handleRestart,
|
||||
onClick: () => {
|
||||
restartBrowserWithEnvironmentVariable({
|
||||
[promptEnvRestart]: "1",
|
||||
});
|
||||
},
|
||||
},
|
||||
Localized({ id: "perftools-button-restart" })
|
||||
)
|
||||
|
@ -121,9 +307,13 @@ class AboutProfiling extends PureComponent {
|
|||
|
||||
div(
|
||||
{ className: "perf-intro" },
|
||||
h1(
|
||||
{ className: "perf-intro-title" },
|
||||
Localized({ id: "perftools-intro-title" })
|
||||
div(
|
||||
{ className: "perf-intro-title-bar" },
|
||||
h1(
|
||||
{ className: "perf-intro-title" },
|
||||
Localized({ id: "perftools-intro-title" })
|
||||
),
|
||||
h(MoreActionsButton)
|
||||
),
|
||||
div(
|
||||
{ className: "perf-intro-row" },
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* @typedef {import("../@types/perf").PreferenceFront} PreferenceFront
|
||||
* @typedef {import("../@types/perf").PerformancePref} PerformancePref
|
||||
* @typedef {import("../@types/perf").RecordingSettings} RecordingSettings
|
||||
* @typedef {import("../@types/perf").RestartBrowserWithEnvironmentVariable} RestartBrowserWithEnvironmentVariable
|
||||
* @typedef {import("../@types/perf").GetActiveBrowserID} GetActiveBrowserID
|
||||
* @typedef {import("../@types/perf").MinimallyTypedGeckoProfile} MinimallyTypedGeckoProfile
|
||||
* @typedef {import("../@types/perf").ProfilerViewMode} ProfilerViewMode
|
||||
|
@ -145,10 +144,12 @@ function sharedLibrariesFromProfile(profile) {
|
|||
/**
|
||||
* Restarts the browser with a given environment variable set to a value.
|
||||
*
|
||||
* @type {RestartBrowserWithEnvironmentVariable}
|
||||
* @param {Record<string, string>} env
|
||||
*/
|
||||
function restartBrowserWithEnvironmentVariable(envName, value) {
|
||||
Services.env.set(envName, value);
|
||||
function restartBrowserWithEnvironmentVariable(env) {
|
||||
for (const [envName, envValue] of Object.entries(env)) {
|
||||
Services.env.set(envName, envValue);
|
||||
}
|
||||
|
||||
Services.startup.quit(
|
||||
Services.startup.eForceQuit | Services.startup.eRestart
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
* @typedef {import("../@types/perf").InitializedValues} InitializedValues
|
||||
* @typedef {import("../@types/perf").PerfFront} PerfFront
|
||||
* @typedef {import("../@types/perf").ReceiveProfile} ReceiveProfile
|
||||
* @typedef {import("../@types/perf").RestartBrowserWithEnvironmentVariable} RestartBrowserWithEnvironmentVariable
|
||||
* @typedef {import("../@types/perf").PageContext} PageContext
|
||||
* @typedef {import("../@types/perf").Presets} Presets
|
||||
*/
|
||||
|
|
|
@ -26,6 +26,8 @@ support-files = [
|
|||
|
||||
["browser_aboutprofiling-interval.js"]
|
||||
|
||||
["browser_aboutprofiling-more-actions-menu.js"]
|
||||
|
||||
["browser_aboutprofiling-presets-custom.js"]
|
||||
|
||||
["browser_aboutprofiling-presets.js"]
|
||||
|
|
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