diff --git a/.cargo/config.toml.in b/.cargo/config.toml.in index 73a4d695b6f62..49b347ca03516 100644 --- a/.cargo/config.toml.in +++ b/.cargo/config.toml.in @@ -75,11 +75,6 @@ git = "https://github.com/jfkthame/mapped_hyph.git" rev = "eff105f6ad7ec9b79816cfc1985a28e5340ad14b" replace-with = "vendored-sources" -[source."git+https://github.com/martinthomson/ohttp.git?rev=bf6a983845cc0b540effb3a615e92d914dfcfd0b"] -git = "https://github.com/martinthomson/ohttp.git" -rev = "bf6a983845cc0b540effb3a615e92d914dfcfd0b" -replace-with = "vendored-sources" - [source."git+https://github.com/mozilla/application-services?rev=0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2"] git = "https://github.com/mozilla/application-services" rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" diff --git a/Cargo.lock b/Cargo.lock index 01e14cea83341..9b98c95b8a18d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aa-stroke" @@ -425,10 +425,11 @@ dependencies = [ [[package]] name = "bhttp" -version = "0.6.1" -source = "git+https://github.com/martinthomson/ohttp.git?rev=bf6a983845cc0b540effb3a615e92d914dfcfd0b#bf6a983845cc0b540effb3a615e92d914dfcfd0b" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d305a54bcb99974213b4c78a486c34091e83c5d6d6572f7f4331c904ea9d127" dependencies = [ - "thiserror 1.999.999", + "thiserror 2.0.12", ] [[package]] @@ -5265,8 +5266,9 @@ dependencies = [ [[package]] name = "ohttp" -version = "0.6.1" -source = "git+https://github.com/martinthomson/ohttp.git?rev=bf6a983845cc0b540effb3a615e92d914dfcfd0b#bf6a983845cc0b540effb3a615e92d914dfcfd0b" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a03aaaf57495c75ce66aee6a7c3b21abf046c9d4cca3d45b22cdbf0de1bfba" dependencies = [ "bindgen 0.72.0", "byteorder", @@ -5275,8 +5277,8 @@ dependencies = [ "mozbuild", "serde", "serde_derive", - "thiserror 1.999.999", - "toml 0.5.999", + "thiserror 2.0.12", + "toml 0.9.8", ] [[package]] @@ -6730,6 +6732,8 @@ dependencies = [ "smallvec", "static_assertions", "static_prefs", + "strum", + "strum_macros", "style_derive", "style_traits", "thin-vec", diff --git a/Cargo.toml b/Cargo.toml index 5c3f6fcaf9ef8..5bc4763bdd243 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -266,12 +266,6 @@ objc = { git = "https://github.com/glandium/rust-objc", rev = "4de89f5aa9851ceca # allocator-api2 + f95e3419ce41883904fcb2279b52aa35b5f04d76 + fdd92751afa7ce34408b677004b429d597e72c90 allocator-api2 = { git = "https://github.com/glandium/allocator-api2", rev = "ad5f3d56a5a4519eff52af4ff85293431466ef5c" } -# patch ohttp - app-services relies on a version yet to be released. However, even if a new version was released it -# will have bumped the `toml` crate to a version which would cause dupes here. We should remove this and revert back -# to a published version when we can upgrade both a-s and m-c to the same version. -ohttp = { git = "https://github.com/martinthomson/ohttp.git", rev = "bf6a983845cc0b540effb3a615e92d914dfcfd0b" } -bhttp = { git = "https://github.com/martinthomson/ohttp.git", rev = "bf6a983845cc0b540effb3a615e92d914dfcfd0b" } - # application-services overrides to make updating them all simpler. context_id = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } error-support = { git = "https://github.com/mozilla/application-services", rev = "0376c542e4a31cde8d33dd0e8da17dcfbc6c58b2" } diff --git a/accessible/tests/browser/windows/a11y_setup.py b/accessible/tests/browser/windows/a11y_setup.py index 40e306152b83e..719b06fa9b983 100644 --- a/accessible/tests/browser/windows/a11y_setup.py +++ b/accessible/tests/browser/windows/a11y_setup.py @@ -2,8 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -"""Python environment for Windows a11y browser tests. -""" +"""Python environment for Windows a11y browser tests.""" import ctypes import os diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index c2665c2177b36..fe944ec1c17c0 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -681,7 +681,7 @@ pref("browser.urlbar.merino.ohttpConfigURL", ""); pref("browser.urlbar.merino.ohttpRelayURL", ""); // OHTTP hpke for DAP -pref("dap.ohttp.hpke", "https://dap-09-3.api.divviup.org/ohttp-configs"); +pref("dap.ohttp.hpke", "gAAgJSO22Y3HKzRSese15JtQVuuFfOIcTrZ56lQ5kDQwS0oABAABAAE"); // OHTTP relay URL for DAP pref("dap.ohttp.relayURL", "https://mozilla-ohttp-dap.mozilla.fastly-edge.com/"); @@ -2271,6 +2271,7 @@ pref("browser.aiwindow.memoriesLogLevel", "Warn"); pref("browser.aiwindow.firstrun.autoAdvanceMS", 3000); pref("browser.aiwindow.firstrun.modelChoice", ""); pref("browser.aiwindow.model", "qwen3-235b-a22b-instruct-2507-maas"); +pref("browser.aiwindow.preferences.enabled", false); // Block insecure active content on https pages pref("security.mixed_content.block_active_content", true); diff --git a/browser/base/content/browser-trustPanel.js b/browser/base/content/browser-trustPanel.js index e026c7143ba44..1eca67d0df908 100644 --- a/browser/base/content/browser-trustPanel.js +++ b/browser/base/content/browser-trustPanel.js @@ -321,17 +321,19 @@ class TrustPanel { icon.classList.add("inactive"); } + icon.setAttribute("tooltiptext", this.#tooltipText()); icon.classList.toggle("chickletShown", this.#isSecureInternalUI); } async #updatePopup() { - this.#host = BrowserUtils.formatURIForDisplay(this.#uri, { - onlyBaseDomain: true, - }); - this.#popup.setAttribute( - "connection", - this.#isSecurePage() ? "secure" : "not-secure" - ); + if (this.#uri) { + this.#host = BrowserUtils.formatURIForDisplay(this.#uri, { + onlyBaseDomain: true, + }); + } else { + this.#host = ""; + } + this.#popup.setAttribute("connection", this.#connectionState()); this.#popup.setAttribute( "tracking-protection", this.#trackingProtectionStatus() @@ -863,7 +865,7 @@ class TrustPanel { if (this.#isEV) { let iData = this.#getIdentityData(); owner = iData.subjectOrg; - verifier = this._identityIconLabel.tooltipText; + verifier = this.#tooltipText(); // Build an appropriate supplemental block out of whatever location data we have if (iData.city) { diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml index 557d6a2462de5..3ca8c94c0f08b 100644 --- a/browser/base/content/navigator-toolbox.inc.xhtml +++ b/browser/base/content/navigator-toolbox.inc.xhtml @@ -339,11 +339,12 @@ data-l10n-id="urlbar-search-mode-indicator-close"/> - + - + diff --git a/browser/base/content/test/about/browser.toml b/browser/base/content/test/about/browser.toml index b5a5a20c82aed..11159a2d0a0ef 100644 --- a/browser/base/content/test/about/browser.toml +++ b/browser/base/content/test/about/browser.toml @@ -137,3 +137,5 @@ skip-if = [ ["browser_bug435325.js"] ["browser_bug633691.js"] + +["browser_bug2008464.js"] diff --git a/browser/base/content/test/about/browser_bug2008464.js b/browser/base/content/test/about/browser_bug2008464.js new file mode 100644 index 0000000000000..a1231660b1750 --- /dev/null +++ b/browser/base/content/test/about/browser_bug2008464.js @@ -0,0 +1,58 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +"use strict"; + +add_task(async function testHostnameDisplayedCorrectly() { + const { HttpServer } = ChromeUtils.importESModule( + "resource://testing-common/httpd.sys.mjs" + ); + + const server = new HttpServer(); + registerCleanupFunction(() => new Promise(resolve => server.stop(resolve))); + server.registerPathHandler("/auth", (request, response) => { + response.setStatusLine(request.httpVersion, 401, "Unauthorized"); + response.setHeader("WWW-Authenticate", 'Basic realm="test"', false); + }); + server.start(-1); + const port = server.identity.primaryPort; + server.identity.add("http", "localhost", port); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.security.https_first", false], + ["network.http.basic_http_auth.enabled", false], + ["browser.http.blank_page_with_error_response.enabled", true], + ["security.certerrors.felt-privacy-v1", true], + ], + }); + registerCleanupFunction(() => SpecialPowers.popPrefEnv()); + + const url = `http://localhost:${port}/auth`; + info(`Checking URL (${url}) against displayed hostname.`); + await BrowserTestUtils.withNewTab( + { gBrowser, url, waitForLoad: false }, + async browser => { + await BrowserTestUtils.waitForErrorPage(browser); + await SpecialPowers.spawn(browser, [port], async p => { + const netErrorCard = await ContentTaskUtils.waitForCondition( + () => + content.document.querySelector("net-error-card")?.wrappedJSObject + ); + await netErrorCard.getUpdateComplete(); + + Assert.equal( + netErrorCard.errorInfo.errorCodeString, + "NS_ERROR_BASIC_HTTP_AUTH_DISABLED", + "Shows HTTP auth disabled error" + ); + Assert.equal( + netErrorCard.hostname, + `localhost:${p}`, + "Hostname includes the port once" + ); + }); + } + ); +}); diff --git a/browser/base/content/test/static/browser_all_files_referenced.js b/browser/base/content/test/static/browser_all_files_referenced.js index eb5cc9e7fc7d8..afc9bd3fb4470 100644 --- a/browser/base/content/test/static/browser_all_files_referenced.js +++ b/browser/base/content/test/static/browser_all_files_referenced.js @@ -328,14 +328,11 @@ var allowlist = [ { file: "resource://app/modules/backup/CookiesBackupResource.sys.mjs", }, + // Bug 2000945 - Move query intent detection to AI-window r?mardak (backed out due to unused file) { file: "moz-src:///browser/components/aiwindow/models/IntentClassifier.sys.mjs", }, - // Bug 2004888 - [FirstRun] Create Firstrun.html opening firstrun welcome screen - { - file: "chrome://browser/content/aiwindow/firstrun.html", - }, // Bug 2005768 - Insights scheduler for generation from history // Bug 2007939 - Rename "insights" to "memories" { @@ -350,6 +347,13 @@ var allowlist = [ { file: "moz-src:///browser/components/aiwindow/models/ConversationSuggestions.sys.mjs", }, + // Bug 1996315: QR code generation modules + { + file: "moz-src:///browser/components/qrcode/QRCodeGenerator.sys.mjs", + }, + { + file: "moz-src:///browser/components/qrcode/QRCodeWorker.sys.mjs", + }, ]; if (AppConstants.NIGHTLY_BUILD) { diff --git a/browser/components/aboutwelcome/content-src/components/EmbeddedMigrationWizard.jsx b/browser/components/aboutwelcome/content-src/components/EmbeddedMigrationWizard.jsx index ca5e9b2d8bb18..6aa0f51f20962 100644 --- a/browser/components/aboutwelcome/content-src/components/EmbeddedMigrationWizard.jsx +++ b/browser/components/aboutwelcome/content-src/components/EmbeddedMigrationWizard.jsx @@ -4,9 +4,36 @@ import React, { useEffect, useRef } from "react"; +/** + * Embeds a migration wizard component within About:Welcome, + * and passes configuration options from content to the migration-wizard element + * + * @param {function} handleAction - The action handler function that processes migration events + * @param {object} content - The content object that contains tiles configuration + * @param {object} content.tiles - The tiles configuration object + * @param {object} content.tiles.migration_wizard_options - Configuration options for the migration wizard + * All options, including migration_wizard_options itself, are optional and have fallback values: + * - {boolean} force_show_import_all - Whether to force show import all option + * - {string} option_expander_title_string - Title string for the option expander + * - {boolean} hide_option_expander_subtitle - Whether or not to hide the option expander subtitle + * - {string} data_import_complete_success_string - Success message string after import completion + * - {string} selection_header_string - Header string for the selection section + * - {string} selection_subheader_string - Subheader string for the selection section + * - {boolean} hide_select_all - Whether to hide the select all option + * - {string} checkbox_margin_inline - Inline margin for checkboxes + * - {string} checkbox_margin_block - Block margin for checkboxes + * - {string} import_button_string - Text string for the import button + * - {string} import_button_class - CSS class for the import button + * - {string} header_font_size - Font size for the header + * - {string} header_font_weight - Font weight for the header + * - {string} header_margin_block - Block margin for the header + * - {string} subheader_font_size - Font size for the subheader + * - {string} subheader_font_weight - Font weight for the subheader + * - {string} subheader_margin_block - Block margin for the subheader + */ export const EmbeddedMigrationWizard = ({ handleAction, content }) => { const ref = useRef(); - const options = content.migration_wizard_options; + const options = content.tiles?.migration_wizard_options; useEffect(() => { const handleBeginMigration = () => { handleAction({ @@ -44,7 +71,7 @@ export const EmbeddedMigrationWizard = ({ handleAction, content }) => { data-import-complete-success-string={ options?.data_import_complete_success_string || "" } - selection-header-string={options?.selection_header_string} + selection-header-string={options?.selection_header_string || ""} selection-subheader-string={options?.selection_subheader_string || ""} hide-select-all={options?.hide_select_all || false} checkbox-margin-inline={options?.checkbox_margin_inline || ""} diff --git a/browser/components/aboutwelcome/content/aboutwelcome.bundle.js b/browser/components/aboutwelcome/content/aboutwelcome.bundle.js index a36cda5d8268a..dc7e71b886e79 100644 --- a/browser/components/aboutwelcome/content/aboutwelcome.bundle.js +++ b/browser/components/aboutwelcome/content/aboutwelcome.bundle.js @@ -3344,12 +3344,40 @@ __webpack_require__.r(__webpack_exports__); * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Embeds a migration wizard component within About:Welcome, + * and passes configuration options from content to the migration-wizard element + * + * @param {function} handleAction - The action handler function that processes migration events + * @param {object} content - The content object that contains tiles configuration + * @param {object} content.tiles - The tiles configuration object + * @param {object} content.tiles.migration_wizard_options - Configuration options for the migration wizard + * All options, including migration_wizard_options itself, are optional and have fallback values: + * - {boolean} force_show_import_all - Whether to force show import all option + * - {string} option_expander_title_string - Title string for the option expander + * - {boolean} hide_option_expander_subtitle - Whether or not to hide the option expander subtitle + * - {string} data_import_complete_success_string - Success message string after import completion + * - {string} selection_header_string - Header string for the selection section + * - {string} selection_subheader_string - Subheader string for the selection section + * - {boolean} hide_select_all - Whether to hide the select all option + * - {string} checkbox_margin_inline - Inline margin for checkboxes + * - {string} checkbox_margin_block - Block margin for checkboxes + * - {string} import_button_string - Text string for the import button + * - {string} import_button_class - CSS class for the import button + * - {string} header_font_size - Font size for the header + * - {string} header_font_weight - Font weight for the header + * - {string} header_margin_block - Block margin for the header + * - {string} subheader_font_size - Font size for the subheader + * - {string} subheader_font_weight - Font weight for the subheader + * - {string} subheader_margin_block - Block margin for the subheader + */ const EmbeddedMigrationWizard = ({ handleAction, content }) => { const ref = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(); - const options = content.migration_wizard_options; + const options = content.tiles?.migration_wizard_options; (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { const handleBeginMigration = () => { handleAction({ @@ -3384,7 +3412,7 @@ const EmbeddedMigrationWizard = ({ "option-expander-title-string": options?.option_expander_title_string || "", "hide-option-expander-subtitle": options?.hide_option_expander_subtitle || false, "data-import-complete-success-string": options?.data_import_complete_success_string || "", - "selection-header-string": options?.selection_header_string, + "selection-header-string": options?.selection_header_string || "", "selection-subheader-string": options?.selection_subheader_string || "", "hide-select-all": options?.hide_select_all || false, "checkbox-margin-inline": options?.checkbox_margin_inline || "", diff --git a/browser/components/aboutwelcome/tests/unit/ContentTiles.test.jsx b/browser/components/aboutwelcome/tests/unit/ContentTiles.test.jsx index d5a9f47efce9e..79866f1612da1 100644 --- a/browser/components/aboutwelcome/tests/unit/ContentTiles.test.jsx +++ b/browser/components/aboutwelcome/tests/unit/ContentTiles.test.jsx @@ -4,6 +4,7 @@ import { ContentTiles } from "content-src/components/ContentTiles"; import { ActionChecklist } from "content-src/components/ActionChecklist"; import { MobileDownloads } from "content-src/components/MobileDownloads"; import { EmbeddedBackupRestore } from "content-src/components/EmbeddedBackupRestore"; +import { EmbeddedMigrationWizard } from "content-src/components/EmbeddedMigrationWizard"; import { AboutWelcomeUtils } from "content-src/lib/aboutwelcome-utils.mjs"; import { GlobalOverrider } from "asrouter/tests/unit/utils"; @@ -1062,4 +1063,63 @@ describe("ContentTiles component", () => { mounted.unmount(); }); + + it("passes migration_wizard_options properties to migration-wizard element", () => { + const MIGRATION_WIZARD_TILE = { + type: "migration-wizard", + migration_wizard_options: { + force_show_import_all: true, + option_expander_title_string: "Custom title", + hide_option_expander_subtitle: true, + hide_select_all: true, + }, + }; + + const content = { + tiles: [MIGRATION_WIZARD_TILE], + }; + + const mountedWrapper = mount( + + ); + + const embeddedMigrationWizard = mountedWrapper.find( + EmbeddedMigrationWizard + ); + assert.ok( + embeddedMigrationWizard.exists(), + "EmbeddedMigrationWizard rendered" + ); + + const migrationWizardEl = mountedWrapper.find("migration-wizard"); + assert.ok(migrationWizardEl.exists(), "migration-wizard element rendered"); + + assert.equal( + migrationWizardEl.prop("force-show-import-all"), + true, + "force-show-import-all is set" + ); + assert.equal( + migrationWizardEl.prop("option-expander-title-string"), + "Custom title", + "option-expander-title-string is set" + ); + assert.equal( + migrationWizardEl.prop("hide-option-expander-subtitle"), + true, + "hide-option-expander-subtitle is set" + ); + assert.equal( + migrationWizardEl.prop("hide-select-all"), + true, + "hide-select-all is set" + ); + + mountedWrapper.unmount(); + }); }); diff --git a/browser/components/aiwindow/models/Chat.sys.mjs b/browser/components/aiwindow/models/Chat.sys.mjs index 258c2c390d77c..91d984d1f0d0d 100644 --- a/browser/components/aiwindow/models/Chat.sys.mjs +++ b/browser/components/aiwindow/models/Chat.sys.mjs @@ -5,7 +5,10 @@ */ import { ToolRoleOpts } from "moz-src:///browser/components/aiwindow/ui/modules/ChatMessage.sys.mjs"; -import { openAIEngine } from "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs"; +import { + MODEL_FEATURES, + openAIEngine, +} from "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs"; import { toolsConfig, getOpenTabs, @@ -39,11 +42,14 @@ export const Chat = { // @todo Bug 2007046 // Update this with correct model id + // Move engineInstance initialization up to access engineInstance.model const modelId = "qwen3-235b-a22b-instruct-2507-maas"; const toolRoleOpts = new ToolRoleOpts(modelId); const currentTurn = conversation.currentTurnIndex(); - const engineInstance = await openAIEngine.build(); + const engineInstance = await openAIEngine.build(MODEL_FEATURES.CHAT); + const config = engineInstance.getConfig(engineInstance.feature); + const inferenceParams = config?.parameters || {}; // Helper to run the model once (streaming) on current convo const streamModelResponse = () => @@ -53,6 +59,7 @@ export const Chat = { tool_choice: "auto", tools: toolsConfig, args: conversation.getMessagesInOpenAiFormat(), + ...inferenceParams, }); // Keep calling until the model finishes without requesting tools diff --git a/browser/components/aiwindow/models/ConversationSuggestions.sys.mjs b/browser/components/aiwindow/models/ConversationSuggestions.sys.mjs index efdaff81d675f..ebe321a9b6269 100644 --- a/browser/components/aiwindow/models/ConversationSuggestions.sys.mjs +++ b/browser/components/aiwindow/models/ConversationSuggestions.sys.mjs @@ -9,14 +9,9 @@ import { openAIEngine, renderPrompt, + MODEL_FEATURES, } from "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs"; -import { - conversationStarterPrompt, - conversationFollowupPrompt, - conversationMemoriesPrompt, -} from "moz-src:///browser/components/aiwindow/models/prompts/ConversationSuggestionsPrompts.sys.mjs"; - import { MESSAGE_ROLE } from "moz-src:///browser/components/aiwindow/ui/modules/ChatStore.sys.mjs"; import { MemoriesManager } from "moz-src:///browser/components/aiwindow/models/memories/MemoriesManager.sys.mjs"; @@ -52,9 +47,10 @@ export function trimConversation(messages, maxMessages = 15) { * Helper to add memories to base prompt if applicable * * @param {string} base - base prompt + * @param {string} conversationMemoriesPrompt - the memories prompt template * @returns {Promise} - prompt with memories added if applicable */ -export async function addMemoriesToPrompt(base) { +export async function addMemoriesToPrompt(base, conversationMemoriesPrompt) { let memorySummaries = await MemoriesGetterForSuggestionPrompts.getMemorySummariesForPrompt( MAX_NUM_MEMORIES @@ -196,19 +192,39 @@ export async function generateConversationStartersSidebar( openedTabs = "No tabs available"; } + // Build engine and load prompt + const engineInstance = await openAIEngine.build( + MODEL_FEATURES.CONVERSATION_SUGGESTIONS_SIDEBAR_STARTER + ); + + const conversationStarterPrompt = await engineInstance.loadPrompt( + MODEL_FEATURES.CONVERSATION_SUGGESTIONS_SIDEBAR_STARTER + ); + + const assistantLimitations = await engineInstance.loadPrompt( + MODEL_FEATURES.CONVERSATION_SUGGESTIONS_ASSISTANT_LIMITATIONS + ); + // Base template const base = await renderPrompt(conversationStarterPrompt, { current_tab: currentTab, open_tabs: openedTabs, n: String(n), date: today, + assistant_limitations: assistantLimitations, }); - let filled = useMemories - ? await addMemoriesToPrompt(base, useMemories) - : base; + let filled = base; + if (useMemories) { + const conversationMemoriesPrompt = await engineInstance.loadPrompt( + MODEL_FEATURES.CONVERSATION_SUGGESTIONS_MEMORIES + ); + filled = await addMemoriesToPrompt(base, conversationMemoriesPrompt); + } - const engineInstance = await openAIEngine.build("starter"); + // Get config for inference parameters + const config = engineInstance.getConfig(engineInstance.feature); + const inferenceParams = config?.parameters || {}; const result = await engineInstance.run({ messages: [ @@ -218,6 +234,7 @@ export async function generateConversationStartersSidebar( }, { role: "user", content: filled }, ], + ...inferenceParams, }); const prompts = cleanInferenceOutput(result); @@ -254,18 +271,41 @@ export async function generateFollowupPrompts( currentTab && Object.keys(currentTab).length ? formatJson({ title: currentTab.title, url: currentTab.url }) : "No tab"; + + // Build engine and load prompt + const engineInstance = await openAIEngine.build( + MODEL_FEATURES.CONVERSATION_SUGGESTIONS_FOLLOWUP + ); + + const conversationFollowupPrompt = await engineInstance.loadPrompt( + MODEL_FEATURES.CONVERSATION_SUGGESTIONS_FOLLOWUP + ); + + const assistantLimitationsFollowup = await engineInstance.loadPrompt( + MODEL_FEATURES.CONVERSATION_SUGGESTIONS_ASSISTANT_LIMITATIONS + ); + const base = await renderPrompt(conversationFollowupPrompt, { current_tab: currentTabStr, conversation: formatJson(convo), n: String(n), date: today, + assistant_limitations: assistantLimitationsFollowup, }); - let filled = useMemories - ? await addMemoriesToPrompt(base, useMemories) - : base; + let filled = base; + if (useMemories) { + const conversationMemoriesPrompt = await engineInstance.loadPrompt( + MODEL_FEATURES.CONVERSATION_SUGGESTIONS_MEMORIES + ); + filled = await addMemoriesToPrompt(base, conversationMemoriesPrompt); + } - const engineInstance = await openAIEngine.build("followup"); + // Get config for inference parameters + const config = engineInstance.getConfig( + MODEL_FEATURES.CONVERSATION_SUGGESTIONS_FOLLOWUP + ); + const inferenceParams = config?.parameters || {}; const result = await engineInstance.run({ messages: [ @@ -275,6 +315,7 @@ export async function generateFollowupPrompts( }, { role: "user", content: filled }, ], + ...inferenceParams, }); const prompts = cleanInferenceOutput(result); diff --git a/browser/components/aiwindow/models/TitleGeneration.sys.mjs b/browser/components/aiwindow/models/TitleGeneration.sys.mjs index f253d772b4d74..3dc54049348cd 100644 --- a/browser/components/aiwindow/models/TitleGeneration.sys.mjs +++ b/browser/components/aiwindow/models/TitleGeneration.sys.mjs @@ -7,8 +7,8 @@ import { openAIEngine, renderPrompt, + MODEL_FEATURES, } from "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs"; -import { titleGenerationPrompt } from "moz-src:///browser/components/aiwindow/models/prompts/TitleGenerationPrompts.sys.mjs"; /** * Generate a default title from the first four words of a message. @@ -43,13 +43,14 @@ function generateDefaultTitle(message) { */ export async function generateChatTitle(message, current_tab) { try { - // Build the OpenAI engines - const engine = await openAIEngine.build(); + // Build the OpenAI engine + const engine = await openAIEngine.build(MODEL_FEATURES.TITLE_GENERATION); const tabInfo = current_tab || { url: "", title: "", description: "" }; - // Render the prompt with actual values - const systemPrompt = await renderPrompt(titleGenerationPrompt, { + // Load and render the prompt with actual values + const rawPrompt = await engine.loadPrompt(MODEL_FEATURES.TITLE_GENERATION); + const systemPrompt = await renderPrompt(rawPrompt, { current_tab: JSON.stringify(tabInfo), }); @@ -59,10 +60,15 @@ export async function generateChatTitle(message, current_tab) { { role: "user", content: message }, ]; + // Get config for inference parameters if exists + const config = engine.getConfig(engine.feature); + const inferenceParams = config?.parameters || {}; + // Call the LLM const response = await engine.run({ messages, fxAccountToken: await openAIEngine.getFxAccountToken(), + ...inferenceParams, }); // Extract the generated title from the response diff --git a/browser/components/aiwindow/models/Utils.sys.mjs b/browser/components/aiwindow/models/Utils.sys.mjs index fd7d4d2c5082c..d4747e23e8337 100644 --- a/browser/components/aiwindow/models/Utils.sys.mjs +++ b/browser/components/aiwindow/models/Utils.sys.mjs @@ -16,6 +16,180 @@ import { OAUTH_CLIENT_ID, SCOPE_PROFILE, } from "resource://gre/modules/FxAccountsCommon.sys.mjs"; +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = XPCOMUtils.declareLazy({ + RemoteSettings: "resource://services-settings/remote-settings.sys.mjs", +}); + +const MODEL_PREF = "browser.aiwindow.model"; + +/** + * Default engine ID used for all AI Window features + */ +export const DEFAULT_ENGINE_ID = "smart-openai"; + +/** + * Service types for different AI Window features + */ +export const SERVICE_TYPES = Object.freeze({ + AI: "ai", + MEMORIES: "memories", +}); + +/** + * Observer for model preference changes. + * Invalidates the Remote Settings client cache when user changes their model preference. + */ +const modelPrefObserver = { + observe(_subject, topic, data) { + if (topic === "nsPref:changed" && data === MODEL_PREF) { + console.warn( + "Model preference changed, invalidating Remote Settings cache" + ); + openAIEngine._remoteClient = null; + } + }, +}; +Services.prefs.addObserver(MODEL_PREF, modelPrefObserver); + +/** + * Feature identifiers for AI Window model, configurations and prompts. + * These are used to look up model configs, prompts, and inference parameters + * from Remote Settings or local defaults. + */ +export const MODEL_FEATURES = Object.freeze({ + CHAT: "chat", + TITLE_GENERATION: "title-generation", + CONVERSATION_SUGGESTIONS_SIDEBAR_STARTER: + "conversation-suggestions-sidebar-starter", + CONVERSATION_SUGGESTIONS_FOLLOWUP: "conversation-suggestions-followup", + CONVERSATION_SUGGESTIONS_ASSISTANT_LIMITATIONS: + "conversation-suggestions-assistant-limitations", + CONVERSATION_SUGGESTIONS_MEMORIES: "conversation-suggestions-memories", + // TODO: update with actual memories prompts identifiers + MEMORIES: "memories", +}); + +/** + * Default model IDs for each feature. + * These are Mozilla's recommended models, used when user hasn't configured + * custom settings or when remote setting retrieval fails. + */ +export const DEFAULT_MODEL = Object.freeze({ + [MODEL_FEATURES.CHAT]: "qwen3-235b-a22b-instruct-2507-maas", + [MODEL_FEATURES.TITLE_GENERATION]: "qwen3-235b-a22b-instruct-2507-maas", + [MODEL_FEATURES.CONVERSATION_SUGGESTIONS_SIDEBAR_STARTER]: + "qwen3-235b-a22b-instruct-2507-maas", + [MODEL_FEATURES.CONVERSATION_SUGGESTIONS_FOLLOWUP]: + "qwen3-235b-a22b-instruct-2507-maas", + [MODEL_FEATURES.CONVERSATION_SUGGESTIONS_ASSISTANT_LIMITATIONS]: + "qwen3-235b-a22b-instruct-2507-maas", + [MODEL_FEATURES.CONVERSATION_SUGGESTIONS_INSIGHTS]: + "qwen3-235b-a22b-instruct-2507-maas", + // TODO: update with actual memories default model + [MODEL_FEATURES.MEMORIES]: "qwen3-235b-a22b-instruct-2507-maas", +}); + +/** + * Major version compatibility requirements for each feature. + * When incrementing a feature's major version: + * - Update this constant + * - Ensure Remote Settings has configs for the new major version + * - Old clients will continue using old major version + */ +export const FEATURE_MAJOR_VERSIONS = Object.freeze({ + [MODEL_FEATURES.CHAT]: 1, + [MODEL_FEATURES.TITLE_GENERATION]: 1, + [MODEL_FEATURES.CONVERSATION_SUGGESTIONS_SIDEBAR_STARTER]: 1, + [MODEL_FEATURES.CONVERSATION_SUGGESTIONS_FOLLOWUP]: 1, + [MODEL_FEATURES.CONVERSATION_SUGGESTIONS_ASSISTANT_LIMITATIONS]: 1, + [MODEL_FEATURES.CONVERSATION_SUGGESTIONS_INSIGHTS]: 1, + // TODO: add major version for memories prompts +}); + +/** + * Remote Settings configuration record structure + * + * @typedef {object} RemoteSettingsConfig + * @property {string} feature - Feature identifier + * @property {string} model - Model identifier for LLM inference + * @property {string} prompts - Prompt template content + * @property {string} version - Version string in "v{major}.{minor}" format + * @property {boolean} [is_default] - Whether this is the default config for the feature + * @property {object} [parameters] - Optional inference parameters (e.g., temperature) + * @property {string[]} [additional_components] - Optional list of dependent feature configs + */ + +/** + * Parses a version string in the format "v{major}.{minor}". + * + * @param {string} versionString - Version string to parse (e.g., "v1.2") + * @returns {object|null} Parsed version with major and minor numbers, or null if invalid + */ +function parseVersion(versionString) { + const match = /^v(\d+)\.(\d+)$/.exec(versionString || ""); + if (!match) { + return null; + } + return { + major: Number(match[1]), + minor: Number(match[2]), + original: versionString, + }; +} + +/** + * Selects the main configuration for a feature based on version and model preferences. + * + * Remote Settings maintains only the latest minor version for each (feature, model, major_version) combination. + * + * Selection logic: + * 1. Filter to configs matching the required major version + * 2. If user has model preference, find that model's config + * 3. Otherwise, find the default config (is_default: true) + * + * @param {Array} featureConfigs - All configs for the feature from Remote Settings + * @param {object} options - Selection options + * @param {number} options.majorVersion - Required major version for the feature + * @param {string} options.userModel - User's preferred model (empty string if none) + * @returns {object|null} Selected config or null if no match + */ +function selectMainConfig(featureConfigs, { majorVersion, userModel }) { + // Filter to configs matching the required major version + const sameMajor = featureConfigs.filter(config => { + const parsed = parseVersion(config.version); + return parsed && parsed.major === majorVersion; + }); + + if (sameMajor.length === 0) { + return null; + } + + // If user specified a model preference, find that model's config + if (userModel) { + const userModelConfig = sameMajor.find( + config => config.model === userModel + ); + if (userModelConfig) { + return userModelConfig; + } + // User's model not found in this major version - fall through to defaults + console.warn( + `User model "${userModel}" not found for major version ${majorVersion}, using default` + ); + } + + // No user model pref OR user's model not found: use default + const defaultConfig = sameMajor.find(config => config.is_default === true); + if (defaultConfig) { + return defaultConfig; + } + + // No default found - this shouldn't happen with proper Remote Settings data + console.warn(`No default config found for major version ${majorVersion}`); + return null; +} /** * openAIEngine class @@ -29,18 +203,263 @@ export class openAIEngine { static _createEngine = createEngine; /** - * Returns an OpenAIEngine instance with the specified engine and service types + * The Remote Settings collection name for AI window prompt configurations + */ + static RS_AI_WINDOW_COLLECTION = "ai-window-prompts"; + + /** + * Cached Remote Settings client + * Cache is invalidated when user changes MODEL_PREF pref via modelPrefObserver * - * @param {string} engineId The identifier for the engine instance - * @param {string} serviceType The type of message to be sent ("ai", "memories", "s2s") - * @returns {Promise} The OpenAIEngine instance + * @type {RemoteSettingsClient | null} + */ + static _remoteClient = null; + + /** + * Configuration map: { featureName: configObject } + * + * @type {object | null} + */ + #configs = null; + + /** + * Main feature name + * + * @type {string | null} */ - static async build(engineId = "smart-openai", serviceType = "ai") { + feature = null; + + /** + * Resolved model name for LLM inference + * + * @type {string | null} + */ + model = null; + + /** + * Gets the Remote Settings client for AI window configurations. + * + * @returns {RemoteSettingsClient} + */ + static getRemoteClient() { + if (openAIEngine._remoteClient) { + return openAIEngine._remoteClient; + } + + const client = lazy.RemoteSettings(openAIEngine.RS_AI_WINDOW_COLLECTION, { + bucketName: "main", + }); + + openAIEngine._remoteClient = client; + return client; + } + + /** + * Applies default configuration fallback when Remote Settings selection fails + * + * @param {string} feature - The feature identifier + * @private + */ + _applyDefaultConfig(feature) { + this.feature = feature; + this.model = DEFAULT_MODEL[feature]; + this.#configs = {}; + } + + /** + * Loads configuration from Remote Settings with version-aware selection. + * + * Selection logic: + * 1. Filters configs by feature and major version compatibility + * 2. If user has model preference, finds latest minor for that model + * 3. Otherwise, finds latest minor among default configs + * 4. Falls back to latest minor overall if no defaults + * 5. Falls back to local defaults if no matching major version + * + * @param {string} feature - The feature identifier from MODEL_FEATURES + * @returns {Promise} + * Sets this.feature to the feature name + * Sets this.model to the selected model ID + * Sets this.#configs to contain feature's and additional_components' configs + */ + async loadConfig(feature) { + const client = openAIEngine.getRemoteClient(); + const allRecords = await client.get(); + + // Filter to configs for this feature + const featureConfigs = allRecords.filter( + record => record.feature === feature + ); + + // Fallback to default if no remote settings records for given feature + if (!featureConfigs.length) { + console.warn( + `No Remote Settings records found for feature: ${feature}, using default` + ); + this._applyDefaultConfig(feature); + return; + } + + const majorVersion = FEATURE_MAJOR_VERSIONS[feature]; + const userModel = Services.prefs.getStringPref(MODEL_PREF, ""); + + // Find matching config with version and provided userModel pref + const mainConfig = selectMainConfig(featureConfigs, { + majorVersion, + userModel, + }); + + if (!mainConfig) { + console.warn( + `No matching model config found for feature: ${feature} with major version ${majorVersion}, using default` + ); + this._applyDefaultConfig(feature); + return; + } + + // Store the selected configuration + this.feature = feature; + this.model = mainConfig.model; + + // Build configsMap for looking up additional_components + const configsMap = new Map(allRecords.map(r => [r.feature, r])); + + // Build configs map: { featureName: configObject } + this.#configs = {}; + this.#configs[feature] = mainConfig; + + // Add additional_components if exists + // This field lists what other remote settings configs are needed + // as dependency to the current feature. + if (mainConfig.additional_components) { + for (const componentFeature of mainConfig.additional_components) { + const componentConfig = configsMap.get(componentFeature); + if (componentConfig) { + this.#configs[componentFeature] = componentConfig; + } else { + console.warn( + `Additional component "${componentFeature}" not found in Remote Settings` + ); + } + } + } + } + + /** + * Gets the configuration for a specific feature. + * + * @param {string} [feature] - The feature identifier. Defaults to the main feature. + * @returns {object|null} The feature's configuration object + */ + getConfig(feature) { + const targetFeature = feature || this.feature; + return this.#configs?.[targetFeature] || null; + } + + /** + * Loads a prompt for the specified feature. + * Tries Remote Settings first, then falls back to local prompts. + * + * @param {string} feature - The feature identifier + * @returns {Promise} The prompt content + */ + async loadPrompt(feature) { + // Try loading from Remote Settings first + const config = this.getConfig(feature); + if (config?.prompts) { + return config.prompts; + } + + console.warn( + `No Remote Settings prompt for ${feature}, falling back to local` + ); + + // Fall back to local prompts + try { + return await this.#loadLocalPrompt(feature); + } catch (error) { + throw new Error(`Failed to load prompt for ${feature}: ${error.message}`); + } + } + + /** + * Loads a prompt from local prompt files. + * + * @param {string} feature - The feature identifier + * @returns {Promise} The prompt content from local files + */ + async #loadLocalPrompt(feature) { + switch (feature) { + case MODEL_FEATURES.CHAT: { + const { assistantPrompt } = await import( + "moz-src:///browser/components/aiwindow/models/prompts/AssistantPrompts.sys.mjs" + ); + return assistantPrompt; + } + case MODEL_FEATURES.TITLE_GENERATION: { + const { titleGenerationPrompt } = await import( + "moz-src:///browser/components/aiwindow/models/prompts/TitleGenerationPrompts.sys.mjs" + ); + return titleGenerationPrompt; + } + case MODEL_FEATURES.CONVERSATION_SUGGESTIONS_SIDEBAR_STARTER: { + const { conversationStarterPrompt } = await import( + "moz-src:///browser/components/aiwindow/models/prompts/ConversationSuggestionsPrompts.sys.mjs" + ); + return conversationStarterPrompt; + } + case MODEL_FEATURES.CONVERSATION_SUGGESTIONS_FOLLOWUP: { + const { conversationFollowupPrompt } = await import( + "moz-src:///browser/components/aiwindow/models/prompts/ConversationSuggestionsPrompts.sys.mjs" + ); + return conversationFollowupPrompt; + } + case MODEL_FEATURES.CONVERSATION_SUGGESTIONS_ASSISTANT_LIMITATIONS: { + const { assistantLimitations } = await import( + "moz-src:///browser/components/aiwindow/models/prompts/ConversationSuggestionsPrompts.sys.mjs" + ); + return assistantLimitations; + } + case MODEL_FEATURES.CONVERSATION_SUGGESTIONS_MEMORIES: { + const { conversationMemoriesPrompt } = await import( + "moz-src:///browser/components/aiwindow/models/prompts/ConversationSuggestionsPrompts.sys.mjs" + ); + return conversationMemoriesPrompt; + } + // TODO: add local memories prompts imports for each feature + default: + throw new Error(`No local prompt found for feature: ${feature}`); + } + } + + /** + * Builds an openAIEngine instance with configuration loaded from Remote Settings. + * + * @param {string} feature + * The feature name to use to retrieve remote settings for prompts. + * @param {string} engineId + * The engine ID for MLEngine creation. Defaults to DEFAULT_ENGINE_ID. + * @param {string} serviceType + * The type of message to be sent ("ai", "memories", "s2s"). + * Defaults to SERVICE_TYPES.AI. + * @returns {Promise} + * Promise that will resolve to the configured engine instance. + */ + static async build( + feature, + engineId = DEFAULT_ENGINE_ID, + serviceType = SERVICE_TYPES.AI + ) { const engine = new openAIEngine(); + + await engine.loadConfig(feature); + engine.engineInstance = await openAIEngine.#createOpenAIEngine( engineId, - serviceType + serviceType, + engine.model ); + return engine; } @@ -68,9 +487,10 @@ export class openAIEngine { * * @param {string} engineId The identifier for the engine instance * @param {string} serviceType The type of message to be sent ("ai", "memories", "s2s") + * @param {string | null} modelId The resolved model ID (already contains fallback logic) * @returns {Promise} The configured engine instance */ - static async #createOpenAIEngine(engineId, serviceType) { + static async #createOpenAIEngine(engineId, serviceType, modelId = null) { const extraHeadersPref = Services.prefs.getStringPref( "browser.aiwindow.extraHeaders", "{}" @@ -85,11 +505,11 @@ export class openAIEngine { try { const engineInstance = await openAIEngine._createEngine({ - apiKey: Services.prefs.getStringPref("browser.aiwindow.apiKey"), + apiKey: Services.prefs.getStringPref("browser.aiwindow.apiKey", ""), backend: "openai", - baseURL: Services.prefs.getStringPref("browser.aiwindow.endpoint"), + baseURL: Services.prefs.getStringPref("browser.aiwindow.endpoint", ""), engineId, - modelId: Services.prefs.getStringPref("browser.aiwindow.model"), + modelId, modelRevision: "main", taskName: "text-generation", serviceType, diff --git a/browser/components/aiwindow/models/memories/Memories.sys.mjs b/browser/components/aiwindow/models/memories/Memories.sys.mjs index c4aed7e28c2bf..1411035830493 100644 --- a/browser/components/aiwindow/models/memories/Memories.sys.mjs +++ b/browser/components/aiwindow/models/memories/Memories.sys.mjs @@ -17,7 +17,7 @@ * 3. `existingMemoriesList`: an array of existing memory summary strings to deduplicate against * * Example Usage: - * const engine = await openAIEngine.build(); + * const engine = await openAIEngine.build(MODEL_FEATURES.MEMORIES, DEFAULT_ENGINE_ID, SERVICE_TYPES.MEMORIES); * const sources = {history: [domainItems, titleItems, searchItems]}; * const existingMemoriesList = [...]; // Array of existing memory summary strings; this should be fetched from memory storage * const newMemories = await generateMemories(engine, sources, existingMemoriesList); diff --git a/browser/components/aiwindow/models/memories/MemoriesManager.sys.mjs b/browser/components/aiwindow/models/memories/MemoriesManager.sys.mjs index 7678d2c7408b1..c59e447fadf0b 100644 --- a/browser/components/aiwindow/models/memories/MemoriesManager.sys.mjs +++ b/browser/components/aiwindow/models/memories/MemoriesManager.sys.mjs @@ -11,8 +11,11 @@ import { } from "moz-src:///browser/components/aiwindow/models/memories/MemoriesHistorySource.sys.mjs"; import { getRecentChats } from "./MemoriesChatSource.sys.mjs"; import { + DEFAULT_ENGINE_ID, + MODEL_FEATURES, openAIEngine, renderPrompt, + SERVICE_TYPES, } from "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs"; import { MemoryStore } from "moz-src:///browser/components/aiwindow/services/MemoryStore.sys.mjs"; import { @@ -64,7 +67,11 @@ export class MemoriesManager { */ static async ensureOpenAIEngine() { if (!this.#openAIEnginePromise) { - this.#openAIEnginePromise = await openAIEngine.build(); + this.#openAIEnginePromise = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); } return this.#openAIEnginePromise; } diff --git a/browser/components/aiwindow/models/prompts/AssistantPrompts.sys.mjs b/browser/components/aiwindow/models/prompts/AssistantPrompts.sys.mjs index 7ac58383ff506..f215ac26d879c 100644 --- a/browser/components/aiwindow/models/prompts/AssistantPrompts.sys.mjs +++ b/browser/components/aiwindow/models/prompts/AssistantPrompts.sys.mjs @@ -5,9 +5,8 @@ */ export const assistantPromptMetadata = { - version: "0.1", + version: "v1.0", }; - export const assistantPrompt = `You are a very knowledgeable personal browser assistant, designed to assist the user in navigating the web. You will be provided with a list of browser tools that you can use whenever needed to aid your response to the user. Your internal knowledge cutoff date is: July, 2024. diff --git a/browser/components/aiwindow/models/prompts/ConversationSuggestionsPrompts.sys.mjs b/browser/components/aiwindow/models/prompts/ConversationSuggestionsPrompts.sys.mjs index 1da22d12ec7f9..d99db9a00b7d2 100644 --- a/browser/components/aiwindow/models/prompts/ConversationSuggestionsPrompts.sys.mjs +++ b/browser/components/aiwindow/models/prompts/ConversationSuggestionsPrompts.sys.mjs @@ -4,7 +4,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const assistantLimitations = `The following tools are available to the browser assistant: +export const assistantLimitationstMetadata = { + version: "v1.0", +}; +export const assistantLimitations = `The following tools are available to the browser assistant: - get_open_tabs(): Access the user's browser and return a list of the most recently browsed data - get_page_content(url): Retrieve cleaned text content of the provided browser page URL - search_browsing_history(search_term, start_ts, end_ts): Retrieve pages from the user's past browsing history, optionally filtered by topic and/or time range @@ -27,7 +30,7 @@ Browser Assistant Capabilities & Limitations: 3. The assistant will decline to answer when it identifies agentic or unsafe requests.`; export const conversationStarterPromptMetadata = { - version: "0.1", + version: "v1.0", }; export const conversationStarterPrompt = `You are an expert in suggesting conversation starters for a browser assistant. @@ -44,7 +47,7 @@ Open Tabs: {open_tabs} ======== -${assistantLimitations} +{assistant_limitations} ======== Task: @@ -70,7 +73,7 @@ Rules: Return ONLY the suggestions, one per line, no numbering, no extra formatting. Sort from most to least relevant.`; export const conversationFollowupPromptMetadata = { - version: "0.1", + version: "v1.0", }; export const conversationFollowupPrompt = `You are an expert suggesting next responses or queries for a user during a conversation with an AI browser assistant. @@ -87,7 +90,7 @@ Conversation History (latest last): {conversation} ======== -${assistantLimitations} +{assistant_limitations} ======== Generate {n} suggested next responses or queries that the user might want to message next. diff --git a/browser/components/aiwindow/models/prompts/TitleGenerationPrompts.sys.mjs b/browser/components/aiwindow/models/prompts/TitleGenerationPrompts.sys.mjs index 127d8958d17a6..9e635770901fd 100644 --- a/browser/components/aiwindow/models/prompts/TitleGenerationPrompts.sys.mjs +++ b/browser/components/aiwindow/models/prompts/TitleGenerationPrompts.sys.mjs @@ -2,6 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +export const titleGenerationPromptMetadata = { + version: "v1.0", +}; export const titleGenerationPrompt = `Generate a concise chat title using only the current user message and the current context. Rules: diff --git a/browser/components/aiwindow/models/tests/browser/ai-window-prompts-remote-settings-snapshot.json b/browser/components/aiwindow/models/tests/browser/ai-window-prompts-remote-settings-snapshot.json new file mode 100644 index 0000000000000..797ed1a166a1d --- /dev/null +++ b/browser/components/aiwindow/models/tests/browser/ai-window-prompts-remote-settings-snapshot.json @@ -0,0 +1,86 @@ +[ + { + "model": "", + "feature": "conversation-suggestions-assistant-limitations", + "prompts": "You can do this and cannot do that.", + "version": "v1.0", + "is_default": true, + "parameters": {}, + "additional_components": [] + }, + { + "model": "qwen3-235b-a22b-instruct-2507-maas", + "feature": "conversation-suggestions-sidebar-starter", + "prompts": "Suggest a conversation starter message. {assistant_limitations}", + "version": "v1.0", + "is_default": true, + "parameters": {}, + "additional_components": [ + "conversation-suggestions-assistant-limitations", + "conversation-suggestions-memories" + ] + }, + { + "model": "qwen3-235b-a22b-instruct-2507-maas", + "feature": "chat", + "prompts": "You are a helpful browser assistant.", + "version": "v1.0", + "is_default": true, + "parameters": { + "temperature": 1.0 + }, + "additional_components": [] + }, + { + "model": "gpt-oss-120b", + "feature": "chat", + "prompts": "You are a helpful browser assistant.", + "version": "v1.0", + "is_default": false, + "parameters": { + "temperature": 1.0 + }, + "additional_components": [] + }, + { + "model": "gemini-2.5-flash-lite", + "feature": "chat", + "prompts": "You are a helpful browser assistant.", + "version": "v1.0", + "is_default": false, + "parameters": { + "temperature": 1.0 + }, + "additional_components": [] + }, + { + "model": "", + "feature": "conversation-suggestions-memories", + "prompts": "Use the following memories {memories}.", + "version": "v1.0", + "is_default": true, + "parameters": {}, + "additional_components": [] + }, + { + "model": "qwen3-235b-a22b-instruct-2507-maas", + "feature": "conversation-suggestions-followup", + "prompts": "Suggest next conversation message. {assistant_limitations}", + "version": "v1.0", + "is_default": true, + "parameters": {}, + "additional_components": [ + "conversation-suggestions-assistant-limitations", + "conversation-suggestions-memories" + ] + }, + { + "model": "qwen3-235b-a22b-instruct-2507-maas", + "feature": "title-generation", + "prompts": "Generate a chat title basd on current conversation.", + "version": "v1.0", + "is_default": true, + "parameters": {}, + "additional_components": [] + } +] diff --git a/browser/components/aiwindow/models/tests/browser/browser.toml b/browser/components/aiwindow/models/tests/browser/browser.toml index 6ea36a4efebc6..f6a301175e80d 100644 --- a/browser/components/aiwindow/models/tests/browser/browser.toml +++ b/browser/components/aiwindow/models/tests/browser/browser.toml @@ -1,6 +1,7 @@ [DEFAULT] support-files = [ "head.js", + "ai-window-prompts-remote-settings-snapshot.json", ] prefs = [ "browser.aiwindow.enabled=true", @@ -9,3 +10,4 @@ prefs = [ ["browser_getCurrentTabMetadata.js"] ["browser_get_page_content.js"] window_attributes = "ai-window" +["browser_utils_loadConfig.js"] diff --git a/browser/components/aiwindow/models/tests/browser/browser_utils_loadConfig.js b/browser/components/aiwindow/models/tests/browser/browser_utils_loadConfig.js new file mode 100644 index 0000000000000..63050aa56bfff --- /dev/null +++ b/browser/components/aiwindow/models/tests/browser/browser_utils_loadConfig.js @@ -0,0 +1,131 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { openAIEngine, MODEL_FEATURES } = ChromeUtils.importESModule( + "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs" +); + +const { RemoteSettings } = ChromeUtils.importESModule( + "resource://services-settings/remote-settings.sys.mjs" +); + +async function loadRemoteSettingsSnapshot() { + const chromeUrl = getRootDirectory(gTestPath); + const snapshotUrl = `${chromeUrl}ai-window-prompts-remote-settings-snapshot.json`; + + const response = await fetch(snapshotUrl); + if (!response.ok) { + throw new Error(`Failed to load snapshot: ${response.statusText}`); + } + return response.json(); +} + +add_setup(async function () { + const snapshotData = await loadRemoteSettingsSnapshot(); + + // Populate Remote Settings with snapshot data + const client = RemoteSettings("ai-window-prompts"); + await client.db.clear(); + + for (const record of snapshotData) { + await client.db.create({ + id: `${record.feature}-${record.model || "default"}-${record.version}`, + ...record, + }); + } + + await client.db.importChanges({}, Date.now()); + + registerCleanupFunction(async () => { + await client.db.clear(); + }); +}); + +add_task(async function test_loadConfig_chat_feature() { + const engine = new openAIEngine(); + await engine.loadConfig(MODEL_FEATURES.CHAT); + const config = engine.getConfig(engine.feature); + + info("Loaded config for 'chat' feature:"); + info(` Model: ${engine.model}`); + info(` Feature: ${engine.feature}`); + if (config) { + info(` Config version: ${config.version}`); + info(` Config model: ${config.model}`); + info(` Has prompts: ${!!config.prompts}`); + info(` Prompts: ${config.prompts}`); + } + + Assert.equal(engine.feature, "chat", "Feature should be set to 'chat'"); + Assert.equal( + engine.model, + "qwen3-235b-a22b-instruct-2507-maas", + "Model should be loaded from remote settings" + ); + Assert.ok(config, "Config should not be null or undefined"); + Assert.notEqual( + JSON.stringify(config), + "{}", + "Config should not be an empty object" + ); + Assert.equal(config.version, "v1.0", "Version should be v1.0"); + Assert.equal( + config.prompts, + "You are a helpful browser assistant.", + "Prompts should be loaded from remote settings" + ); + Assert.equal( + config.parameters.temperature, + 1.0, + "Temperature parameter should be loaded" + ); +}); + +add_task(async function test_loadConfig_with_additional_components() { + const engine = new openAIEngine(); + await engine.loadConfig( + MODEL_FEATURES.CONVERSATION_SUGGESTIONS_SIDEBAR_STARTER + ); + + const mainConfig = engine.getConfig( + MODEL_FEATURES.CONVERSATION_SUGGESTIONS_SIDEBAR_STARTER + ); + + info("Testing additional_components loading:"); + info(` Main feature: ${engine.feature}`); + info(` Model: ${engine.model}`); + if (mainConfig) { + info( + ` Additional components: ${mainConfig.additional_components.join(", ")}` + ); + } + + Assert.ok(mainConfig, "Main config should be loaded"); + Assert.ok( + Array.isArray(mainConfig.additional_components), + "additional_components should be an array" + ); + Assert.equal( + mainConfig.additional_components.length, + 2, + "Should have 2 additional components" + ); + + const limitationsConfig = engine.getConfig( + "conversation-suggestions-assistant-limitations" + ); + Assert.ok( + limitationsConfig, + "Assistant limitations component should be loaded" + ); + Assert.ok( + limitationsConfig.prompts, + "Assistant limitations should have prompts" + ); + + const memoriesConfig = engine.getConfig("conversation-suggestions-memories"); + Assert.ok(memoriesConfig, "Memories component should be loaded"); + Assert.ok(memoriesConfig.prompts, "Memories should have prompts"); +}); diff --git a/browser/components/aiwindow/models/tests/xpcshell/ai-window-prompts-remote-settings-snapshot.json b/browser/components/aiwindow/models/tests/xpcshell/ai-window-prompts-remote-settings-snapshot.json new file mode 100644 index 0000000000000..797ed1a166a1d --- /dev/null +++ b/browser/components/aiwindow/models/tests/xpcshell/ai-window-prompts-remote-settings-snapshot.json @@ -0,0 +1,86 @@ +[ + { + "model": "", + "feature": "conversation-suggestions-assistant-limitations", + "prompts": "You can do this and cannot do that.", + "version": "v1.0", + "is_default": true, + "parameters": {}, + "additional_components": [] + }, + { + "model": "qwen3-235b-a22b-instruct-2507-maas", + "feature": "conversation-suggestions-sidebar-starter", + "prompts": "Suggest a conversation starter message. {assistant_limitations}", + "version": "v1.0", + "is_default": true, + "parameters": {}, + "additional_components": [ + "conversation-suggestions-assistant-limitations", + "conversation-suggestions-memories" + ] + }, + { + "model": "qwen3-235b-a22b-instruct-2507-maas", + "feature": "chat", + "prompts": "You are a helpful browser assistant.", + "version": "v1.0", + "is_default": true, + "parameters": { + "temperature": 1.0 + }, + "additional_components": [] + }, + { + "model": "gpt-oss-120b", + "feature": "chat", + "prompts": "You are a helpful browser assistant.", + "version": "v1.0", + "is_default": false, + "parameters": { + "temperature": 1.0 + }, + "additional_components": [] + }, + { + "model": "gemini-2.5-flash-lite", + "feature": "chat", + "prompts": "You are a helpful browser assistant.", + "version": "v1.0", + "is_default": false, + "parameters": { + "temperature": 1.0 + }, + "additional_components": [] + }, + { + "model": "", + "feature": "conversation-suggestions-memories", + "prompts": "Use the following memories {memories}.", + "version": "v1.0", + "is_default": true, + "parameters": {}, + "additional_components": [] + }, + { + "model": "qwen3-235b-a22b-instruct-2507-maas", + "feature": "conversation-suggestions-followup", + "prompts": "Suggest next conversation message. {assistant_limitations}", + "version": "v1.0", + "is_default": true, + "parameters": {}, + "additional_components": [ + "conversation-suggestions-assistant-limitations", + "conversation-suggestions-memories" + ] + }, + { + "model": "qwen3-235b-a22b-instruct-2507-maas", + "feature": "title-generation", + "prompts": "Generate a chat title basd on current conversation.", + "version": "v1.0", + "is_default": true, + "parameters": {}, + "additional_components": [] + } +] diff --git a/browser/components/aiwindow/models/tests/xpcshell/test_Chat.js b/browser/components/aiwindow/models/tests/xpcshell/test_Chat.js index 8109efe7d0ce8..2b19918b9a0d7 100644 --- a/browser/components/aiwindow/models/tests/xpcshell/test_Chat.js +++ b/browser/components/aiwindow/models/tests/xpcshell/test_Chat.js @@ -12,7 +12,7 @@ const { SYSTEM_PROMPT_TYPE, MESSAGE_ROLE } = ChromeUtils.importESModule( const { Chat } = ChromeUtils.importESModule( "moz-src:///browser/components/aiwindow/models/Chat.sys.mjs" ); -const { openAIEngine } = ChromeUtils.importESModule( +const { MODEL_FEATURES, openAIEngine } = ChromeUtils.importESModule( "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs" ); @@ -52,47 +52,53 @@ add_task(async function test_Chat_real_tools_are_registered() { ); }); -add_task(async function test_openAIEngine_build_uses_prefs() { - Services.prefs.setStringPref(PREF_API_KEY, "test-key-123"); - Services.prefs.setStringPref(PREF_ENDPOINT, "https://example.test/v1"); - Services.prefs.setStringPref(PREF_MODEL, "gpt-fake"); +add_task( + async function test_openAIEngine_build_with_chat_feature_and_nonexistent_model() { + Services.prefs.setStringPref(PREF_API_KEY, "test-key-123"); + Services.prefs.setStringPref(PREF_ENDPOINT, "https://example.test/v1"); + Services.prefs.setStringPref(PREF_MODEL, "nonexistent-model"); - const sb = sinon.createSandbox(); - try { - const fakeEngineInstance = { - runWithGenerator() { - throw new Error("not used"); - }, - }; - const stub = sb - .stub(openAIEngine, "_createEngine") - .resolves(fakeEngineInstance); + const sb = sinon.createSandbox(); + try { + const fakeEngineInstance = { + runWithGenerator() { + throw new Error("not used"); + }, + }; + const stub = sb + .stub(openAIEngine, "_createEngine") + .resolves(fakeEngineInstance); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build(MODEL_FEATURES.CHAT); - Assert.ok( - engine instanceof openAIEngine, - "Should return openAIEngine instance" - ); - Assert.strictEqual( - engine.engineInstance, - fakeEngineInstance, - "Should store engine instance" - ); - Assert.ok(stub.calledOnce, "_createEngine should be called once"); + Assert.ok( + engine instanceof openAIEngine, + "Should return openAIEngine instance" + ); + Assert.strictEqual( + engine.engineInstance, + fakeEngineInstance, + "Should store engine instance" + ); + Assert.ok(stub.calledOnce, "_createEngine should be called once"); - const opts = stub.firstCall.args[0]; - Assert.equal(opts.apiKey, "test-key-123", "apiKey should come from pref"); - Assert.equal( - opts.baseURL, - "https://example.test/v1", - "baseURL should come from pref" - ); - Assert.equal(opts.modelId, "gpt-fake", "modelId should come from pref"); - } finally { - sb.restore(); + const opts = stub.firstCall.args[0]; + Assert.equal(opts.apiKey, "test-key-123", "apiKey should come from pref"); + Assert.equal( + opts.baseURL, + "https://example.test/v1", + "baseURL should come from pref" + ); + Assert.equal( + opts.modelId, + "qwen3-235b-a22b-instruct-2507-maas", + "modelId should fallback to default" + ); + } finally { + sb.restore(); + } } -}); +); add_task(async function test_Chat_fetchWithHistory_streams_and_forwards_args() { const sb = sinon.createSandbox(); @@ -114,6 +120,9 @@ add_task(async function test_Chat_fetchWithHistory_streams_and_forwards_args() { } return gen(); }, + getConfig() { + return {}; + }, }; sb.stub(openAIEngine, "build").resolves(fakeEngine); @@ -189,6 +198,9 @@ add_task(async function test_Chat_fetchWithHistory_handles_tool_calls() { } return gen(); }, + getConfig() { + return {}; + }, }; // Mock tool function @@ -314,6 +326,9 @@ add_task( } return gen(); }, + getConfig() { + return {}; + }, }; Chat.toolMap.test_tool = sb.stub().resolves("should not be called"); diff --git a/browser/components/aiwindow/models/tests/xpcshell/test_ConversationSuggestions.js b/browser/components/aiwindow/models/tests/xpcshell/test_ConversationSuggestions.js index afabb850c1c35..34e0a39181bfa 100644 --- a/browser/components/aiwindow/models/tests/xpcshell/test_ConversationSuggestions.js +++ b/browser/components/aiwindow/models/tests/xpcshell/test_ConversationSuggestions.js @@ -23,7 +23,7 @@ const { MemoriesManager } = ChromeUtils.importESModule( "moz-src:///browser/components/aiwindow/models/memories/MemoriesManager.sys.mjs" ); const { MESSAGE_ROLE } = ChromeUtils.importESModule( - "moz-src:///browser/components/aiwindow/ui/modules/ChatStore.sys.mjs" + "moz-src:///browser/components/aiwindow/ui/modules/ChatConstants.sys.mjs" ); const { sinon } = ChromeUtils.importESModule( "resource://testing-common/Sinon.sys.mjs" @@ -42,6 +42,18 @@ const API_KEY = "test-api-key"; const ENDPOINT = "https://api.test-endpoint.com/v1"; const MODEL = "test-model"; +async function loadRemoteSettingsSnapshot() { + const file = do_get_file("ai-window-prompts-remote-settings-snapshot.json"); + const data = await IOUtils.readUTF8(file.path); + return JSON.parse(data); +} + +let REAL_REMOTE_SETTINGS_SNAPSHOT; + +add_setup(async function () { + REAL_REMOTE_SETTINGS_SNAPSHOT = await loadRemoteSettingsSnapshot(); +}); + /** * Cleans up preferences after testing */ @@ -176,7 +188,12 @@ add_task(async function test_addMemoriesToPrompt_have_memories() { const memoriesStub = sb .stub(MemoriesGetterForSuggestionPrompts, "getMemorySummariesForPrompt") .resolves(fakeMemories); - const promptWithMemories = await addMemoriesToPrompt(basePrompt); + const conversationMemoriesPrompt = "Memories block:\n{memories}"; + const promptWithMemories = await addMemoriesToPrompt( + basePrompt, + conversationMemoriesPrompt + ); + Assert.ok( memoriesStub.calledOnce, "getMemorySummariesForPrompt should be called" @@ -202,7 +219,12 @@ add_task(async function test_addMemoriesToPrompt_dont_have_memories() { const memoriesStub = sb .stub(MemoriesGetterForSuggestionPrompts, "getMemorySummariesForPrompt") .resolves(fakeMemories); - const promptWithMemories = await addMemoriesToPrompt(basePrompt); + const conversationMemoriesPrompt = "Memories block:\n{memories}"; + const promptWithMemories = await addMemoriesToPrompt( + basePrompt, + conversationMemoriesPrompt + ); + Assert.ok( memoriesStub.calledOnce, "getMemorySummariesForPrompt should be called" @@ -869,6 +891,55 @@ add_task( } ); +/** + * Tests that assistant limitations are included in conversation starter prompts + */ +add_task( + async function test_generateConversationStartersSidebar_includes_assistant_limitations() { + Services.prefs.setStringPref(PREF_API_KEY, API_KEY); + Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT); + Services.prefs.setStringPref(PREF_MODEL, MODEL); + + const sb = sinon.createSandbox(); + try { + const fakeEngine = { + run: sb.stub().resolves({ + finalOutput: `Suggestion 1\nSuggestion 2\nSuggestion 3`, + }), + }; + sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); + + sb.stub(openAIEngine, "getRemoteClient").returns({ + get: sb.stub().resolves(REAL_REMOTE_SETTINGS_SNAPSHOT), + }); + + sb.stub( + MemoriesGetterForSuggestionPrompts, + "getMemorySummariesForPrompt" + ).resolves([]); + + const n = 3; + const contextTabs = [ + { title: "Test Tab", url: "https://test.example.com" }, + ]; + + await generateConversationStartersSidebar(contextTabs, n, false); + + Assert.ok(fakeEngine.run.calledOnce, "Engine run should be called once"); + + const callArgs = fakeEngine.run.firstCall.args[0]; + Assert.ok( + callArgs.messages[1].content.includes( + "You can do this and cannot do that." + ), + "Prompt should include assistant limitations from remote settings" + ); + } finally { + sb.restore(); + } + } +); + /** * Tests for generateFollowupPrompts successfully generating suggestions */ @@ -1235,6 +1306,57 @@ add_task(async function test_generateFollowupPrompts_engine_error() { } }); +/** + * Tests that assistant limitations are included in followup prompts + */ +add_task( + async function test_generateFollowupPrompts_includes_assistant_limitations() { + Services.prefs.setStringPref(PREF_API_KEY, API_KEY); + Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT); + Services.prefs.setStringPref(PREF_MODEL, MODEL); + + const sb = sinon.createSandbox(); + try { + const fakeEngine = { + run: sb.stub().resolves({ + finalOutput: `Suggestion 1\nSuggestion 2`, + }), + }; + sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); + + sb.stub(openAIEngine, "getRemoteClient").returns({ + get: sb.stub().resolves(REAL_REMOTE_SETTINGS_SNAPSHOT), + }); + + sb.stub( + MemoriesGetterForSuggestionPrompts, + "getMemorySummariesForPrompt" + ).resolves([]); + + const n = 2; + const conversationHistory = [ + { role: MESSAGE_ROLE.USER, content: "Hello" }, + { role: MESSAGE_ROLE.ASSISTANT, content: "Hi there!" }, + ]; + const currentTab = { title: "Test", url: "https://test.example.com" }; + + await generateFollowupPrompts(conversationHistory, currentTab, n, false); + + Assert.ok(fakeEngine.run.calledOnce, "Engine run should be called once"); + + const callArgs = fakeEngine.run.firstCall.args[0]; + Assert.ok( + callArgs.messages[1].content.includes( + "You can do this and cannot do that." + ), + "Prompt should include assistant limitations from remote settings" + ); + } finally { + sb.restore(); + } + } +); + /** * Tests for getMemorySummariesForPrompt happy path */ diff --git a/browser/components/aiwindow/models/tests/xpcshell/test_Memories.js b/browser/components/aiwindow/models/tests/xpcshell/test_Memories.js index 9902001833ec1..526e87f68864a 100644 --- a/browser/components/aiwindow/models/tests/xpcshell/test_Memories.js +++ b/browser/components/aiwindow/models/tests/xpcshell/test_Memories.js @@ -18,9 +18,10 @@ const { const { getRecentChats } = ChromeUtils.importESModule( "moz-src:///browser/components/aiwindow/models/memories/MemoriesChatSource.sys.mjs" ); -const { openAIEngine } = ChromeUtils.importESModule( - "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs" -); +const { DEFAULT_ENGINE_ID, MODEL_FEATURES, openAIEngine, SERVICE_TYPES } = + ChromeUtils.importESModule( + "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs" + ); const { sinon } = ChromeUtils.importESModule( "resource://testing-common/Sinon.sys.mjs" ); @@ -454,7 +455,11 @@ add_task(async function test_generateInitialMemoriesList_happy_path() { // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").returns(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const [domainItems, titleItems, searchItems] = @@ -532,7 +537,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").returns(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const [domainItems, titleItems, searchItems] = @@ -566,7 +575,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").returns(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const [domainItems, titleItems, searchItems] = @@ -600,7 +613,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").returns(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const [domainItems, titleItems, searchItems] = @@ -664,7 +681,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").returns(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const [domainItems, titleItems, searchItems] = @@ -732,7 +753,11 @@ add_task(async function test_deduplicateMemoriesList_happy_path() { // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const dedupedMemoriesList = await deduplicateMemories( @@ -791,7 +816,11 @@ add_task(async function test_deduplicateMemoriesList_sad_path_empty_output() { // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const dedupedMemoriesList = await deduplicateMemories( @@ -825,7 +854,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const dedupedMemoriesList = await deduplicateMemories( @@ -866,7 +899,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const dedupedMemoriesList = await deduplicateMemories( @@ -907,7 +944,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const dedupedMemoriesList = await deduplicateMemories( @@ -969,7 +1010,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const dedupedMemoriesList = await deduplicateMemories( @@ -1023,7 +1068,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const dedupedMemoriesList = await deduplicateMemories( @@ -1075,7 +1124,11 @@ add_task(async function test_filterSensitiveMemories_happy_path() { // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const nonSensitiveMemoriesList = await filterSensitiveMemories( @@ -1125,7 +1178,11 @@ add_task(async function test_filterSensitiveMemories_sad_path_empty_output() { // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const nonSensitiveMemoriesList = await filterSensitiveMemories( @@ -1165,7 +1222,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const nonSensitiveMemoriesList = await filterSensitiveMemories( @@ -1208,7 +1269,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const nonSensitiveMemoriesList = await filterSensitiveMemories( @@ -1253,7 +1318,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const nonSensitiveMemoriesList = await filterSensitiveMemories( @@ -1300,7 +1369,11 @@ add_task( // Check that the stub was called const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build( + MODEL_FEATURES.MEMORIES, + DEFAULT_ENGINE_ID, + SERVICE_TYPES.MEMORIES + ); Assert.ok(stub.calledOnce, "_createEngine should be called once"); const nonSensitiveMemoriesList = await filterSensitiveMemories( diff --git a/browser/components/aiwindow/models/tests/xpcshell/test_Tools_GetOpenTabs.js b/browser/components/aiwindow/models/tests/xpcshell/test_Tools_GetOpenTabs.js index 0d0615792b1d6..d1fa66c55e725 100644 --- a/browser/components/aiwindow/models/tests/xpcshell/test_Tools_GetOpenTabs.js +++ b/browser/components/aiwindow/models/tests/xpcshell/test_Tools_GetOpenTabs.js @@ -81,17 +81,19 @@ add_task(async function test_getOpenTabs_basic() { Assert.equal(tabs.length, 3, "Should return all 3 tabs"); Assert.equal(tabs[0].url, "https://firefox.com", "Most recent tab first"); Assert.equal(tabs[0].title, "Firefox", "Title should match"); - Assert.equal( - tabs[0].description, - "Firefox browser homepage", - "Description should be fetched" - ); + // @todo Bug2009194 + // Assert.equal( + // tabs[0].description, + // "Firefox browser homepage", + // "Description should be fetched" + // ); Assert.equal(tabs[1].url, "https://mozilla.org", "Second most recent tab"); - Assert.equal( - tabs[1].description, - "Mozilla organization site", - "Description should be fetched" - ); + // @todo Bug2009194 + // Assert.equal( + // tabs[1].description, + // "Mozilla organization site", + // "Description should be fetched" + // ); Assert.equal(tabs[2].url, "https://example.com", "Least recent tab"); Assert.equal( tabs[2].description, @@ -277,11 +279,12 @@ add_task(async function test_getOpenTabs_return_structure() { Assert.equal(tab.url, "https://test.com", "url value correct"); Assert.equal(tab.title, "Test Page", "title value correct"); - Assert.equal( - tab.description, - "A test page description", - "description should be fetched from PageDataService" - ); + // @todo Bug2009194 + // Assert.equal( + // tab.description, + // "A test page description", + // "description should be fetched from PageDataService" + // ); Assert.equal(tab.lastAccessed, 1000, "lastAccessed value correct"); } finally { sb.restore(); diff --git a/browser/components/aiwindow/models/tests/xpcshell/test_Utils.js b/browser/components/aiwindow/models/tests/xpcshell/test_Utils.js index 32e9a728650e3..1e696795db2fe 100644 --- a/browser/components/aiwindow/models/tests/xpcshell/test_Utils.js +++ b/browser/components/aiwindow/models/tests/xpcshell/test_Utils.js @@ -2,9 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const { openAIEngine, renderPrompt } = ChromeUtils.importESModule( - "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs" -); +const { MODEL_FEATURES, openAIEngine, renderPrompt } = + ChromeUtils.importESModule( + "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs" + ); const { sinon } = ChromeUtils.importESModule( "resource://testing-common/Sinon.sys.mjs" @@ -37,7 +38,7 @@ registerCleanupFunction(() => { /** * Tests the creation of an OpenAI engine instance */ -add_task(async function test_createOpenAIEngine() { +add_task(async function test_createOpenAIEngine_with_chat_feature() { Services.prefs.setStringPref(PREF_API_KEY, API_KEY); Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT); Services.prefs.setStringPref(PREF_MODEL, MODEL); @@ -53,7 +54,7 @@ add_task(async function test_createOpenAIEngine() { }; const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); - const engine = await openAIEngine.build(); + const engine = await openAIEngine.build(MODEL_FEATURES.CHAT); Assert.strictEqual( engine.engineInstance, fakeEngine, @@ -71,7 +72,7 @@ add_task(async function test_createOpenAIEngine() { "smart-openai", "engineId should be smart-openai" ); - Assert.equal(opts.modelId, MODEL, "modelId should come from pref"); + Assert.ok(opts.modelId, "modelId should be set"); Assert.equal(opts.modelRevision, "main", "modelRevision should be main"); Assert.equal( opts.taskName, diff --git a/browser/components/aiwindow/models/tests/xpcshell/test_Utils_RemoteSettings.js b/browser/components/aiwindow/models/tests/xpcshell/test_Utils_RemoteSettings.js new file mode 100644 index 0000000000000..d217a4f2fc661 --- /dev/null +++ b/browser/components/aiwindow/models/tests/xpcshell/test_Utils_RemoteSettings.js @@ -0,0 +1,374 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { openAIEngine, MODEL_FEATURES } = ChromeUtils.importESModule( + "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs" +); + +const { sinon } = ChromeUtils.importESModule( + "resource://testing-common/Sinon.sys.mjs" +); + +const PREF_API_KEY = "browser.aiwindow.apiKey"; +const PREF_ENDPOINT = "browser.aiwindow.endpoint"; +const PREF_MODEL = "browser.aiwindow.model"; + +const API_KEY = "fake-key"; +const ENDPOINT = "https://api.fake-endpoint.com/v1"; + +async function loadRemoteSettingsSnapshot() { + const file = do_get_file("ai-window-prompts-remote-settings-snapshot.json"); + const data = await IOUtils.readUTF8(file.path); + return JSON.parse(data); +} + +let REAL_REMOTE_SETTINGS_SNAPSHOT; + +add_setup(async function () { + REAL_REMOTE_SETTINGS_SNAPSHOT = await loadRemoteSettingsSnapshot(); +}); + +registerCleanupFunction(() => { + for (let pref of [PREF_API_KEY, PREF_ENDPOINT, PREF_MODEL]) { + if (Services.prefs.prefHasUserValue(pref)) { + Services.prefs.clearUserPref(pref); + } + } +}); + +add_task(async function test_loadConfig_basic_with_real_snapshot() { + Services.prefs.setStringPref(PREF_API_KEY, API_KEY); + Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT); + + const sb = sinon.createSandbox(); + try { + const fakeEngine = { + runWithGenerator() { + throw new Error("not used"); + }, + }; + sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); + + sb.stub(openAIEngine, "getRemoteClient").returns({ + get: sb.stub().resolves(REAL_REMOTE_SETTINGS_SNAPSHOT), + }); + + const engine = new openAIEngine(); + + await engine.loadConfig(MODEL_FEATURES.CHAT); + + Assert.equal( + engine.feature, + MODEL_FEATURES.CHAT, + "Feature should be set correctly" + ); + Assert.ok(engine.model, "Model should be loaded from remote settings"); + + const config = engine.getConfig(MODEL_FEATURES.CHAT); + Assert.ok(config, "Config should be loaded"); + Assert.ok(config.prompts, "Prompts should be loaded from remote settings"); + Assert.ok( + config.prompts.includes("browser assistant"), + "Prompts should contain expected content" + ); + } finally { + sb.restore(); + } +}); + +add_task(async function test_loadConfig_with_user_pref_model() { + Services.prefs.setStringPref(PREF_API_KEY, API_KEY); + Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT); + Services.prefs.setStringPref(PREF_MODEL, "gpt-oss-120b"); + + const sb = sinon.createSandbox(); + try { + const fakeEngine = { + runWithGenerator() { + throw new Error("not used"); + }, + }; + sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); + + sb.stub(openAIEngine, "getRemoteClient").returns({ + get: sb.stub().resolves(REAL_REMOTE_SETTINGS_SNAPSHOT), + }); + + const engine = new openAIEngine(); + + await engine.loadConfig(MODEL_FEATURES.CHAT); + + Assert.equal( + engine.model, + "gpt-oss-120b", + "User pref model should filter to matching configs" + ); + const config = engine.getConfig(MODEL_FEATURES.CHAT); + Assert.equal( + config.model, + "gpt-oss-120b", + "Selected config should be for user's preferred model" + ); + } finally { + sb.restore(); + Services.prefs.clearUserPref(PREF_MODEL); + } +}); + +add_task(async function test_loadConfig_no_records() { + Services.prefs.setStringPref(PREF_API_KEY, API_KEY); + Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT); + + const sb = sinon.createSandbox(); + try { + const fakeEngine = { + runWithGenerator() { + throw new Error("not used"); + }, + }; + sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); + + sb.stub(openAIEngine, "getRemoteClient").returns({ + get: sb.stub().resolves([]), + }); + + const engine = new openAIEngine(); + + await engine.loadConfig(MODEL_FEATURES.CHAT); + + Assert.equal( + engine.model, + "qwen3-235b-a22b-instruct-2507-maas", + "Should fall back to default model when remote settings returns no records" + ); + Assert.equal( + engine.feature, + MODEL_FEATURES.CHAT, + "Should set feature when remote settings returns no records" + ); + } finally { + sb.restore(); + } +}); + +add_task(async function test_loadConfig_filters_by_major_version() { + Services.prefs.setStringPref(PREF_API_KEY, API_KEY); + Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT); + + const sb = sinon.createSandbox(); + try { + const fakeEngine = { + runWithGenerator() { + throw new Error("not used"); + }, + }; + sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); + + // Add a v2.0 record to test data + const recordsWithV2 = [ + ...REAL_REMOTE_SETTINGS_SNAPSHOT, + { + model: "future-model", + feature: "chat", + prompts: "Future version prompt", + version: "v2.0", + is_default: true, + }, + ]; + + sb.stub(openAIEngine, "getRemoteClient").returns({ + get: sb.stub().resolves(recordsWithV2), + }); + + const engine = new openAIEngine(); + await engine.loadConfig(MODEL_FEATURES.CHAT); + + const config = engine.getConfig(MODEL_FEATURES.CHAT); + // Should get v1.x, not v2.0 + Assert.ok(config.version.startsWith("v1."), "Should select v1.x, not v2.0"); + } finally { + sb.restore(); + } +}); + +add_task(async function test_loadConfig_fallback_when_user_model_not_found() { + Services.prefs.setStringPref(PREF_API_KEY, API_KEY); + Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT); + Services.prefs.setStringPref(PREF_MODEL, "nonexistent-model"); + + const sb = sinon.createSandbox(); + try { + const fakeEngine = { + runWithGenerator() { + throw new Error("not used"); + }, + }; + sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); + + sb.stub(openAIEngine, "getRemoteClient").returns({ + get: sb.stub().resolves(REAL_REMOTE_SETTINGS_SNAPSHOT), + }); + + const engine = new openAIEngine(); + await engine.loadConfig(MODEL_FEATURES.CHAT); + + // Should fall back to default model + Assert.notEqual( + engine.model, + "nonexistent-model", + "Should not use invalid user model" + ); + const config = engine.getConfig(MODEL_FEATURES.CHAT); + Assert.equal(config.is_default, true, "Should fall back to default config"); + Assert.equal( + config.model, + engine.model, + "Engine model should match the default config's model" + ); + Assert.equal(config.version, "v1.0", "Should use v1.0"); + } finally { + sb.restore(); + Services.prefs.clearUserPref(PREF_MODEL); + } +}); + +add_task(async function test_loadPrompt_from_remote_settings() { + Services.prefs.setStringPref(PREF_API_KEY, API_KEY); + Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT); + + const sb = sinon.createSandbox(); + try { + const fakeEngine = { + runWithGenerator() { + throw new Error("not used"); + }, + }; + sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); + + sb.stub(openAIEngine, "getRemoteClient").returns({ + get: sb.stub().resolves(REAL_REMOTE_SETTINGS_SNAPSHOT), + }); + + const engine = new openAIEngine(); + await engine.loadConfig(MODEL_FEATURES.TITLE_GENERATION); + + const prompt = await engine.loadPrompt(MODEL_FEATURES.TITLE_GENERATION); + + Assert.ok(prompt, "Prompt should be loaded from remote settings"); + Assert.ok( + prompt.includes("title") || prompt.includes("conversation"), + "Prompt should contain expected content for title generation" + ); + } finally { + sb.restore(); + } +}); + +add_task(async function test_loadPrompt_fallback_to_local() { + Services.prefs.setStringPref(PREF_API_KEY, API_KEY); + Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT); + + const sb = sinon.createSandbox(); + try { + const fakeEngine = { + runWithGenerator() { + throw new Error("not used"); + }, + }; + sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); + + sb.stub(openAIEngine, "getRemoteClient").returns({ + get: sb.stub().resolves([]), + }); + + const engine = new openAIEngine(); + await engine.loadConfig(MODEL_FEATURES.TITLE_GENERATION); + + const prompt = await engine.loadPrompt(MODEL_FEATURES.TITLE_GENERATION); + + Assert.ok(prompt, "Prompt should fallback to local prompt"); + Assert.ok( + prompt.includes("Generate a concise chat title"), + "Should load local prompt when remote settings has no config" + ); + } finally { + sb.restore(); + } +}); + +add_task(async function test_build_with_feature() { + Services.prefs.setStringPref(PREF_API_KEY, API_KEY); + Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT); + + const sb = sinon.createSandbox(); + try { + const fakeEngine = { + runWithGenerator() { + throw new Error("not used"); + }, + }; + const createEngineStub = sb + .stub(openAIEngine, "_createEngine") + .resolves(fakeEngine); + + sb.stub(openAIEngine, "getRemoteClient").returns({ + get: sb.stub().resolves(REAL_REMOTE_SETTINGS_SNAPSHOT), + }); + + const engine = await openAIEngine.build(MODEL_FEATURES.CHAT); + + Assert.ok(engine.engineInstance, "Engine instance should be created"); + Assert.equal(engine.feature, MODEL_FEATURES.CHAT, "Feature should be set"); + Assert.ok(engine.model, "Model should be loaded from remote settings"); + + const opts = createEngineStub.firstCall.args[0]; + Assert.ok(opts.modelId, "Model should be passed to engine creation"); + Assert.equal( + opts.modelId, + engine.model, + "Model passed to engine should match loaded model" + ); + } finally { + sb.restore(); + } +}); + +add_task(async function test_inference_params_from_config() { + Services.prefs.setStringPref(PREF_API_KEY, API_KEY); + Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT); + + const sb = sinon.createSandbox(); + try { + const fakeEngine = { + runWithGenerator() { + throw new Error("not used"); + }, + }; + sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine); + + sb.stub(openAIEngine, "getRemoteClient").returns({ + get: sb.stub().resolves(REAL_REMOTE_SETTINGS_SNAPSHOT), + }); + + const engine = new openAIEngine(); + await engine.loadConfig(MODEL_FEATURES.CHAT); + + const config = engine.getConfig(MODEL_FEATURES.CHAT); + Assert.ok(config, "Config should be loaded"); + + const inferenceParams = config?.parameters || {}; + Assert.equal( + typeof inferenceParams, + "object", + "Inference parameters should be an object" + ); + Assert.equal( + inferenceParams.temperature, + 1.0, + "Temperature should be loaded from parameters" + ); + } finally { + sb.restore(); + } +}); diff --git a/browser/components/aiwindow/models/tests/xpcshell/xpcshell.toml b/browser/components/aiwindow/models/tests/xpcshell/xpcshell.toml index 06345d1e8eeeb..a1fca6c43b148 100644 --- a/browser/components/aiwindow/models/tests/xpcshell/xpcshell.toml +++ b/browser/components/aiwindow/models/tests/xpcshell/xpcshell.toml @@ -4,7 +4,9 @@ run-if = [ ] head = "head.js" firefox-appdir = "browser" -support-files = [] +support-files = [ + "ai-window-prompts-remote-settings-snapshot.json", +] ["test_Chat.js"] @@ -40,4 +42,6 @@ support-files = [] ["test_Utils.js"] +["test_Utils_RemoteSettings.js"] + ["test_intent_classifier.js"] diff --git a/browser/components/aiwindow/ui/components/ai-chat-content/ai-chat-content.css b/browser/components/aiwindow/ui/components/ai-chat-content/ai-chat-content.css index 64c7f3bbec776..18956704071e1 100644 --- a/browser/components/aiwindow/ui/components/ai-chat-content/ai-chat-content.css +++ b/browser/components/aiwindow/ui/components/ai-chat-content/ai-chat-content.css @@ -1,8 +1,28 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* stylelint-disable */ .chat-content-wrapper { display: flex; flex-direction: column; + gap: var(--space-small); +} + +/* Temporary chat bubble styling */ +.chat-bubble { + width: 400px; + display: flex; + flex-direction: column; + padding: var(--space-large); + box-sizing: border-box; + border: 1px dashed #ccc; +} + +.chat-bubble-user { + align-items: flex-end; +} + +.chat-bubble-assistant { + gap: var(--space-small); } diff --git a/browser/components/aiwindow/ui/components/ai-chat-content/ai-chat-content.mjs b/browser/components/aiwindow/ui/components/ai-chat-content/ai-chat-content.mjs index 5ec677c363980..8b302493cb727 100644 --- a/browser/components/aiwindow/ui/components/ai-chat-content/ai-chat-content.mjs +++ b/browser/components/aiwindow/ui/components/ai-chat-content/ai-chat-content.mjs @@ -2,8 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { html } from "chrome://global/content/vendor/lit.all.mjs"; +import { html, nothing } from "chrome://global/content/vendor/lit.all.mjs"; import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; +// eslint-disable-next-line import/no-unassigned-import +import "chrome://browser/content/aiwindow/components/assistant-message-footer.mjs"; /** * A custom element for managing AI Chat Content @@ -46,14 +48,15 @@ export class AIChatContent extends MozLitElement { /** * Handle user prompt events * - * @param {CustomeEvent} event - The custom event containing the user prompt + * @param {CustomEvent} event - The custom event containing the user prompt */ handleUserPromptEvent(event) { const { content } = event.detail; - - this.conversationState.push({ role: "user", content }); - + this.conversationState.push({ + role: "user", + body: content.body, + }); this.requestUpdate(); } @@ -64,9 +67,15 @@ export class AIChatContent extends MozLitElement { */ handleAIResponseEvent(event) { - const { ordinal } = event.detail; + // TODO (bug 2009434): update reference to insights + const { ordinal, id: messageId, content, insightsApplied } = event.detail; - this.conversationState[ordinal] = event.detail; + this.conversationState[ordinal] = { + role: "assistant", + messageId, + body: content.body, + appliedMemories: insightsApplied ?? [], + }; this.requestUpdate(); } @@ -79,10 +88,23 @@ export class AIChatContent extends MozLitElement { />
${this.conversationState.map(msg => { - return html``; + return html` +
+ + + ${msg.role === "assistant" + ? html` + + ` + : nothing} +
+ `; })}
`; diff --git a/browser/components/aiwindow/ui/components/ai-chat-content/ai-chat-content.stories.mjs b/browser/components/aiwindow/ui/components/ai-chat-content/ai-chat-content.stories.mjs index 57d107539277e..f5ff99601aac2 100644 --- a/browser/components/aiwindow/ui/components/ai-chat-content/ai-chat-content.stories.mjs +++ b/browser/components/aiwindow/ui/components/ai-chat-content/ai-chat-content.stories.mjs @@ -1,6 +1,6 @@ /* 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/. */ + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { html } from "chrome://global/content/vendor/lit.all.mjs"; import "chrome://browser/content/aiwindow/components/ai-chat-content.mjs"; @@ -9,9 +9,19 @@ export default { title: "Domain-specific UI Widgets/AI Window/AI Chat Content", component: "ai-chat-content", argTypes: { - conversationState: { - control: { type: "object" }, - }, + conversationState: { control: { type: "object" } }, + }, + parameters: { + fluent: ` +aiwindow-memories-used = + .label = Memories used +aiwindow-retry-without-memories = + .label = Retry without memories +aiwindow-retry = + .tooltiptext = Retry +aiwindow-copy-message = + .tooltiptext = Copy + `, }, }; @@ -20,31 +30,34 @@ const Template = ({ conversationState }) => html` `; export const Empty = Template.bind({}); -Empty.args = { - conversationState: [], -}; +Empty.args = { conversationState: [] }; export const SingleUserMessage = Template.bind({}); SingleUserMessage.args = { conversationState: [ - { role: "user", content: "What is the weather like today?" }, + { role: "user", body: "What is the weather like today?" }, ], }; export const Conversation = Template.bind({}); Conversation.args = { conversationState: [ - { role: "user", content: "Test: What is the weather like today?" }, + { role: "user", body: "What is the weather like today?" }, { role: "assistant", - content: - "Test: I don't have access to real-time weather data, but I can help you with other tasks!", + messageId: "a1", + body: "I don't have access to real-time weather data, but I can help you with other tasks!", + appliedMemories: [], }, - { role: "user", content: "Test: Can you help me with coding?" }, + { role: "user", body: "Can you help me with coding?" }, { role: "assistant", - content: - "Test: Yes, I can help you with coding! What programming language or problem are you working on?", + messageId: "a2", + body: "Yes, I can help you with coding! What programming language or problem are you working on?", + appliedMemories: [ + "Looking for help with coding", + "Looking for real time weather data", + ], }, ], }; diff --git a/browser/components/aiwindow/ui/components/ai-chat-content/chat-assistant-footer/applied-memories-button.css b/browser/components/aiwindow/ui/components/ai-chat-content/chat-assistant-footer/applied-memories-button.css new file mode 100644 index 0000000000000..e5859c64092f3 --- /dev/null +++ b/browser/components/aiwindow/ui/components/ai-chat-content/chat-assistant-footer/applied-memories-button.css @@ -0,0 +1,157 @@ +/* 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/. */ + +:host { + --aiwindow-text-accent: var(--color-violet-90); + --aiwindow-border-subtle: var(--color-gray-20); + --aiwindow-memory-item-bg: var(--color-gray-05); + --memories-accent-bg-hover: rgba(191, 143, 204, 0.2); + --memories-accent-bg-active: rgba(191, 143, 204, 0.26); + --memories-accent-border: rgba(0, 0, 0, 0); +} + +:host([data-open]) moz-button.memories-trigger { + /* stylelint-disable-next-line */ + color: var(--aiwindow-text-accent); + --button-background-color-ghost: var(--memories-accent-bg-hover); + --button-border-color-ghost: var(--memories-accent-border); + --button-background-color-ghost-selected: var(--memories-accent-bg-hover); + --button-border-color-ghost-selected: var(--memories-accent-border); + --button-text-color-ghost-selected: currentColor; + --button-background-color-ghost-hover: var(--memories-accent-bg-hover); + --button-border-color-ghost-hover: var(--memories-accent-border); +} + +moz-button.memories-trigger { + color: var(--text-color-deemphasized, rgba(21, 20, 26, 0.69)); + --button-padding: var(--space-small); + --button-background-color-ghost-hover: var(--memories-accent-bg-hover); + --button-border-color-ghost-hover: var(--memories-accent-border); + --button-background-color-ghost-active: var(--memories-accent-bg-active); + --button-border-color-ghost-active: var(--memories-accent-border); +} + +moz-button.memories-trigger::part(button) { + padding: var(--button-padding-block, var(--space-xsmall)) var(--space-xsmall); + gap: var(--space-xsmall); +} + +moz-button.memories-trigger[disabled] { + opacity: 0.6; +} + +moz-button.memories-trigger::part(moz-button-label) { + white-space: nowrap; + overflow: hidden; + opacity: 0; + max-width: 0; + transition: + opacity 120ms ease, + max-width 120ms ease; + font-size: var(--font-size-small, 13px); +} + +moz-button.memories-trigger:not([disabled]):hover::part(moz-button-label), +:host([data-open]) moz-button.memories-trigger::part(moz-button-label) { + opacity: 1; + max-width: 12em; +} + +.popover { + opacity: 0; + pointer-events: none; + position: absolute; + /* stylelint-disable-next-line */ + width: 318px; + /* stylelint-disable-next-line */ + bottom: 40px; + left: 0; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: var(--space-medium); + padding: var(--space-large); + border-radius: var(--border-radius-medium); + border-width: var(--border-width); + border-style: solid; + /* stylelint-disable-next-line */ + border-color: var(--aiwindow-border-subtle); + background: var(--color-white, white); + box-shadow: var(--box-shadow-level-3); + transition: + opacity 120ms ease, + transform 120ms ease; +} + +.popover.open { + opacity: 1; + pointer-events: auto; +} + +@media (prefers-reduced-motion: reduce) { + .popover { + transition: none; + } +} + +.memories-list { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: var(--space-xsmall); +} + +.memories-list-item { + display: flex; + /* stylelint-disable-next-line */ + width: 306px; + height: var(--size-item-large); + padding: var(--space-xsmall) var(--space-xsmall) var(--space-xsmall) var(--space-small); + justify-content: space-between; + align-items: center; + border-radius: var(--border-radius-medium); + /* stylelint-disable-next-line */ + background-color: var(--aiwindow-memory-item-bg); +} + +.memories-list-label { + font-size: var(--font-size-small, 13px); + font-weight: var(--font-weight-semibold); + overflow: hidden; + text-overflow: ellipsis; +} + +.memories-remove-button { + display: none; +} + +.memories-list-item:hover .memories-remove-button { + display: inline-flex; +} + +.retry-row { + padding: 0 var(--space-xsmall); + display: flex; + align-items: center; + gap: var(--space-xsmall); + font-size: var(--font-size-small, 13px); + cursor: pointer; +} + +.retry-row-button { + border: none; + height: var(--size-item-large); + padding: 0; + margin: 0; + background: transparent; + font: inherit; + cursor: pointer; + display: inline-flex; + align-items: center; + gap: var(--space-xsmall); + --button-padding: var(--space-xsmall); + --button-font-weight: var(--button-font-weight); +} diff --git a/browser/components/aiwindow/ui/components/ai-chat-content/chat-assistant-footer/applied-memories-button.mjs b/browser/components/aiwindow/ui/components/ai-chat-content/chat-assistant-footer/applied-memories-button.mjs new file mode 100644 index 0000000000000..67930ea9ed402 --- /dev/null +++ b/browser/components/aiwindow/ui/components/ai-chat-content/chat-assistant-footer/applied-memories-button.mjs @@ -0,0 +1,248 @@ +/* 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 { MozLitElement } from "chrome://global/content/lit-utils.mjs"; +import { html, nothing } from "chrome://global/content/vendor/lit.all.mjs"; + +/** + * AppliedMemoriesButton + * + * TODO: Currently using placeholder "Highlights" icon which will be replaced + * with the memories icon once ready + * + * Custom element that renders the “Memories applied” pill and popover for + * a single assistant message. The popover shows a list of applied + * memories and allows the user to: + * - Remove an individual applied insight. + * - Retry the message without any applied memories. + * + * @property {string|null} messageId + * Identifier for the assistant message this control belongs to. + * + * @property {Array} appliedMemories + * List of applied memories for the message. The component will render up + * to the first 5 items in the popover. + * + * @property {boolean} open + * Whether the popover is currently open. This is typically controlled + * internally when the button is clicked and also reflected via the + * "toggle-applied-memories" event. + * + * Events dispatched: + * - "toggle-applied-memories" + * detail: { messageId, open } + * - "remove-applied-memory" + * detail: { messageId, index, insight } + * - "retry-without-memories" + * detail: { messageId } + */ +export class AppliedMemoriesButton extends MozLitElement { + static properties = { + messageId: { type: String, attribute: "message-id" }, + appliedMemories: { attribute: false }, + open: { type: Boolean, reflect: false }, + }; + + constructor() { + super(); + this.messageId = null; + this.appliedMemories = []; + this.open = false; + + this._onDocumentClick = this._onDocumentClick.bind(this); + } + + connectedCallback() { + super.connectedCallback(); + document.addEventListener("click", this._onDocumentClick); + } + + disconnectedCallback() { + document.removeEventListener("click", this._onDocumentClick); + super.disconnectedCallback(); + } + + get _hasMemories() { + return Array.isArray(this.appliedMemories) && !!this.appliedMemories.length; + } + + get _visibleMemories() { + return this.appliedMemories.slice(0, 5); + } + + #onTriggerClick(event) { + event.stopPropagation(); + if (!this._hasMemories) { + return; + } + + this.open = !this.open; + this.toggleAttribute("data-open", this.open); + + this.dispatchEvent( + new CustomEvent("toggle-applied-memories", { + bubbles: true, + composed: true, + detail: { + messageId: this.messageId, + open: this.open, + }, + }) + ); + } + + _onPopoverClick(event) { + event.stopPropagation(); + } + + _onDocumentClick() { + if (!this.open) { + return; + } + this.open = false; + this.toggleAttribute("data-open", false); + this.requestUpdate(); + + this.dispatchEvent( + new CustomEvent("toggle-applied-memories", { + bubbles: true, + composed: true, + detail: { + messageId: this.messageId, + open: false, + }, + }) + ); + } + + _onRemoveInsight(event, index) { + event.stopPropagation(); + + if (!Array.isArray(this.appliedMemories)) { + return; + } + + const insight = this.appliedMemories[index]; + + // Remove insight visually, but update will be done by parent + this.appliedMemories = this.appliedMemories.filter((_, i) => { + return i !== index; + }); + + this.dispatchEvent( + new CustomEvent("remove-applied-memory", { + bubbles: true, + composed: true, + detail: { + messageId: this.messageId, + index, + insight, + }, + }) + ); + } + + _onRetryWithoutMemories(event) { + event.stopPropagation(); + + this.dispatchEvent( + new CustomEvent("retry-without-memories", { + bubbles: true, + composed: true, + detail: { + messageId: this.messageId, + }, + }) + ); + } + + // TODO: Update formatting function once shape of memories passed is confirmed + _formatInsightLabel(insight) { + if (typeof insight === "string") { + return insight; + } + return ""; + } + + renderPopover() { + if (!this._hasMemories) { + return nothing; + } + + const isOpen = this.open; + const visibleMemories = this._visibleMemories; + + return html` +
this._onPopoverClick(event)} + > +
    + ${visibleMemories.map((insight, index) => { + const label = this._formatInsightLabel(insight); + if (!label) { + return nothing; + } + return html` +
  • + ${label} + this._onRemoveInsight(event, index)} + > +
  • + `; + })} +
+ +
+ +
+
+ `; + } + + render() { + if (!this._hasMemories) { + return null; + } + + return html` + + this.#onTriggerClick(event)} + > + + ${this.renderPopover()} + `; + } +} + +customElements.define("applied-memories-button", AppliedMemoriesButton); diff --git a/browser/components/aiwindow/ui/components/ai-chat-content/chat-assistant-footer/assistant-message-footer.css b/browser/components/aiwindow/ui/components/ai-chat-content/chat-assistant-footer/assistant-message-footer.css new file mode 100644 index 0000000000000..4c5232474bf4c --- /dev/null +++ b/browser/components/aiwindow/ui/components/ai-chat-content/chat-assistant-footer/assistant-message-footer.css @@ -0,0 +1,27 @@ +/* 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/. */ + +:host { + --chat-assistant-footer-button-padding: var(--space-xsmall); + --chat-assistant-footer-button-bg-hover: rgba(191, 143, 204, 0.2); + --chat-assistant-footer-button-bg-active: rgba(191, 143, 204, 0.26); + --chat-assistant-footer-button-border-hover: rgba(0, 0, 0, 0); + --chat-assistant-footer-button-border-active: rgba(0, 0, 0, 0); +} + +.footer { + position: relative; + display: inline-flex; + align-items: center; + gap: var(--space-xsmall); +} + +moz-button.footer-icon-button { + --button-icon-fill: var(--color-gray-70); + --button-padding: var(--chat-assistant-footer-button-padding); + --button-background-color-ghost-hover: var(--chat-assistant-footer-button-bg-hover); + --button-border-color-ghost-hover: var(--chat-assistant-footer-button-border-hover); + --button-background-color-ghost-active: var(--chat-assistant-footer-button-bg-active); + --button-border-color-ghost-active: var(--chat-assistant-footer-button-border-active); +} diff --git a/browser/components/aiwindow/ui/components/ai-chat-content/chat-assistant-footer/assistant-message-footer.mjs b/browser/components/aiwindow/ui/components/ai-chat-content/chat-assistant-footer/assistant-message-footer.mjs new file mode 100644 index 0000000000000..303b3e3117f6c --- /dev/null +++ b/browser/components/aiwindow/ui/components/ai-chat-content/chat-assistant-footer/assistant-message-footer.mjs @@ -0,0 +1,154 @@ +/* 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 { MozLitElement } from "chrome://global/content/lit-utils.mjs"; +import { html } from "chrome://global/content/vendor/lit.all.mjs"; +// eslint-disable-next-line import/no-unassigned-import +import "chrome://browser/content/aiwindow/components/applied-memories-button.mjs"; + +/** + * AssistantMessageFooter + * + * TODO: Currently using placeholder "Edit Copy" icon which will be replaced + * with the copy icon once ready + * + * Custom element that renders the footer controls for an assistant message + * in the AI Window chat UI. The footer includes: + * - A copy button for copying the assistant response. + * - A retry button for regenerating the response. + * - An applied memories button for viewing and/or deleting applied memories. + * + * Data updates and network behavior are controlled by its parent. + * + * @property {string|null} messageId + * Identifier of the assistant message this footer is associated with. + * + * @property {Array} appliedMemories + * List of applied memories for the message. Passed through to the + * child. + * + * Events dispatched: + * - "copy-message" + * detail: { messageId } + * - "retry-message" + * detail: { messageId } + * - "retry-without-memories" + * detail: { messageId } + * - "remove-applied-memory" + * (re-dispatched from the applied memories button) + * detail: { messageId, index, memory } + * - "toggle-applied-memories" + * (re-dispatched from the applied memories button) + * detail: { messageId, open } + */ +export class AssistantMessageFooter extends MozLitElement { + static properties = { + messageId: { type: String, attribute: "message-id" }, + appliedMemories: { attribute: false }, + }; + + constructor() { + super(); + this.messageId = null; + this.appliedMemories = []; + } + + static eventBehaviors = { + bubbles: true, + composed: true, + }; + + static get events() { + return { + copy: "copy-message", + retry: "retry-message", + toggleMemories: "toggle-applied-memories", + removeMemory: "remove-applied-memory", + retryWithoutMemories: "retry-without-memories", + }; + } + + #emit(type, detail) { + this.dispatchEvent( + new CustomEvent(type, { + ...this.constructor.eventBehaviors, + ...(detail !== undefined ? { detail } : {}), + }) + ); + } + + #emitCopy() { + this.#emit(this.constructor.events.copy, { messageId: this.messageId }); + } + + #emitRetry() { + this.#emit(this.constructor.events.retry, { messageId: this.messageId }); + } + + #onAppliedMemoriesToggle(event) { + this.#emit(this.constructor.events.toggleMemories, event.detail); + } + + #onRemoveAppliedMemory(event) { + this.#emit(this.constructor.events.removeMemory, event.detail); + } + + #onRetryWithoutMemories(event) { + this.#emit( + this.constructor.events.retryWithoutMemories, + event.detail ?? { messageId: this.messageId } + ); + } + + render() { + return html` + + + `; + } +} + +customElements.define("assistant-message-footer", AssistantMessageFooter); diff --git a/browser/components/aiwindow/ui/components/ai-chat-message/ai-chat-message.mjs b/browser/components/aiwindow/ui/components/ai-chat-message/ai-chat-message.mjs index ac188c7a1e607..c8c720898a9d3 100644 --- a/browser/components/aiwindow/ui/components/ai-chat-message/ai-chat-message.mjs +++ b/browser/components/aiwindow/ui/components/ai-chat-message/ai-chat-message.mjs @@ -55,7 +55,7 @@ export class AIChatMessage extends MozLitElement { />
-
+
${this.message}
diff --git a/browser/components/aiwindow/ui/components/ai-chat-message/ai-chat-message.stories.mjs b/browser/components/aiwindow/ui/components/ai-chat-message/ai-chat-message.stories.mjs index b36ad00ab9aba..78d9e52161326 100644 --- a/browser/components/aiwindow/ui/components/ai-chat-message/ai-chat-message.stories.mjs +++ b/browser/components/aiwindow/ui/components/ai-chat-message/ai-chat-message.stories.mjs @@ -20,7 +20,7 @@ export default { }; const Template = ({ role, content }) => html` - + `; export const UserMessage = Template.bind({}); diff --git a/browser/components/aiwindow/ui/components/ai-window/ai-window.mjs b/browser/components/aiwindow/ui/components/ai-window/ai-window.mjs index 5ec45851dbd17..3777c2dd1c411 100644 --- a/browser/components/aiwindow/ui/components/ai-window/ai-window.mjs +++ b/browser/components/aiwindow/ui/components/ai-window/ai-window.mjs @@ -29,23 +29,34 @@ ChromeUtils.defineLazyGetter(lazy, "log", function () { }); }); +const FULLPAGE = "fullpage"; +const SIDEBAR = "sidebar"; + /** * A custom element for managing AI Window */ export class AIWindow extends MozLitElement { static properties = { userPrompt: { type: String }, + mode: { type: String }, // sidebar | fullpage }; #browser; #conversation; + #detectModeFromContext() { + return window.browsingContext?.embedderElement?.id === "ai-window-browser" + ? SIDEBAR + : FULLPAGE; + } + constructor() { super(); this.userPrompt = ""; this.#browser = null; this.#conversation = new lazy.ChatConversation({}); + this.mode = this.#detectModeFromContext(); } connectedCallback() { @@ -251,6 +262,11 @@ export class AIWindow extends MozLitElement { Submit mock prompt + + + ${this.mode === FULLPAGE + ? html`
Fullpage Footer Content
` + : ""}
`; } diff --git a/browser/components/aiwindow/ui/content/firstrun.js b/browser/components/aiwindow/ui/content/firstrun.js index 42107f3fdcb2c..42cd10126df4a 100644 --- a/browser/components/aiwindow/ui/content/firstrun.js +++ b/browser/components/aiwindow/ui/content/firstrun.js @@ -202,6 +202,14 @@ function renderFirstRun() { window.location.href = lazy.AIWindow.newTabURL; }; + window.addEventListener( + "unload", + () => { + AWParent.didDestroy(); + }, + { once: true } + ); + const script = document.createElement("script"); script.src = "chrome://browser/content/aboutwelcome/aboutwelcome.bundle.js"; document.body.appendChild(script); diff --git a/browser/components/aiwindow/ui/jar.mn b/browser/components/aiwindow/ui/jar.mn index a66ea456aad49..ef2a13fb26ba6 100644 --- a/browser/components/aiwindow/ui/jar.mn +++ b/browser/components/aiwindow/ui/jar.mn @@ -5,6 +5,7 @@ browser.jar: content/browser/aiwindow/aiChatContent.html (content/aiChatContent.html) content/browser/aiwindow/aiWindow.html (content/aiWindow.html) + content/browser/aiwindow/assets/input-cta-arrow-icon.svg (assets/input-cta-arrow-icon.svg) content/browser/aiwindow/components/ai-chat-content.mjs (components/ai-chat-content/ai-chat-content.mjs) content/browser/aiwindow/components/ai-chat-content.css (components/ai-chat-content/ai-chat-content.css) @@ -23,3 +24,7 @@ browser.jar: content/browser/aiwindow/firstrun.html (content/firstrun.html) content/browser/aiwindow/firstrun.css (content/firstrun.css) content/browser/aiwindow/firstrun.js (content/firstrun.js) + content/browser/aiwindow/components/applied-memories-button.mjs (components/ai-chat-content/chat-assistant-footer/applied-memories-button.mjs) + content/browser/aiwindow/components/applied-memories-button.css (components/ai-chat-content/chat-assistant-footer/applied-memories-button.css) + content/browser/aiwindow/components/assistant-message-footer.mjs (components/ai-chat-content/chat-assistant-footer/assistant-message-footer.mjs) + content/browser/aiwindow/components/assistant-message-footer.css (components/ai-chat-content/chat-assistant-footer/assistant-message-footer.css) diff --git a/browser/components/aiwindow/ui/modules/AIWindow.sys.mjs b/browser/components/aiwindow/ui/modules/AIWindow.sys.mjs index b55b28e53321c..f591e3b0934c3 100644 --- a/browser/components/aiwindow/ui/modules/AIWindow.sys.mjs +++ b/browser/components/aiwindow/ui/modules/AIWindow.sys.mjs @@ -5,8 +5,10 @@ */ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; -const AIWINDOW_URL = "chrome://browser/content/aiwindow/aiWindow.html"; +export const AIWINDOW_URL = "chrome://browser/content/aiwindow/aiWindow.html"; const AIWINDOW_URI = Services.io.newURI(AIWINDOW_URL); +const FIRSTRUN_URL = "chrome://browser/content/aiwindow/firstrun.html"; +const FIRSTRUN_URI = Services.io.newURI(FIRSTRUN_URL); const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { @@ -229,7 +231,9 @@ export const AIWindow = { * @returns {boolean} whether AI Window content page is active */ isAIWindowContentPage(uri) { - return AIWINDOW_URI.equalsExceptRef(uri); + return ( + AIWINDOW_URI.equalsExceptRef(uri) || FIRSTRUN_URI.equalsExceptRef(uri) + ); }, /** diff --git a/browser/components/aiwindow/ui/modules/AIWindowUI.sys.mjs b/browser/components/aiwindow/ui/modules/AIWindowUI.sys.mjs index 707795a7ba33a..e500015ea4068 100644 --- a/browser/components/aiwindow/ui/modules/AIWindowUI.sys.mjs +++ b/browser/components/aiwindow/ui/modules/AIWindowUI.sys.mjs @@ -4,8 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const AIWINDOW_SIDEBAR_URL = - "chrome://browser/content/aiwindow/aiWindow.html#mode=sidebar"; +import { AIWINDOW_URL } from "moz-src:///browser/components/aiwindow/ui/modules/AIWindow.sys.mjs"; export const AIWindowUI = { BOX_ID: "ai-window-box", @@ -60,7 +59,7 @@ export const AIWindowUI = { browser.setAttribute("disablehistory", "true"); browser.setAttribute("disablefullscreen", "true"); browser.setAttribute("tooltip", "aHTMLTooltip"); - browser.setAttribute("src", AIWINDOW_SIDEBAR_URL); + browser.setAttribute("src", AIWINDOW_URL); stack.appendChild(browser); return browser; }, diff --git a/browser/components/aiwindow/ui/modules/ChatConversation.sys.mjs b/browser/components/aiwindow/ui/modules/ChatConversation.sys.mjs index c3e94e17721c7..cd7e4c82defef 100644 --- a/browser/components/aiwindow/ui/modules/ChatConversation.sys.mjs +++ b/browser/components/aiwindow/ui/modules/ChatConversation.sys.mjs @@ -234,6 +234,8 @@ export class ChatConversation { */ async generatePrompt(prompt, pageUrl) { if (!this.#messages.length) { + // TODO: Bug 2008865 + // switch to use remote settings prompt accessed via engine.loadPrompt(feature) this.addSystemMessage(SYSTEM_PROMPT_TYPE.TEXT, assistantPrompt); } diff --git a/browser/components/aiwindow/ui/test/browser/browser.toml b/browser/components/aiwindow/ui/test/browser/browser.toml index 494b8d007d628..8dbc957f80b12 100644 --- a/browser/components/aiwindow/ui/test/browser/browser.toml +++ b/browser/components/aiwindow/ui/test/browser/browser.toml @@ -1,8 +1,11 @@ [DEFAULT] +prefs = ["browser.newtab.preload=false"] support-files = [ "head.js", "test_chat_search_button.html", "test_chat_search_button.mjs", + "test_assistant_message_footer_page.html", + "test_applied_memories_page.html", ] ["browser_actor_user_prompt.js"] @@ -11,6 +14,10 @@ support-files = [ ["browser_aichat_message.js"] +["browser_aiwindow_applied_memories_button.js"] + +["browser_aiwindow_assistant_message_footer.js"] + ["browser_aiwindow_firstrun.js"] ["browser_aiwindow_integration.js"] diff --git a/browser/components/aiwindow/ui/test/browser/browser_aiwindow_applied_memories_button.js b/browser/components/aiwindow/ui/test/browser/browser_aiwindow_applied_memories_button.js new file mode 100644 index 0000000000000..673093ac6f25d --- /dev/null +++ b/browser/components/aiwindow/ui/test/browser/browser_aiwindow_applied_memories_button.js @@ -0,0 +1,70 @@ +"use strict"; + +const TEST_PAGE = + "chrome://mochitests/content/browser/browser/components/aiwindow/ui/test/browser/test_applied_memories_page.html"; + +add_task(async function test_applied_memories_button_basic() { + await BrowserTestUtils.withNewTab(TEST_PAGE, async browser => { + await SpecialPowers.spawn(browser, [], async () => { + const doc = content.document; + const button = doc.getElementById("test-button"); + + button.messageId = "msg-1"; + button.appliedMemories = ["User is vegan", "User has a cat"]; + + await content.customElements.whenDefined("applied-memories-button"); + + let popover = button.shadowRoot.querySelector(".popover"); + ok(popover, "Popover element exists"); + ok(!popover.classList.contains("open"), "Popover is initially closed"); + + const trigger = button.shadowRoot.querySelector( + "moz-button.memories-trigger" + ); + ok(trigger, "Found memories trigger"); + + trigger.click(); + await content.Promise.resolve(); + + popover = button.shadowRoot.querySelector(".popover"); + ok( + popover.classList.contains("open"), + "Popover opens after trigger click" + ); + + const items = button.shadowRoot.querySelectorAll(".memories-list-item"); + is(items.length, 2, "Two memories rendered initially"); + + const removeButton = items[0].querySelector(".memories-remove-button"); + ok(removeButton, "Found remove button for first memory"); + + let removeEventDetail = null; + function onRemove(evt) { + button.removeEventListener("remove-applied-memory", onRemove); + removeEventDetail = evt.detail; + } + button.addEventListener("remove-applied-memory", onRemove); + + removeButton.click(); + await content.Promise.resolve(); + + const itemsAfter = button.shadowRoot.querySelectorAll( + ".memories-list-item" + ); + is(itemsAfter.length, 1, "One memory remains after removal"); + + ok(removeEventDetail, "remove-applied-memory event fired"); + is(removeEventDetail.messageId, "msg-1", "Event includes messageId"); + is(removeEventDetail.index, 0, "Event index is 0"); + + doc.body.click(); + await content.Promise.resolve(); + + popover = button.shadowRoot.querySelector(".popover"); + ok( + !popover.classList.contains("open"), + "Popover closes on outside click" + ); + }); + }); +}); diff --git a/browser/components/aiwindow/ui/test/browser/browser_aiwindow_assistant_message_footer.js b/browser/components/aiwindow/ui/test/browser/browser_aiwindow_assistant_message_footer.js new file mode 100644 index 0000000000000..f33c55bcc7f3d --- /dev/null +++ b/browser/components/aiwindow/ui/test/browser/browser_aiwindow_assistant_message_footer.js @@ -0,0 +1,66 @@ +"use strict"; + +const TEST_PAGE = + "chrome://mochitests/content/browser/browser/components/aiwindow/ui/test/browser/test_assistant_message_footer_page.html"; + +add_task(async function test_message_footer_wires_buttons() { + await BrowserTestUtils.withNewTab(TEST_PAGE, async browser => { + await SpecialPowers.spawn(browser, [], async () => { + const doc = content.document; + const footer = doc.getElementById("footer"); + + footer.messageId = "msg-1"; + footer.appliedMemories = ["User is vegan"]; + + await content.customElements.whenDefined("assistant-message-footer"); + + const shadow = footer.shadowRoot; + ok(shadow, "Footer has a shadow root"); + + const copyButton = shadow.querySelector("moz-button.copy-button"); + const retryButton = shadow.querySelector("moz-button.retry-button"); + const appliedButton = shadow.querySelector("applied-memories-button"); + + ok(copyButton, "Found copy button"); + ok(retryButton, "Found retry button"); + ok(appliedButton, "Found applied memories button"); + + is( + appliedButton.messageId, + "msg-1", + "Footer passes messageId to applied memories button" + ); + is( + appliedButton.appliedMemories.length, + 1, + "Footer passes appliedMemories to applied memories button" + ); + + let copyDetail = null; + function onCopy(evt) { + footer.removeEventListener("copy-message", onCopy); + copyDetail = evt.detail; + } + footer.addEventListener("copy-message", onCopy); + + copyButton.click(); + await content.Promise.resolve(); + + ok(copyDetail, "copy-message event fired"); + is(copyDetail.messageId, "msg-1", "copy-message includes messageId"); + + let retryDetail = null; + function onRetry(evt) { + footer.removeEventListener("retry-message", onRetry); + retryDetail = evt.detail; + } + footer.addEventListener("retry-message", onRetry); + + retryButton.click(); + await content.Promise.resolve(); + + ok(retryDetail, "retry-message event fired"); + is(retryDetail.messageId, "msg-1", "retry-message includes messageId"); + }); + }); +}); diff --git a/browser/components/aiwindow/ui/test/browser/browser_aiwindow_transparency.js b/browser/components/aiwindow/ui/test/browser/browser_aiwindow_transparency.js index 5b2f7b5ad2d07..8453df7fe8da5 100644 --- a/browser/components/aiwindow/ui/test/browser/browser_aiwindow_transparency.js +++ b/browser/components/aiwindow/ui/test/browser/browser_aiwindow_transparency.js @@ -3,6 +3,8 @@ "use strict"; +const FIRSTRUN_URL = "chrome://browser/content/aiwindow/firstrun.html"; + /** * Checks if browser has transparent attribute * @@ -68,6 +70,27 @@ add_task(async function test_transparency_on_new_tab() { gAIWindow.gBrowser.removeTab(newTab); }); +add_task(async function test_transparency_on_firstrun_page() { + const newTab = await BrowserTestUtils.openNewForegroundTab( + gAIWindow.gBrowser, + FIRSTRUN_URL + ); + const newBrowser = gAIWindow.gBrowser.getBrowserForTab(newTab); + + Assert.ok( + isBrowserTransparent(newBrowser), + "Browser should be transparent on new firstrun page" + ); + + Assert.equal( + newBrowser.currentURI.spec, + FIRSTRUN_URL, + "New tab should be on firstrun URL" + ); + + gAIWindow.gBrowser.removeTab(newTab); +}); + add_task(async function test_transparency_removed_on_navigation() { gReusableTab = await BrowserTestUtils.openNewForegroundTab( gAIWindow.gBrowser, diff --git a/browser/components/aiwindow/ui/test/browser/browser_open_aiwindow.js b/browser/components/aiwindow/ui/test/browser/browser_open_aiwindow.js index 22b7d3ed42a2f..8477264288696 100644 --- a/browser/components/aiwindow/ui/test/browser/browser_open_aiwindow.js +++ b/browser/components/aiwindow/ui/test/browser/browser_open_aiwindow.js @@ -190,6 +190,38 @@ add_task(async function test_openNewBrowserWindow_and_ai_inherit() { await SpecialPowers.popPrefEnv(); }); +/** + * Test AI window mode detection in aiWindow.html + */ +add_task(async function test_aiwindow_html_mode_detection() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.aiwindow.enabled", true]], + }); + + // Open aiWindow.html directly + await BrowserTestUtils.withNewTab( + "chrome://browser/content/aiwindow/aiWindow.html", + async browser => { + await SpecialPowers.spawn(browser, [], async () => { + await content.customElements.whenDefined("ai-window"); + + const aiWindowElement = content.document.querySelector("ai-window"); + Assert.ok(aiWindowElement, "ai-window element should exist"); + + // Check that mode is detected (should be FULLPAGE when loaded directly) + info(`aiWindowElement.mode: ${aiWindowElement.mode}`); + Assert.strictEqual( + aiWindowElement.mode, + "fullpage", + `Mode should be detected as fullpage, got: ${aiWindowElement.mode}` + ); + }); + } + ); + + await SpecialPowers.popPrefEnv(); +}); + function checkMenuItemVisibility( aiWindowEnabled, aiOpenerButton, diff --git a/browser/components/aiwindow/ui/test/browser/test_applied_memories_page.html b/browser/components/aiwindow/ui/test/browser/test_applied_memories_page.html new file mode 100644 index 0000000000000..4eba01f11e5d3 --- /dev/null +++ b/browser/components/aiwindow/ui/test/browser/test_applied_memories_page.html @@ -0,0 +1,15 @@ + + + + + + AI Window applied memories button test + + + + + + + diff --git a/browser/components/aiwindow/ui/test/browser/test_assistant_message_footer_page.html b/browser/components/aiwindow/ui/test/browser/test_assistant_message_footer_page.html new file mode 100644 index 0000000000000..ca50e814d2439 --- /dev/null +++ b/browser/components/aiwindow/ui/test/browser/test_assistant_message_footer_page.html @@ -0,0 +1,15 @@ + + + + + + AI Window assistant message footer test + + + + + + + diff --git a/browser/components/asrouter/content-src/schemas/make-schemas.py b/browser/components/asrouter/content-src/schemas/make-schemas.py index 570f27ba6e30c..f4b325a1f0937 100755 --- a/browser/components/asrouter/content-src/schemas/make-schemas.py +++ b/browser/components/asrouter/content-src/schemas/make-schemas.py @@ -262,13 +262,11 @@ def dfn_filter(name): # patch_schema mutates the given schema, so we read a new copy in for # each bundle operation. - defs.update( - { - name: dfn - for name, dfn in common_schema["$defs"].items() - if dfn_filter(name) - } - ) + defs.update({ + name: dfn + for name, dfn in common_schema["$defs"].items() + if dfn_filter(name) + }) # Ensure all bundled schemas have an $id so that $refs inside the # bundled schema work correctly (i.e, they will reference the subschema diff --git a/browser/components/backup/tests/marionette/test_backup.py b/browser/components/backup/tests/marionette/test_backup.py index 924c36779cd13..402af08ab54ea 100644 --- a/browser/components/backup/tests/marionette/test_backup.py +++ b/browser/components/backup/tests/marionette/test_backup.py @@ -21,19 +21,17 @@ def setUp(self): # We need to force the service to be enabled because it's disabled # by default for Marionette. Also "browser.backup.log" has to be set # to true before Firefox starts in order for it to be displayed. - self.marionette.enforce_gecko_prefs( - { - "browser.backup.enabled": True, - "browser.backup.log": True, - "browser.backup.archive.enabled": True, - "browser.backup.restore.enabled": True, - "browser.backup.archive.overridePlatformCheck": True, - "browser.backup.restore.overridePlatformCheck": True, - # Necessary to test Session Restore from backup, which relies on - # the crash restore mechanism. - "browser.sessionstore.resume_from_crash": True, - } - ) + self.marionette.enforce_gecko_prefs({ + "browser.backup.enabled": True, + "browser.backup.log": True, + "browser.backup.archive.enabled": True, + "browser.backup.restore.enabled": True, + "browser.backup.archive.overridePlatformCheck": True, + "browser.backup.restore.overridePlatformCheck": True, + # Necessary to test Session Restore from backup, which relies on + # the crash restore mechanism. + "browser.sessionstore.resume_from_crash": True, + }) self.marionette.set_context("chrome") diff --git a/browser/components/controlcenter/content/trustPanel.inc.xhtml b/browser/components/controlcenter/content/trustPanel.inc.xhtml index b429d757632bf..a39a273ff3d10 100644 --- a/browser/components/controlcenter/content/trustPanel.inc.xhtml +++ b/browser/components/controlcenter/content/trustPanel.inc.xhtml @@ -34,13 +34,13 @@ - - diff --git a/browser/components/moz.build b/browser/components/moz.build index 7aa821485139c..a2d8b39044f31 100644 --- a/browser/components/moz.build +++ b/browser/components/moz.build @@ -58,6 +58,7 @@ DIRS += [ "prompts", "protections", "protocolhandler", + "qrcode", "reportbrokensite", "resistfingerprinting", "screenshots", diff --git a/browser/components/newtab/metrics.yaml b/browser/components/newtab/metrics.yaml index 39852812c1c66..71e087872772a 100644 --- a/browser/components/newtab/metrics.yaml +++ b/browser/components/newtab/metrics.yaml @@ -2023,6 +2023,28 @@ pocket: send_in_pings: - newtab + spoc_placeholder_duration: + type: timing_distribution + time_unit: millisecond + description: > + Time in milliseconds that a placeholder for a sponsored story (spoc) is + visible to the user before being replaced with actual sponsored content. + This measures how long users see loading placeholders when spocs need to + be fetched, which can happen during startup, cache expiration, or other + loading scenarios. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1993586 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1993586 + data_sensitivity: + - technical + notification_emails: + - mcrawford@mozilla.com + expires: never + send_in_pings: + - newtab + + shim: type: text lifetime: ping diff --git a/browser/components/preferences/OnDeviceModelManager.mjs b/browser/components/preferences/OnDeviceModelManager.mjs new file mode 100644 index 0000000000000..836d702ff2797 --- /dev/null +++ b/browser/components/preferences/OnDeviceModelManager.mjs @@ -0,0 +1,95 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * @import { ModelHub } from "chrome://global/content/ml/ModelHub.sys.mjs" + */ + +/** + * Helpers for managing the install and uninstall of on-device AI models. This + * could be a .sys.mjs module if that makes more sense, but any feature-specific + * helpers could be imported here too. + */ + +const XPCOMUtils = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +).XPCOMUtils; +const lazy = XPCOMUtils.declareLazy({ + ModelHub: "chrome://global/content/ml/ModelHub.sys.mjs", + log: () => + console.createInstance({ + prefix: "OnDeviceModelManager", + maxLogLevel: "Info", + }), +}); + +/** @typedef {typeof OnDeviceModelFeatures[keyof typeof OnDeviceModelFeatures]} OnDeviceModelFeaturesEnum */ + +/** + * Features that support on-device AI models. + */ +const OnDeviceModelFeatures = Object.freeze({ + // NOTE: Feel free to change the values here to whatever makes sense. + TabGroups: "tabgroups", + KeyPoints: "keypoints", + PdfAltText: "pdfalttext", +}); + +export const OnDeviceModelManager = { + features: OnDeviceModelFeatures, + + /** @type {ModelHub} */ + _modelHub: null, + get modelHub() { + if (!this._modelHub) { + this._modelHub = new lazy.ModelHub(); + } + return this._modelHub; + }, + + /** + * Install the models for a specific feature. This should be used when a user + * explicitly enables a feature, so it's ready when they go to use it. + * + * @param {OnDeviceModelFeaturesEnum} feature The feature key to install. + */ + async install(feature) { + switch (feature) { + case OnDeviceModelFeatures.TabGroups: + lazy.log.info("install TabGroups"); + return; + case OnDeviceModelFeatures.KeyPoints: + lazy.log.info("install KeyPoints"); + return; + case OnDeviceModelFeatures.PdfAltText: + lazy.log.info("install PdfAltText"); + return; + default: + throw new Error(`Unknown on-device model feature "${feature}"`); + } + }, + + /** + * Uninstall the models for a specific feature. + * + * @param {OnDeviceModelFeaturesEnum} feature The feature key to uninstall. + */ + async uninstall(feature) { + // TODO: Maybe something like this? + // this.modelHub.deleteFilesByEngine(feature); + switch (feature) { + case OnDeviceModelFeatures.TabGroups: + lazy.log.info("uninstall TabGroups"); + return; + case OnDeviceModelFeatures.KeyPoints: + lazy.log.info("uninstall KeyPoints"); + return; + case OnDeviceModelFeatures.PdfAltText: + lazy.log.info("uninstall PdfAltText"); + return; + default: + throw new Error(`Unknown on-device model feature "${feature}"`); + } + }, +}; diff --git a/browser/components/preferences/config/aiFeatures.mjs b/browser/components/preferences/config/aiFeatures.mjs index e94d803bf2868..24a926d59e88a 100644 --- a/browser/components/preferences/config/aiFeatures.mjs +++ b/browser/components/preferences/config/aiFeatures.mjs @@ -4,15 +4,29 @@ import { Preferences } from "chrome://global/content/preferences/Preferences.mjs"; import { SettingGroupManager } from "chrome://browser/content/preferences/config/SettingGroupManager.mjs"; +import { OnDeviceModelManager } from "chrome://browser/content/preferences/OnDeviceModelManager.mjs"; + +/** + * @import { OnDeviceModelFeaturesEnum } from "chrome://browser/content/preferences/OnDeviceModelManager.mjs" + */ const XPCOMUtils = ChromeUtils.importESModule( "resource://gre/modules/XPCOMUtils.sys.mjs" ).XPCOMUtils; const lazy = XPCOMUtils.declareLazy({ GenAI: "resource:///modules/GenAI.sys.mjs", + log: () => + console.createInstance({ + prefix: "aiFeatures", + maxLogLevel: "Info", + }), }); -Preferences.addAll([{ id: "browser.ml.chat.provider", type: "string" }]); +Preferences.addAll([ + { id: "browser.ml.chat.provider", type: "string" }, + { id: "browser.aiwindow.enabled", type: "bool" }, + { id: "browser.aiwindow.preferences.enabled", type: "bool" }, +]); Preferences.addSetting({ id: "chatbotProviderItem" }); Preferences.addSetting({ @@ -49,8 +63,125 @@ Preferences.addSetting({ }; }, }); +Preferences.addSetting( + /** @type {{ selected: string } & SettingConfig} */ ({ + id: "onDeviceModel", + selected: Object.values(OnDeviceModelManager.features)[0], + getControlConfig(config) { + if (!config.options) { + config.options = Object.entries(OnDeviceModelManager.features).map( + ([key, value]) => ({ + value, + controlAttrs: { label: key }, + }) + ); + } + return config; + }, + get() { + return this.selected; + }, + set(val) { + this.selected = String(val); + }, + }) +); +Preferences.addSetting({ + id: "onDeviceModelInstall", + deps: ["onDeviceModel"], + async onUserClick(_, deps) { + let feature = /** @type {OnDeviceModelFeaturesEnum} */ ( + deps.onDeviceModel.value + ); + lazy.log.info("Will install: ", feature); + await OnDeviceModelManager.install(feature); + lazy.log.info("Done install: ", feature); + }, +}); +Preferences.addSetting({ + id: "onDeviceModelUninstall", + deps: ["onDeviceModel"], + async onUserClick(_, deps) { + let feature = /** @type {OnDeviceModelFeaturesEnum} */ ( + deps.onDeviceModel.value + ); + lazy.log.info("Will uninstall: ", feature); + await OnDeviceModelManager.uninstall(feature); + lazy.log.info("Done uninstall: ", feature); + }, +}); +Preferences.addSetting({ + id: "onDeviceModelUninstallAll", + async onUserClick() { + lazy.log.info("Will uninstall: ALL"); + await Promise.all( + Object.values(OnDeviceModelManager.features).map(feature => + OnDeviceModelManager.uninstall(feature) + ) + ); + lazy.log.info("Done uninstall: ALL"); + }, +}); + +Preferences.addSetting({ + id: "AIWindowEnabled", + pref: "browser.aiwindow.enabled", +}); + +Preferences.addSetting({ + id: "AIWindowPreferencesEnabled", + pref: "browser.aiwindow.preferences.enabled", +}); + +// Only show the feature settings if the prefs are allowed to show and the +// feature isn't enabled. +Preferences.addSetting({ + id: "AIWindowItem", + deps: ["AIWindowEnabled", "AIWindowPreferencesEnabled"], + visible: deps => { + return deps.AIWindowPreferencesEnabled.value && !deps.AIWindowEnabled.value; + }, +}); +Preferences.addSetting({ id: "AIWindowHeader" }); +Preferences.addSetting({ id: "AIWindowActivateLink" }); + +// Only show the AI Window features if the prefs are allowed to show and the +// feature is enabled. +// TODO: Enable when Model and Insight options are added +Preferences.addSetting({ + id: "aiFeaturesAIWindowGroup", + deps: ["AIWindowEnabled", "AIWindowPreferencesEnabled"], + visible: deps => { + return deps.AIWindowPreferencesEnabled.value && deps.AIWindowEnabled.value; + }, +}); SettingGroupManager.registerGroups({ + debugModelManagement: { + l10nId: "debug-model-management-group", + items: [ + { + id: "onDeviceModel", + control: "moz-select", + l10nId: "debug-model-management-feature", + }, + { + id: "onDeviceModelInstall", + control: "moz-button", + l10nId: "debug-model-management-install", + }, + { + id: "onDeviceModelUninstall", + control: "moz-button", + l10nId: "debug-model-management-uninstall", + }, + { + id: "onDeviceModelUninstallAll", + control: "moz-button", + l10nId: "debug-model-management-uninstall-all", + }, + ], + }, aiFeatures: { l10nId: "try-ai-features-group", items: [ @@ -72,6 +203,35 @@ SettingGroupManager.registerGroups({ }, ], }, + { + id: "AIWindowItem", + control: "moz-box-group", + items: [ + { + id: "AIWindowHeader", + l10nId: "try-ai-features-ai-window", + control: "moz-box-item", + }, + { + id: "AIWindowActivateLink", + l10nId: "try-ai-features-ai-window-activate-link", + control: "moz-box-link", + }, + ], + }, + ], + }, + aiWindowFeatures: { + l10nId: "ai-window-features-group", + headingLevel: 2, + items: [ + { + id: "aiFeaturesAIWindowGroup", + control: "moz-box-group", + // TODO: Add Model and Insight list + // options: [ + // ], + }, ], }, }); diff --git a/browser/components/preferences/jar.mn b/browser/components/preferences/jar.mn index ffbc825683d3f..6ea9fdcc18af5 100644 --- a/browser/components/preferences/jar.mn +++ b/browser/components/preferences/jar.mn @@ -25,6 +25,7 @@ browser.jar: content/browser/preferences/web-appearance-light.svg content/browser/preferences/etp-toggle-promo.svg content/browser/preferences/etp-advanced-banner.svg + content/browser/preferences/OnDeviceModelManager.mjs content/browser/preferences/config/aiFeatures.mjs (config/aiFeatures.mjs) content/browser/preferences/config/SettingGroupManager.mjs (config/SettingGroupManager.mjs) content/browser/preferences/widgets/dialog-button.mjs (widgets/dialog-button/dialog-button.mjs) diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js index 9c69ef8df6837..d8e09140b6673 100644 --- a/browser/components/preferences/main.js +++ b/browser/components/preferences/main.js @@ -2850,7 +2850,6 @@ SettingGroupManager.registerGroups({ items: [ { id: "ipProtectionExceptionAllListButton", - l10nId: "ip-protection-site-exceptions-all-sites-button", control: "moz-box-button", }, ], diff --git a/browser/components/preferences/preferences.js b/browser/components/preferences/preferences.js index cd31c00454aa5..a2c9d2eb085f0 100644 --- a/browser/components/preferences/preferences.js +++ b/browser/components/preferences/preferences.js @@ -281,7 +281,7 @@ const CONFIG_PANES = Object.freeze({ }, aiFeatures: { l10nId: "preferences-ai-features-header", - groupIds: ["aiFeatures"], + groupIds: ["debugModelManagement", "aiFeatures", "aiWindowFeatures"], module: "chrome://browser/content/preferences/config/aiFeatures.mjs", visible: () => srdSectionEnabled("aiFeatures"), }, diff --git a/browser/components/preferences/privacy.js b/browser/components/preferences/privacy.js index 69345123860d1..feb32eacea9d3 100644 --- a/browser/components/preferences/privacy.js +++ b/browser/components/preferences/privacy.js @@ -1458,6 +1458,22 @@ Preferences.addSetting({ Preferences.addSetting({ id: "ipProtectionExceptionAllListButton", deps: ["ipProtectionVisible", "ipProtectionSiteExceptionsFeatureEnabled"], + setup(emitChange) { + let permObserver = { + observe(subject, topic, _data) { + if (subject && topic === "perm-changed") { + let permission = subject.QueryInterface(Ci.nsIPermission); + if (permission.type === "ipp-vpn") { + emitChange(); + } + } + }, + }; + Services.obs.addObserver(permObserver, "perm-changed"); + return () => { + Services.obs.removeObserver(permObserver, "perm-changed"); + }; + }, visible: ({ ipProtectionVisible, ipProtectionSiteExceptionsFeatureEnabled, @@ -1478,6 +1494,24 @@ Preferences.addSetting({ params ); }, + getControlConfig(config) { + let l10nId = "ip-protection-site-exceptions-all-sites-button"; + + let savedExceptions = Services.perms.getAllByTypes(["ipp-vpn"]); + let numberOfExclusions = savedExceptions.filter( + perm => perm.capability === Ci.nsIPermissionManager.DENY_ACTION + ).length; + + let l10nArgs = { + count: numberOfExclusions, + }; + + return { + ...config, + l10nId, + l10nArgs, + }; + }, }); Preferences.addSetting({ id: "ipProtectionAutoStartFeatureEnabled", diff --git a/browser/components/preferences/tests/browser_aiFeatures.js b/browser/components/preferences/tests/browser_aiFeatures.js index 7fb2bc4ddf9a9..0ca226da57ced 100644 --- a/browser/components/preferences/tests/browser_aiFeatures.js +++ b/browser/components/preferences/tests/browser_aiFeatures.js @@ -8,10 +8,7 @@ describe("settings ai features", () => { beforeEach(async function setup() { await SpecialPowers.pushPrefEnv({ - set: [ - ["browser.ml.chat.provider", ""], - ["browser.settings-redesign.aiFeatures.enabled", true], - ], + set: [["browser.settings-redesign.aiFeatures.enabled", true]], }); await openPreferencesViaOpenPreferencesAPI("general", { leaveOpen: true }); doc = gBrowser.selectedBrowser.contentDocument; @@ -20,10 +17,21 @@ describe("settings ai features", () => { afterEach(() => { BrowserTestUtils.removeTab(gBrowser.selectedTab); - gBrowser.ownerGlobal.SidebarController.hide(); }); + async function openAiFeaturePanel() { + const paneLoaded = waitForPaneChange("aiFeatures"); + const categoryButton = doc.getElementById("category-ai-features"); + categoryButton.scrollIntoView(); + EventUtils.synthesizeMouseAtCenter(categoryButton, {}, win); + await paneLoaded; + } + it("can change the chatbot provider value", async () => { + await SpecialPowers.pushPrefEnv({ + set: [["browser.ml.chat.provider", ""]], + }); + const categoryButton = doc.getElementById("category-ai-features"); Assert.ok(categoryButton, "category exists"); Assert.ok( @@ -31,10 +39,7 @@ describe("settings ai features", () => { "category is visible" ); - const paneLoaded = waitForPaneChange("aiFeatures"); - categoryButton.scrollIntoView(); - EventUtils.synthesizeMouseAtCenter(categoryButton, {}, win); - await paneLoaded; + await openAiFeaturePanel(); const providerControl = doc.getElementById("chatbotProvider"); Assert.ok(providerControl, "control exists"); @@ -67,5 +72,63 @@ describe("settings ai features", () => { "", "Pref is not empty" ); + + await gBrowser.ownerGlobal.SidebarController.hide(); + }); + + it("hides AI Window when preferences not enabled", async () => { + await SpecialPowers.pushPrefEnv({ + set: [["browser.aiwindow.preferences.enabled", false]], + }); + + await openAiFeaturePanel(); + + const aiWindowItem = doc.getElementById("AIWindowItem"); + const aiWindowFeatures = doc.getElementById("aiFeaturesAIWindowGroup"); + + Assert.ok( + !BrowserTestUtils.isVisible(aiWindowItem), + "AIWindowItem is hidden when preferences not enabled" + ); + Assert.ok( + !BrowserTestUtils.isVisible(aiWindowFeatures), + "aiWindowFeatures is hidden when preferences not enabled" + ); + }); + + it("shows AI Window activate when preferences enabled and feature not enabled", async () => { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.aiwindow.preferences.enabled", true], + ["browser.aiwindow.enabled", false], + ], + }); + + await openAiFeaturePanel(); + + const aiWindowItem = doc.getElementById("AIWindowItem"); + Assert.ok( + BrowserTestUtils.isVisible(aiWindowItem), + "AIWindowItem is visible when preferences enabled and feature not enabled" + ); + }); + + it("hides AI Window activate when feature enabled", async () => { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.aiwindow.preferences.enabled", true], + ["browser.aiwindow.enabled", true], + ], + }); + + await openAiFeaturePanel(); + + const aiWindowItem = doc.getElementById("AIWindowItem"); + Assert.ok( + !BrowserTestUtils.isVisible(aiWindowItem), + "AIWindowItem is hidden when feature enabled" + ); }); + + // TODO: Add tests for aiFeaturesAIWindowGroup when Model and Insight options are added }); diff --git a/browser/components/preferences/tests/browser_privacy_ipprotection.js b/browser/components/preferences/tests/browser_privacy_ipprotection.js index eb0a5a537412d..c2d11d45334c2 100644 --- a/browser/components/preferences/tests/browser_privacy_ipprotection.js +++ b/browser/components/preferences/tests/browser_privacy_ipprotection.js @@ -201,6 +201,101 @@ add_task(async function test_exclusions_add_button() { ); }); +// Test that we show the correct number of site exclusions +add_task(async function test_exclusions_count() { + const PERM_NAME = "ipp-vpn"; + await setupVpnPrefs({ feature: "beta", siteExceptions: true }); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:preferences#privacy" }, + async function (browser) { + let section = browser.contentDocument.getElementById(SECTION_ID); + let settingGroup = section.querySelector( + `setting-group[groupid="ipprotection"]` + ); + is_element_visible(section, "#dataIPProtectionGroup is shown"); + is_element_visible(settingGroup, "ipprotection setting group is shown"); + + let siteExceptionsGroup = settingGroup?.querySelector( + "#ipProtectionExceptions" + ); + is_element_visible(siteExceptionsGroup, "Site exceptions group is shown"); + + let exceptionAllListButton = siteExceptionsGroup?.querySelector( + "#ipProtectionExceptionAllListButton" + ); + is_element_visible( + exceptionAllListButton, + "Button for list of exclusions is shown" + ); + + let sitesCountUpdatedPromise = BrowserTestUtils.waitForMutationCondition( + exceptionAllListButton, + { attributes: true, attributeFilter: ["data-l10n-args"] }, + () => { + let args = exceptionAllListButton.getAttribute("data-l10n-args"); + return args && JSON.parse(args)?.count === 0; + } + ); + + // Clear ipp-vpn to start with 0 exclusions + Services.perms.removeByType(PERM_NAME); + + await sitesCountUpdatedPromise; + + Assert.ok(true, "Should show 0 exclusions initially"); + + // Now test with 1 exclusion + sitesCountUpdatedPromise = BrowserTestUtils.waitForMutationCondition( + exceptionAllListButton, + { attributes: true, attributeFilter: ["data-l10n-args"] }, + () => { + let args = exceptionAllListButton.getAttribute("data-l10n-args"); + return args && JSON.parse(args)?.count === 1; + } + ); + let site1 = "https://example.com"; + let principal1 = + Services.scriptSecurityManager.createContentPrincipalFromOrigin(site1); + Services.perms.addFromPrincipal( + principal1, + PERM_NAME, + Services.perms.DENY_ACTION + ); + + await sitesCountUpdatedPromise; + + Assert.ok(true, "Should show 1 exclusion after adding the first site"); + + // Now test with 2 exclusions + sitesCountUpdatedPromise = BrowserTestUtils.waitForMutationCondition( + exceptionAllListButton, + { attributes: true, attributeFilter: ["data-l10n-args"] }, + () => { + let args = exceptionAllListButton.getAttribute("data-l10n-args"); + return args && JSON.parse(args)?.count === 2; + } + ); + let site2 = "https://example.org"; + let principal2 = + Services.scriptSecurityManager.createContentPrincipalFromOrigin(site2); + Services.perms.addFromPrincipal( + principal2, + PERM_NAME, + Services.perms.DENY_ACTION + ); + + await sitesCountUpdatedPromise; + + Assert.ok(true, "Should show 2 exclusions after adding the second site"); + + // Clean up + Services.perms.removeByType(PERM_NAME); + Services.prefs.clearUserPref(ONBOARDING_MESSAGE_MASK_PREF); + } + ); +}); + // Test that autostart checkboxes exist and map to the correct preferences add_task(async function test_autostart_checkboxes() { await setupVpnPrefs({ diff --git a/browser/components/profiles/SelectableProfile.sys.mjs b/browser/components/profiles/SelectableProfile.sys.mjs index cf51ac13b543c..6b78b19523dd8 100644 --- a/browser/components/profiles/SelectableProfile.sys.mjs +++ b/browser/components/profiles/SelectableProfile.sys.mjs @@ -52,6 +52,42 @@ function standardAvatarURL(avatar, size = "80") { return `chrome://browser/content/profiles/assets/${size}_${avatar}.svg`; } +/** + * Resolve a relative path against an absolute path. The relative path may only + * contain parent directory or child directory parts. + * + * @param {string} absolute The absolute path + * @param {string} relative The relative path + * @returns {string} The resolved path + */ +function resolveDir(absolute, relative) { + let target = absolute; + + for (let pathPart of PathUtils.splitRelative(relative, { + allowParentDir: true, + })) { + if (pathPart === "..") { + target = PathUtils.parent(target); + + // On Windows there is no notion of a single root directory. Instead each + // disk has a root directory. Traversing to a different disk means allowing + // going above the root of the first disk then the next path part will be + // the new disk. + if (!target && AppConstants.platform != "win") { + throw new Error("Invalid path"); + } + } else { + target = target ? PathUtils.join(target, pathPart) : pathPart; + } + } + + if (!target) { + throw new Error("Invalid path"); + } + + return target; +} + /** * The selectable profile */ @@ -131,7 +167,7 @@ export class SelectableProfile { * @returns {string} Path of profile */ get path() { - return PathUtils.joinRelative( + return resolveDir( ProfilesDatastoreService.constructor.getDirectory("UAppData").path, this.#path ); @@ -154,15 +190,11 @@ export class SelectableProfile { * the profile local directory */ get localDir() { - return this.rootDir.then(root => { - let relative = root.getRelativePath( - ProfilesDatastoreService.constructor.getDirectory("DefProfRt") - ); - let local = - ProfilesDatastoreService.constructor.getDirectory("DefProfLRt"); - local.appendRelativePath(relative); - return local; - }); + return this.rootDir.then(root => + ProfilesDatastoreService.toolkitProfileService.getLocalDirFromRootDir( + root + ) + ); } /** diff --git a/browser/components/profiles/SelectableProfileService.sys.mjs b/browser/components/profiles/SelectableProfileService.sys.mjs index 8e08094438601..0fdde2546c28b 100644 --- a/browser/components/profiles/SelectableProfileService.sys.mjs +++ b/browser/components/profiles/SelectableProfileService.sys.mjs @@ -1616,10 +1616,9 @@ export class CommandLineHandler { async findDefaultProfilePath() { try { let profilesRoot = - ProfilesDatastoreService.constructor.getDirectory("DefProfRt").parent - .path; + ProfilesDatastoreService.constructor.getDirectory("UAppData"); - let iniPath = PathUtils.join(profilesRoot, "profiles.ini"); + let iniPath = PathUtils.join(profilesRoot.path, "profiles.ini"); let iniData = await IOUtils.readUTF8(iniPath); @@ -1650,7 +1649,11 @@ export class CommandLineHandler { let isRelative = iniParser.getString(section, "IsRelative") == "1"; if (isRelative) { - path = PathUtils.joinRelative(profilesRoot, path); + let profileDir = Cc["@mozilla.org/file/local;1"].createInstance( + Ci.nsIFile + ); + profileDir.setRelativeDescriptor(profilesRoot, path); + path = profileDir.path; } return path; @@ -1666,10 +1669,58 @@ export class CommandLineHandler { return null; } + /** + * Attempts to parse the arguments expected when opening URLs from other + * applications on macOS. + * + * @param {Array} args The command line arguments. + * @returns {boolean} True if the arguments matched the expected form. + */ + openUrls(args) { + // Arguments are expected to be in pairs of "-url" "". + if (args.length % 2 != 0) { + return false; + } + + for (let i = 0; i < args.length; i += 2) { + if (args[i] != "-url") { + return false; + } + } + + // Now the arguments are verified to only be "-url" arguments we can pass + // them directly to the only handler for those arguments. + let workingDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile); + let cmdLine = Cu.createCommandLine( + args, + workingDir, + Ci.nsICommandLine.STATE_REMOTE_EXPLICIT + ); + + try { + let handler = Cc["@mozilla.org/browser/final-clh;1"].createInstance( + Ci.nsICommandLineHandler + ); + handler.handle(cmdLine); + } catch (e) { + console.error(e); + return false; + } + + return true; + } + async redirectCommandLine(args) { let defaultPath = await this.findDefaultProfilePath(); if (defaultPath) { + if ( + defaultPath == SelectableProfileService.currentProfile.path && + this.openUrls(args) + ) { + return; + } + // Attempt to use the remoting service to send the arguments to any // existing instance of this profile (this even works for the current // instance on macOS which is the only platform we call this for). diff --git a/browser/components/profiles/tests/unit/head.js b/browser/components/profiles/tests/unit/head.js index 362d380dcc6c8..b6f32ebbf447a 100644 --- a/browser/components/profiles/tests/unit/head.js +++ b/browser/components/profiles/tests/unit/head.js @@ -4,21 +4,12 @@ "use strict"; -const { SelectableProfile } = ChromeUtils.importESModule( - "resource:///modules/profiles/SelectableProfile.sys.mjs" -); -const { Sqlite } = ChromeUtils.importESModule( - "resource://gre/modules/Sqlite.sys.mjs" -); - const lazy = {}; -ChromeUtils.defineLazyGetter(lazy, "SelectableProfileService", () => { - const { SelectableProfileService } = ChromeUtils.importESModule( - "resource:///modules/profiles/SelectableProfileService.sys.mjs" - ); - - return SelectableProfileService; +ChromeUtils.defineESModuleGetters(lazy, { + SelectableProfileService: + "resource:///modules/profiles/SelectableProfileService.sys.mjs", + Sqlite: "resource://gre/modules/Sqlite.sys.mjs", }); ChromeUtils.defineLazyGetter(lazy, "ProfilesDatastoreService", () => { @@ -101,7 +92,7 @@ async function openDatabase() { let dbFile = Services.dirsvc.get("UAppData", Ci.nsIFile); dbFile.append("Profile Groups"); dbFile.append(`${getProfileService().currentProfile.storeID}.sqlite`); - return Sqlite.openConnection({ + return lazy.Sqlite.openConnection({ path: dbFile.path, openNotExclusive: true, }); diff --git a/browser/components/profiles/tests/unit/test_command_line_handler.js b/browser/components/profiles/tests/unit/test_command_line_handler.js index 9879c12a421de..e24547ac6fa1c 100644 --- a/browser/components/profiles/tests/unit/test_command_line_handler.js +++ b/browser/components/profiles/tests/unit/test_command_line_handler.js @@ -9,6 +9,7 @@ const { sinon } = ChromeUtils.importESModule( const execProcess = sinon.stub(); const sendCommandLine = sinon.stub().throws(Cr.NS_ERROR_NOT_AVAILABLE); +const commandLineHandler = sinon.stub(); add_setup(async () => { await initSelectableProfileService(); @@ -19,6 +20,12 @@ add_setup(async () => { (path, args, raise) => sendCommandLine(path, [...args], raise) ); sinon.replace(getSelectableProfileService(), "execProcess", execProcess); + + let { nsDefaultCommandLineHandler: browserClh } = ChromeUtils.importESModule( + "resource:///modules/BrowserContentHandler.sys.mjs" + ); + + sinon.replace(browserClh.prototype, "handle", commandLineHandler); }); function nextCall(stub) { @@ -89,29 +96,28 @@ add_task(async function test_redirect_args() { "Should have prevented the default handler" ); - await nextCall(execProcess); + await nextCall(commandLineHandler); Assert.equal( - sendCommandLine.callCount, + commandLineHandler.callCount, 1, - "Should have attempted to remote once" - ); - Assert.deepEqual( - sendCommandLine.firstCall.args, - [profilePath, ["-url", "https://www.google.com/"], true], - "should have used the right arguments" + "Should have called the main browser command line handler directly" ); - sendCommandLine.resetHistory(); - - Assert.equal(execProcess.callCount, 1, "Should have attempted to exec once"); - Assert.deepEqual( - execProcess.firstCall.args, - [["-foreground", "-url", "https://www.google.com/"]], - "should have used the right arguments" + let [generatedCmdLine] = commandLineHandler.firstCall.args; + Assert.equal( + generatedCmdLine.length, + 2, + "Should have generated a command line with the correct number of arguments" + ); + Assert.equal(generatedCmdLine.getArgument(0), "-url"); + Assert.equal( + generatedCmdLine.getArgument(1), + "https://www.google.com/", + "Should have generated a command line with the correct URL" ); - execProcess.resetHistory(); + commandLineHandler.resetHistory(); let profileData = readProfilesIni(); diff --git a/browser/components/profiles/tests/unit/test_create_profile.js b/browser/components/profiles/tests/unit/test_create_profile.js index 88c87651c8baa..c5e5c27cdbcd4 100644 --- a/browser/components/profiles/tests/unit/test_create_profile.js +++ b/browser/components/profiles/tests/unit/test_create_profile.js @@ -76,4 +76,20 @@ add_task(async function test_create_profile() { profiles = await SelectableProfileService.getAllProfiles(); Assert.equal(profiles.length, 2, "Two selectable profiles exist"); + + let db = await openDatabase(); + let rows = await db.execute("SELECT path FROM Profiles WHERE id=:id;", { + id: newProfile.id, + }); + await db.close(); + + Assert.equal(rows.length, 1, "There should be one row for the profile"); + let path = rows[0].getResultByName("path"); + + // Non-unix and mac prefix the profile path with "Profiles/" + if (!AppConstants.XP_UNIX || AppConstants.platform == "macosx") { + path = path.substring("Profiles".length + 1); + } + + Assert.equal(path, leafName, "The profile path should be relative"); }); diff --git a/browser/components/profiles/tests/unit/test_recover_storeID.js b/browser/components/profiles/tests/unit/test_recover_storeID.js index 30aec4b8e7997..58d16f89191a0 100644 --- a/browser/components/profiles/tests/unit/test_recover_storeID.js +++ b/browser/components/profiles/tests/unit/test_recover_storeID.js @@ -16,7 +16,8 @@ add_task(async function test_recover_storeID() { await IOUtils.makeDirectory(groupsPath); let dbFile = PathUtils.join(groupsPath, "foobar.sqlite"); - let db = await Sqlite.openConnection({ + // eslint-disable-next-line mozilla/valid-lazy + let db = await lazy.Sqlite.openConnection({ path: dbFile, openNotExclusive: true, }); diff --git a/browser/components/profiles/tests/unit/test_relative_profile_path.js b/browser/components/profiles/tests/unit/test_relative_profile_path.js new file mode 100644 index 0000000000000..33da31b820371 --- /dev/null +++ b/browser/components/profiles/tests/unit/test_relative_profile_path.js @@ -0,0 +1,70 @@ +/* Any copyright is dedicated to the Public Domain. +https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const PATH_SEPARATOR = AppConstants.platform == "win" ? "\\" : "/"; + +add_setup(() => { + Services.prefs.setBoolPref("browser.profiles.enabled", true); +}); + +add_task(async function test_create_profile() { + let hash = xreDirProvider.getInstallHash(); + + // In the test harness gProfD is outside of the mocked app data directory so + // this will will use a relative profile path starting with `..`. + let absolutePath = gProfD.clone(); + absolutePath.append("absoluteProfile"); + absolutePath.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); + + let profileData = { + profiles: [ + { + name: "default", + path: absolutePath.path, + isRelative: false, + }, + ], + installs: { + [hash]: { + default: absolutePath.path, + }, + }, + }; + + writeProfilesIni(profileData); + + startProfileService(); + let service = getProfileService(); + Assert.equal(service.currentProfile.rootDir.path, absolutePath.path); + + await initSelectableProfileService(); + + let currentProfile = getSelectableProfileService().currentProfile; + + Assert.equal( + (await currentProfile.rootDir).path, + absolutePath.path, + "The profile root path should be correct" + ); + + Assert.equal( + (await currentProfile.localDir).path, + absolutePath.path, + "The profile local path should be correct" + ); + + let db = await openDatabase(); + let rows = await db.execute("SELECT path FROM Profiles WHERE id=:id;", { + id: currentProfile.id, + }); + await db.close(); + + Assert.equal(rows.length, 1, "There should be one row for the profile"); + Assert.equal( + rows[0].getResultByName("path"), + `..${PATH_SEPARATOR}absoluteProfile`, + "The profile path in the database should be relative" + ); +}); diff --git a/browser/components/profiles/tests/unit/xpcshell.toml b/browser/components/profiles/tests/unit/xpcshell.toml index 44a4fca0e1e08..16d1829705e7a 100644 --- a/browser/components/profiles/tests/unit/xpcshell.toml +++ b/browser/components/profiles/tests/unit/xpcshell.toml @@ -32,6 +32,8 @@ skip-if = [ ["test_recover_storeID.js"] +["test_relative_profile_path.js"] + ["test_selectable_profile_launch.js"] ["test_selectable_profile_service_exists.js"] diff --git a/browser/components/qrcode/QRCodeGenerator.sys.mjs b/browser/components/qrcode/QRCodeGenerator.sys.mjs new file mode 100644 index 0000000000000..00feb6468ead3 --- /dev/null +++ b/browser/components/qrcode/QRCodeGenerator.sys.mjs @@ -0,0 +1,135 @@ +/* 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/. */ + +/** + * QR Code Generator with Firefox logo overlay + * This module generates QR codes with the Firefox logo in the center + * Uses a worker thread for QR generation to avoid blocking the main thread + */ + +const lazy = {}; + +ChromeUtils.defineLazyGetter(lazy, "logConsole", function () { + return console.createInstance({ + prefix: "QRCodeGenerator", + maxLogLevel: Services.prefs.getBoolPref("browser.qrcode.log", false) + ? "Debug" + : "Warn", + }); +}); + +ChromeUtils.defineESModuleGetters(lazy, { + QRCodeWorker: "moz-src:///browser/components/qrcode/QRCodeWorker.sys.mjs", +}); + +export const QRCodeGenerator = { + /** + * Generate a QR code for the given URL with Firefox logo overlay + * + * @param {string} url - The URL to encode + * @param {Document} document - The document to use for creating elements + * @returns {Promise} - Data URI of the QR code with logo + */ + async generateQRCode(url, document) { + // Create a fresh worker for this generation + // Worker will be terminated after use to free resources + const worker = new lazy.QRCodeWorker(); + + try { + // Generate the base QR code with high error correction to allow for logo overlay + // Use worker thread to avoid blocking main thread + const qrData = await worker.generateQRCode(url, "H"); + + // Use a higher resolution for better quality (scale up 4x) + const scale = 4; + const canvas = document.createElementNS( + "http://www.w3.org/1999/xhtml", + "canvas" + ); + canvas.width = qrData.width * scale; + canvas.height = qrData.height * scale; + const ctx = canvas.getContext("2d"); + + // Disable image smoothing for crisp QR code rendering + ctx.imageSmoothingEnabled = false; + + // Load and draw the base QR code at high resolution + const qrImage = await this._loadImage(document, qrData.src); + ctx.drawImage(qrImage, 0, 0, qrData.width * scale, qrData.height * scale); + + // Calculate logo size and position (center of QR code) + // Use 18% of QR code size (reduced from 25%) to stay within error correction limits + const logoSize = Math.floor(qrData.width * 0.18) * scale; + const centerX = Math.floor((qrData.width * scale) / 2); + const centerY = Math.floor((qrData.height * scale) / 2); + + // Draw circular white background for logo with minimal padding + const padding = 4 * scale; + const radius = (logoSize + padding * 2) / 2; + ctx.fillStyle = "white"; + ctx.beginPath(); + ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI); + ctx.fill(); + + // Load and draw the Firefox logo at high resolution + try { + const logoImage = await this._loadFirefoxLogo(document); + // Re-enable smoothing for the logo to avoid pixelation + ctx.imageSmoothingEnabled = true; + ctx.imageSmoothingQuality = "high"; + + // Draw logo centered + const logoX = centerX - logoSize / 2; + const logoY = centerY - logoSize / 2; + ctx.drawImage(logoImage, logoX, logoY, logoSize, logoSize); + } catch (e) { + lazy.logConsole.warn("Could not load Firefox logo for QR code:", e); + } + + // Convert canvas to data URI + return canvas.toDataURL("image/png"); + } finally { + // Always terminate the worker to free resources + try { + await worker.terminate(); + lazy.logConsole.debug("QRCode worker terminated successfully"); + } catch (e) { + lazy.logConsole.warn("Failed to terminate QRCode worker:", e); + } + } + }, + + /** + * Load an image from a URL/data URI + * + * @param {Document} document - The document to use for creating the image + * @param {string} src - The image source + * @returns {Promise} + */ + _loadImage(document, src) { + return new Promise((resolve, reject) => { + const img = document.createElementNS( + "http://www.w3.org/1999/xhtml", + "img" + ); + img.onload = () => resolve(img); + img.onerror = reject; + img.src = src; + }); + }, + + /** + * Load the Firefox logo + * + * @param {Document} document - The document to use for creating the image + * @returns {Promise} + */ + async _loadFirefoxLogo(document) { + // Use the Firefox branding logo + return this._loadImage( + document, + "chrome://branding/content/about-logo.svg" + ); + }, +}; diff --git a/browser/components/qrcode/QRCodeWorker.sys.mjs b/browser/components/qrcode/QRCodeWorker.sys.mjs new file mode 100644 index 0000000000000..af5a9ca3d9e7c --- /dev/null +++ b/browser/components/qrcode/QRCodeWorker.sys.mjs @@ -0,0 +1,73 @@ +/* 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/. */ + +/** + * QRCodeWorker - Worker-based QR code generation + * + * This module provides a worker-based implementation for QR code generation + * to avoid blocking the main thread during QR code processing. + */ + +import { BasePromiseWorker } from "resource://gre/modules/PromiseWorker.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineLazyGetter(lazy, "logConsole", function () { + return console.createInstance({ + prefix: "QRCodeWorker", + maxLogLevel: Services.prefs.getBoolPref("browser.qrcode.log", false) + ? "Debug" + : "Warn", + }); +}); + +/** + * Worker wrapper for QR code generation + */ +export class QRCodeWorker extends BasePromiseWorker { + constructor() { + super("moz-src:///browser/components/qrcode/QRCodeWorker.worker.mjs", { + type: "module", + }); + + // Set up logging + this.log = (...args) => lazy.logConsole.debug(...args); + } + + /** + * Simple ping test for worker communication + * + * @returns {Promise} Returns "pong" + */ + async ping() { + return this.post("ping", []); + } + + /** + * Check if the QRCode library is available in the worker + * + * @returns {Promise} True if library is available + */ + async hasQRCodeLibrary() { + return this.post("hasQRCodeLibrary", []); + } + + /** + * Generate a QR code for the given URL + * + * @param {string} url - The URL to encode in the QR code + * @param {string} errorCorrectionLevel - Error correction level (L, M, Q, H) + * @returns {Promise} Object with width, height, and src data URI + */ + async generateQRCode(url, errorCorrectionLevel = "H") { + return this.post("generateQRCode", [url, errorCorrectionLevel]); + } + + /** + * Terminate the worker and clean up resources + */ + async terminate() { + super.terminate(); + } +} diff --git a/browser/components/qrcode/QRCodeWorker.worker.mjs b/browser/components/qrcode/QRCodeWorker.worker.mjs new file mode 100644 index 0000000000000..d3c7b195d7f27 --- /dev/null +++ b/browser/components/qrcode/QRCodeWorker.worker.mjs @@ -0,0 +1,90 @@ +/* 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/. */ + +// Now load the QRCode library with the full resource URI +import { QR } from "moz-src:///toolkit/components/qrcode/encoder.mjs"; +import { PromiseWorker } from "resource://gre/modules/workers/PromiseWorker.mjs"; + +/** + * QRCode Worker Implementation + * + * This worker handles QR code generation off the main thread. + */ + +/** + * The QR Code generator that runs in a worker thread + */ +class QRCodeWorkerImpl { + constructor() { + this.#connectToPromiseWorker(); + } + + /** + * Simple ping test for worker communication + * + * @returns {string} Returns "pong" + */ + ping() { + return "pong"; + } + + /** + * Check if the QRCode library is available + * + * @returns {boolean} True if library is loaded + */ + hasQRCodeLibrary() { + return typeof QR !== "undefined" && QR !== null; + } + + /** + * Generate a QR code for the given URL + * + * @param {string} url - The URL to encode + * @param {string} errorCorrectionLevel - Error correction level (L, M, Q, H) + * @returns {object} Object with width, height, and src data URI + */ + generateQRCode(url, errorCorrectionLevel = "H") { + if (!QR || !QR.encodeToDataURI) { + throw new Error("QRCode library not available in worker"); + } + + // Generate the QR code data URI + const qrData = QR.encodeToDataURI(url, errorCorrectionLevel); + + return { + width: qrData.width, + height: qrData.height, + src: qrData.src, + }; + } + + /** + * Glue code to connect the `QRCodeWorkerImpl` to the PromiseWorker interface. + */ + #connectToPromiseWorker() { + const worker = new PromiseWorker.AbstractWorker(); + + worker.dispatch = (method, args = []) => { + if (!this[method]) { + throw new Error("Method does not exist: " + method); + } + return this[method](...args); + }; + + worker.close = () => self.close(); + + worker.postMessage = (message, ...transfers) => { + self.postMessage(message, ...transfers); + }; + + self.addEventListener("message", msg => worker.handleMessage(msg)); + self.addEventListener("unhandledrejection", function (error) { + throw error.reason; + }); + } +} + +// Create the worker instance +new QRCodeWorkerImpl(); diff --git a/browser/components/qrcode/moz.build b/browser/components/qrcode/moz.build new file mode 100644 index 0000000000000..e18c196fcb7a7 --- /dev/null +++ b/browser/components/qrcode/moz.build @@ -0,0 +1,11 @@ +# 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/. + +MOZ_SRC_FILES += [ + "QRCodeGenerator.sys.mjs", + "QRCodeWorker.sys.mjs", + "QRCodeWorker.worker.mjs", +] + +XPCSHELL_TESTS_MANIFESTS += ["test/xpcshell/xpcshell.toml"] diff --git a/browser/components/qrcode/test/xpcshell/test_QRCodeGenerator_worker_integration.js b/browser/components/qrcode/test/xpcshell/test_QRCodeGenerator_worker_integration.js new file mode 100644 index 0000000000000..c41bff43bab95 --- /dev/null +++ b/browser/components/qrcode/test/xpcshell/test_QRCodeGenerator_worker_integration.js @@ -0,0 +1,88 @@ +/* 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"; + +/** + * Test QRCodeGenerator with worker integration + */ + +const { QRCodeGenerator } = ChromeUtils.importESModule( + "moz-src:///browser/components/qrcode/QRCodeGenerator.sys.mjs" +); + +/** + * Helper function to create a minimal mock document with required methods + */ +function createMockDocument() { + return { + createElementNS: (ns, tagName) => { + if (tagName === "canvas") { + return { + width: 0, + height: 0, + getContext: () => ({ + imageSmoothingEnabled: false, + imageSmoothingQuality: "high", + fillStyle: "white", + beginPath: () => {}, + arc: () => {}, + fill: () => {}, + drawImage: () => {}, + }), + toDataURL: () => "data:image/png;base64,mock", + }; + } else if (tagName === "img") { + return { + onload: null, + onerror: null, + set src(value) { + // Simulate image load + Services.tm.dispatchToMainThread(() => { + if (this.onload) { + this.onload(); + } + }); + }, + }; + } + return {}; + }, + }; +} + +add_task(async function test_generator_uses_worker() { + info("Testing QRCodeGenerator generates QR code using worker"); + + const mockDocument = createMockDocument(); + + // Generate a QR code using the worker + const testUrl = "https://mozilla.org"; + const dataUri = await QRCodeGenerator.generateQRCode(testUrl, mockDocument); + + Assert.ok(dataUri, "Should get a data URI from generateQRCode"); + Assert.ok(dataUri.startsWith("data:image/"), "Should be a data URI"); + + // Worker is automatically cleaned up after generation +}); + +add_task(async function test_generator_multiple_generations() { + info("Testing QRCodeGenerator can generate multiple QR codes"); + + // Generate first QR code + const dataUri1 = await QRCodeGenerator.generateQRCode( + "https://mozilla.org", + createMockDocument() + ); + Assert.ok(dataUri1, "Should get first data URI"); + + // Generate second QR code + const dataUri2 = await QRCodeGenerator.generateQRCode( + "https://firefox.com", + createMockDocument() + ); + Assert.ok(dataUri2, "Should get second data URI"); + + // Each call creates and cleans up its own worker +}); diff --git a/browser/components/qrcode/test/xpcshell/test_QRCodeWorker.js b/browser/components/qrcode/test/xpcshell/test_QRCodeWorker.js new file mode 100644 index 0000000000000..bedb01b28b5ca --- /dev/null +++ b/browser/components/qrcode/test/xpcshell/test_QRCodeWorker.js @@ -0,0 +1,67 @@ +/* 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"; + +/** + * Test QRCodeWorker functionality + */ + +const { QRCodeWorker } = ChromeUtils.importESModule( + "moz-src:///browser/components/qrcode/QRCodeWorker.sys.mjs" +); + +add_task(async function test_worker_instantiation() { + info("Testing QRCodeWorker can be instantiated"); + + const worker = new QRCodeWorker(); + Assert.ok(worker, "QRCodeWorker instance should be created"); + + // Clean up + await worker.terminate(); +}); + +add_task(async function test_worker_responds_to_ping() { + info("Testing QRCodeWorker responds to ping message"); + const worker = new QRCodeWorker(); + + // Test ping functionality + const response = await worker.ping(); + Assert.equal(response, "pong", "Worker should respond with 'pong' to ping"); + + // Clean up + await worker.terminate(); +}); + +add_task(async function test_worker_can_load_qrcode_library() { + info("Testing QRCodeWorker can load QRCode library"); + + const worker = new QRCodeWorker(); + + // Test that the worker can check if the QRCode library is available + const hasLibrary = await worker.hasQRCodeLibrary(); + Assert.ok(hasLibrary, "Worker should have access to QRCode library"); + + // Clean up + await worker.terminate(); +}); + +add_task(async function test_worker_can_generate_simple_qrcode() { + info("Testing QRCodeWorker can generate a simple QR code"); + + const worker = new QRCodeWorker(); + + // Test generating a very simple QR code + const testUrl = "https://mozilla.org"; + const result = await worker.generateQRCode(testUrl); + + Assert.ok(result, "Should get a result from generateQRCode"); + Assert.ok(result.width, "Result should have a width"); + Assert.ok(result.height, "Result should have a height"); + Assert.ok(result.src, "Result should have a src data URI"); + Assert.ok(result.src.startsWith("data:image/"), "src should be a data URI"); + + // Clean up + await worker.terminate(); +}); diff --git a/browser/components/qrcode/test/xpcshell/xpcshell.toml b/browser/components/qrcode/test/xpcshell/xpcshell.toml new file mode 100644 index 0000000000000..9b252cb77e908 --- /dev/null +++ b/browser/components/qrcode/test/xpcshell/xpcshell.toml @@ -0,0 +1,6 @@ +[DEFAULT] +firefox-appdir = "browser" + +["test_QRCodeGenerator_worker_integration.js"] + +["test_QRCodeWorker.js"] diff --git a/browser/components/search/test/marionette/telemetry/test_ping_submitted.py b/browser/components/search/test/marionette/telemetry/test_ping_submitted.py index a1eaf25a80c28..81244cead59c9 100644 --- a/browser/components/search/test/marionette/telemetry/test_ping_submitted.py +++ b/browser/components/search/test/marionette/telemetry/test_ping_submitted.py @@ -12,13 +12,11 @@ def setUp(self): self.marionette.set_context(self.marionette.CONTEXT_CHROME) - self.marionette.enforce_gecko_prefs( - { - "datareporting.healthreport.uploadEnabled": True, - "telemetry.fog.test.localhost_port": 3000, - "browser.search.log": True, - } - ) + self.marionette.enforce_gecko_prefs({ + "datareporting.healthreport.uploadEnabled": True, + "telemetry.fog.test.localhost_port": 3000, + "browser.search.log": True, + }) # The categorization ping is submitted on startup. If anything delays # its initialization, turning the preference on and immediately # attaching a categorization event could result in the ping being diff --git a/browser/components/search/test/marionette/test_engines_on_restart.py b/browser/components/search/test/marionette/test_engines_on_restart.py index 5eccd58623266..8e0b6a6eb842a 100644 --- a/browser/components/search/test/marionette/test_engines_on_restart.py +++ b/browser/components/search/test/marionette/test_engines_on_restart.py @@ -10,11 +10,9 @@ class TestEnginesOnRestart(MarionetteTestCase): def setUp(self): super().setUp() - self.marionette.enforce_gecko_prefs( - { - "browser.search.log": True, - } - ) + self.marionette.enforce_gecko_prefs({ + "browser.search.log": True, + }) def get_default_search_engine(self): """Retrieve the identifier of the default search engine.""" diff --git a/browser/components/sessionstore/test/marionette/session_store_test_case.py b/browser/components/sessionstore/test/marionette/session_store_test_case.py index 4d955cc855869..593cca5e49d45 100644 --- a/browser/components/sessionstore/test/marionette/session_store_test_case.py +++ b/browser/components/sessionstore/test/marionette/session_store_test_case.py @@ -15,23 +15,21 @@ def inline(doc): # Each list element represents a window of tabs loaded at # some testing URL -DEFAULT_WINDOWS = set( - [ - # Window 1. Note the comma after the inline call - - # this is Python's way of declaring a 1 item tuple. - (inline("""Lorem"""),), - # Window 2 - ( - inline("""ipsum"""), - inline("""dolor"""), - ), - # Window 3 - ( - inline("""sit"""), - inline("""amet"""), - ), - ] -) +DEFAULT_WINDOWS = set([ + # Window 1. Note the comma after the inline call - + # this is Python's way of declaring a 1 item tuple. + (inline("""Lorem"""),), + # Window 2 + ( + inline("""ipsum"""), + inline("""dolor"""), + ), + # Window 3 + ( + inline("""sit"""), + inline("""amet"""), + ), +]) class SessionStoreTestCase(WindowManagerMixin, MarionetteTestCase): @@ -53,37 +51,33 @@ def setUp( self.test_windows = test_windows - self.private_windows = set( - [ - ( - inline("""consectetur"""), - inline("""ipsum"""), - ), - ( - inline("""adipiscing"""), - inline("""consectetur"""), - ), - ] - ) - - self.marionette.enforce_gecko_prefs( - { - # Set browser restore previous session pref, - # depending on what the test requires. - "browser.startup.page": startup_page, - # Make the content load right away instead of waiting for - # the user to click on the background tabs - "browser.sessionstore.restore_on_demand": restore_on_demand, - # Avoid race conditions by having the content process never - # send us session updates unless the parent has explicitly asked - # for them via the TabStateFlusher. - "browser.sessionstore.debug.no_auto_updates": no_auto_updates, - # Whether to enable the register application restart mechanism. - "toolkit.winRegisterApplicationRestart": win_register_restart, - # Whether to enable taskbar tabs for this test - "browser.taskbarTabs.enabled": taskbartabs_enable, - } - ) + self.private_windows = set([ + ( + inline("""consectetur"""), + inline("""ipsum"""), + ), + ( + inline("""adipiscing"""), + inline("""consectetur"""), + ), + ]) + + self.marionette.enforce_gecko_prefs({ + # Set browser restore previous session pref, + # depending on what the test requires. + "browser.startup.page": startup_page, + # Make the content load right away instead of waiting for + # the user to click on the background tabs + "browser.sessionstore.restore_on_demand": restore_on_demand, + # Avoid race conditions by having the content process never + # send us session updates unless the parent has explicitly asked + # for them via the TabStateFlusher. + "browser.sessionstore.debug.no_auto_updates": no_auto_updates, + # Whether to enable the register application restart mechanism. + "toolkit.winRegisterApplicationRestart": win_register_restart, + # Whether to enable taskbar tabs for this test + "browser.taskbarTabs.enabled": taskbartabs_enable, + }) self.all_windows = self.test_windows.copy() self.open_windows(self.test_windows) diff --git a/browser/components/sessionstore/test/marionette/test_log_files.py b/browser/components/sessionstore/test/marionette/test_log_files.py index 0b2b3477a4b95..070fb252f387b 100644 --- a/browser/components/sessionstore/test/marionette/test_log_files.py +++ b/browser/components/sessionstore/test/marionette/test_log_files.py @@ -16,12 +16,10 @@ def inline(doc): class TestSessionRestoreLogging(WindowManagerMixin, MarionetteTestCase): def setUp(self): super().setUp() - self.marionette.enforce_gecko_prefs( - { - "browser.sessionstore.loglevel": "Debug", - "browser.sessionstore.log.appender.file.logOnSuccess": True, - } - ) + self.marionette.enforce_gecko_prefs({ + "browser.sessionstore.loglevel": "Debug", + "browser.sessionstore.log.appender.file.logOnSuccess": True, + }) def tearDown(self): try: @@ -101,11 +99,9 @@ def test_per_startup_logfile_creation(self): self.marionette.start_session() def test_errors_flush_to_disk(self): - self.marionette.enforce_gecko_prefs( - { - "browser.sessionstore.log.appender.file.logOnSuccess": False, - } - ) + self.marionette.enforce_gecko_prefs({ + "browser.sessionstore.log.appender.file.logOnSuccess": False, + }) self.marionette.quit() sessionFile = self.getSessionFilePath() self.assertTrue( @@ -114,7 +110,7 @@ def test_errors_flush_to_disk(self): ) # replace the contents with nonsense so we get a not-readable error on startup with open(sessionFile, "wb") as f: - f.write(b"\x00\xFF\xABgarbageDATA") + f.write(b"\x00\xff\xabgarbageDATA") self.marionette.start_session() self.marionette.set_context("chrome") diff --git a/browser/components/sessionstore/test/marionette/test_persist_closed_tabs_restore_manually.py b/browser/components/sessionstore/test/marionette/test_persist_closed_tabs_restore_manually.py index 011eac40905ee..1a58a92dabe0d 100644 --- a/browser/components/sessionstore/test/marionette/test_persist_closed_tabs_restore_manually.py +++ b/browser/components/sessionstore/test/marionette/test_persist_closed_tabs_restore_manually.py @@ -33,15 +33,13 @@ def setUp(self): startup_page=1, include_private=False, restore_on_demand=True, - test_windows=set( - [ - # Window 1 - ( - inline("lorem ipsom"), - inline("dolor"), - ), - ] - ), + test_windows=set([ + # Window 1 + ( + inline("lorem ipsom"), + inline("dolor"), + ), + ]), ) def test_restore(self): diff --git a/browser/components/sessionstore/test/marionette/test_restore_manually.py b/browser/components/sessionstore/test/marionette/test_restore_manually.py index 42b3b5c640caa..0f3291f2e8b79 100644 --- a/browser/components/sessionstore/test/marionette/test_restore_manually.py +++ b/browser/components/sessionstore/test/marionette/test_restore_manually.py @@ -26,17 +26,15 @@ def setUp(self): startup_page=1, include_private=False, restore_on_demand=True, - test_windows=set( - [ - # Window 1 - ( - inline("lorem ipsom"), - inline("dolor"), - ), - # Window 2 - (inline("sit"),), - ] - ), + test_windows=set([ + # Window 1 + ( + inline("lorem ipsom"), + inline("dolor"), + ), + # Window 2 + (inline("sit"),), + ]), ) def test_restore(self): diff --git a/browser/components/sessionstore/test/marionette/test_restore_manually_with_pinned_tabs.py b/browser/components/sessionstore/test/marionette/test_restore_manually_with_pinned_tabs.py index 9266edc54740c..5e92dce2b0a46 100644 --- a/browser/components/sessionstore/test/marionette/test_restore_manually_with_pinned_tabs.py +++ b/browser/components/sessionstore/test/marionette/test_restore_manually_with_pinned_tabs.py @@ -23,16 +23,14 @@ def setUp(self): startup_page=1, include_private=False, restore_on_demand=True, - test_windows=set( - [ - # Window 1 - ( - inline("""ipsum"""), - inline("""dolor"""), - inline("""amet"""), - ), - ] - ), + test_windows=set([ + # Window 1 + ( + inline("""ipsum"""), + inline("""dolor"""), + inline("""amet"""), + ), + ]), ) def test_no_restore_with_quit(self): diff --git a/browser/components/sessionstore/test/marionette/test_restore_manually_with_tab_groups.py b/browser/components/sessionstore/test/marionette/test_restore_manually_with_tab_groups.py index dd60bb6bd75f6..bee623e8e8ed0 100644 --- a/browser/components/sessionstore/test/marionette/test_restore_manually_with_tab_groups.py +++ b/browser/components/sessionstore/test/marionette/test_restore_manually_with_tab_groups.py @@ -23,17 +23,15 @@ def setUp(self): startup_page=1, include_private=False, restore_on_demand=True, - test_windows=set( - [ - ( - inline("""lorem"""), - inline("""ipsum"""), - inline("""dolor"""), - inline("""sit"""), - inline("""amet"""), - ), - ] - ), + test_windows=set([ + ( + inline("""lorem"""), + inline("""ipsum"""), + inline("""dolor"""), + inline("""sit"""), + inline("""amet"""), + ), + ]), ) def test_no_restore_with_quit(self): diff --git a/browser/components/sessionstore/test/marionette/test_restore_sidebar.py b/browser/components/sessionstore/test/marionette/test_restore_sidebar.py index 9ca6ee46d7063..fcccf7c137fe5 100644 --- a/browser/components/sessionstore/test/marionette/test_restore_sidebar.py +++ b/browser/components/sessionstore/test/marionette/test_restore_sidebar.py @@ -25,14 +25,12 @@ def setUp(self): startup_page=1, include_private=False, restore_on_demand=True, - test_windows=set( - [ - ( - inline("lorem ipsom"), - inline("dolor"), - ), - ] - ), + test_windows=set([ + ( + inline("lorem ipsom"), + inline("dolor"), + ), + ]), ) def test_restore_sidebar_open(self): diff --git a/browser/components/sessionstore/test/marionette/test_restore_sidebar_automatic.py b/browser/components/sessionstore/test/marionette/test_restore_sidebar_automatic.py index cad527b8aa881..19c64844cdc71 100644 --- a/browser/components/sessionstore/test/marionette/test_restore_sidebar_automatic.py +++ b/browser/components/sessionstore/test/marionette/test_restore_sidebar_automatic.py @@ -25,14 +25,12 @@ def setUp(self): startup_page=3, include_private=False, restore_on_demand=False, - test_windows=set( - [ - ( - inline("lorem ipsom"), - inline("dolor"), - ), - ] - ), + test_windows=set([ + ( + inline("lorem ipsom"), + inline("dolor"), + ), + ]), ) def test_restore(self): diff --git a/browser/components/sessionstore/test/marionette/test_tabgroups_restore.py b/browser/components/sessionstore/test/marionette/test_tabgroups_restore.py index 501075b906c8f..f3fdee269f85c 100644 --- a/browser/components/sessionstore/test/marionette/test_tabgroups_restore.py +++ b/browser/components/sessionstore/test/marionette/test_tabgroups_restore.py @@ -19,13 +19,11 @@ def inline(doc): # Each list element represents a window of tabs loaded at # some testing URL -DEFAULT_WINDOWS = set( - [ - # Window 1. Note the comma after the inline call - - # this is Python's way of declaring a 1 item tuple. - (inline("""Lorem"""), inline("""Ipsum""")), - ] -) +DEFAULT_WINDOWS = set([ + # Window 1. Note the comma after the inline call - + # this is Python's way of declaring a 1 item tuple. + (inline("""Lorem"""), inline("""Ipsum""")), +]) class TestAutoRestoreWithTabGroups(SessionStoreTestCase): diff --git a/browser/components/sessionstore/test/marionette/test_taskbartab_restore.py b/browser/components/sessionstore/test/marionette/test_taskbartab_restore.py index 96baf1c796fd1..de6b8e2667a70 100644 --- a/browser/components/sessionstore/test/marionette/test_taskbartab_restore.py +++ b/browser/components/sessionstore/test/marionette/test_taskbartab_restore.py @@ -24,15 +24,13 @@ def setUp(self): include_private=False, restore_on_demand=False, taskbartabs_enable=True, - test_windows=set( - [ - # Window 1 - ( - inline("lorem ipsom"), - inline("dolor"), - ), - ] - ), + test_windows=set([ + # Window 1 + ( + inline("lorem ipsom"), + inline("dolor"), + ), + ]), ) """ @@ -94,15 +92,13 @@ def setUp(self): include_private=False, restore_on_demand=False, taskbartabs_enable=True, - test_windows=set( - [ - # Window 1 - ( - inline("lorem ipsom"), - inline("dolor"), - ), - ] - ), + test_windows=set([ + # Window 1 + ( + inline("lorem ipsom"), + inline("dolor"), + ), + ]), ) """ diff --git a/browser/components/sessionstore/test/marionette/test_taskbartab_sessionstate.py b/browser/components/sessionstore/test/marionette/test_taskbartab_sessionstate.py index 0ffc6aa5fbb56..7f75f27dffe8f 100644 --- a/browser/components/sessionstore/test/marionette/test_taskbartab_sessionstate.py +++ b/browser/components/sessionstore/test/marionette/test_taskbartab_sessionstate.py @@ -22,15 +22,13 @@ def setUp(self): include_private=False, restore_on_demand=False, taskbartabs_enable=True, - test_windows=set( - [ - # Window 1 - ( - inline("lorem ipsom"), - inline("dolor"), - ), - ] - ), + test_windows=set([ + # Window 1 + ( + inline("lorem ipsom"), + inline("dolor"), + ), + ]), ) """ diff --git a/browser/components/sidebar/tests/marionette/test_default_launcher_visible.py b/browser/components/sidebar/tests/marionette/test_default_launcher_visible.py index ac639b04d8391..7fa0792519763 100644 --- a/browser/components/sidebar/tests/marionette/test_default_launcher_visible.py +++ b/browser/components/sidebar/tests/marionette/test_default_launcher_visible.py @@ -15,7 +15,6 @@ class TestDefaultLauncherVisible(MarionetteTestCase): - def setUp(self): MarionetteTestCase.setUp(self) @@ -106,12 +105,10 @@ def test_first_use_default_visible_pref_false(self): ) # Mimic an update which enables sidebar.revamp for the first time - self.restart_with_prefs( - { - "sidebar.revamp": True, - "browser.startup.page": 3, - } - ) + self.restart_with_prefs({ + "sidebar.revamp": True, + "browser.startup.page": 3, + }) self.assertTrue( self.is_button_visible(), @@ -222,12 +219,10 @@ def test_new_sidebar_enabled_at_runtime_via_nimbus(self): ) # This mocks the enrollment in which Nimbus sets the following prefs - self.marionette.set_prefs( - { - "sidebar.revamp": True, - "sidebar.revamp.defaultLauncherVisible": False, - } - ) + self.marionette.set_prefs({ + "sidebar.revamp": True, + "sidebar.revamp.defaultLauncherVisible": False, + }) # We expect enabling the pref to add the button to the toolbar Wait(self.marionette).until( @@ -254,13 +249,11 @@ def test_new_sidebar_enabled_at_runtime_via_nimbus(self): def test_vertical_tabs_default_hidden(self): # Verify that starting with verticalTabs enabled and default visibility false results in a visible # launcher with the vertical tabstrip - self.restart_with_prefs( - { - "sidebar.revamp": True, - "sidebar.verticalTabs": True, - "sidebar.visibility": "always-show", - } - ) + self.restart_with_prefs({ + "sidebar.revamp": True, + "sidebar.verticalTabs": True, + "sidebar.visibility": "always-show", + }) Wait(self.marionette).until( lambda _: self.is_launcher_visible(), diff --git a/browser/components/sidebar/tests/marionette/test_initialize_vertical_tabs.py b/browser/components/sidebar/tests/marionette/test_initialize_vertical_tabs.py index 0a52b863c5f77..ef9ca5b2c3ecd 100644 --- a/browser/components/sidebar/tests/marionette/test_initialize_vertical_tabs.py +++ b/browser/components/sidebar/tests/marionette/test_initialize_vertical_tabs.py @@ -97,14 +97,12 @@ def check_tabs_toolbar_visibilities(self, orientation="vertical"): def test_vertical_widgets_in_area(self): # A clean startup in verticalTabs mode; we should get all the defaults - self.restart_with_prefs( - { - "sidebar.revamp": True, - "sidebar.verticalTabs": True, - customization_pref: None, - snapshot_pref: None, - } - ) + self.restart_with_prefs({ + "sidebar.revamp": True, + "sidebar.verticalTabs": True, + customization_pref: None, + snapshot_pref: None, + }) horiz_tab_ids = self.get_area_widgets("AREA_TABSTRIP") vertical_tab_ids = self.get_area_widgets("AREA_VERTICAL_TABSTRIP") @@ -153,13 +151,11 @@ def test_restore_tabstrip_customizations(self): "sidebar.revamp": True, "sidebar.verticalTabs": False, } - self.restart_with_prefs( - { - **fixture_prefs, - customization_pref: None, - snapshot_pref: None, - } - ) + self.restart_with_prefs({ + **fixture_prefs, + customization_pref: None, + snapshot_pref: None, + }) # Add a widget at the start of the horizontal tabstrip # This is synchronous and should result in updating the UI and the saved state in uiCustomization pref @@ -222,13 +218,11 @@ def test_preserve_visibility_pref_after_restart(self): "sidebar.verticalTabs": True, "sidebar.visibility": "hide-sidebar", } - self.restart_with_prefs( - { - **fixture_prefs, - customization_pref: None, - snapshot_pref: None, - } - ) + self.restart_with_prefs({ + **fixture_prefs, + customization_pref: None, + snapshot_pref: None, + }) pref_value = self.marionette.execute_script( """ @@ -265,12 +259,10 @@ def test_preserve_visibility_pref_after_restart(self): def test_hide_drag_to_pin_promo_if_horizontal_tabs_pinned(self): # Pin a tab using the horizontal tabstrip. - self.restart_with_prefs( - { - "sidebar.revamp": False, - "sidebar.verticalTabs": False, - } - ) + self.restart_with_prefs({ + "sidebar.revamp": False, + "sidebar.verticalTabs": False, + }) self.marionette.execute_async_script( """ let resolve = arguments[0]; diff --git a/browser/components/urlbar/UrlbarUtils.sys.mjs b/browser/components/urlbar/UrlbarUtils.sys.mjs index abb7571bc2497..caceb63a04fe2 100644 --- a/browser/components/urlbar/UrlbarUtils.sys.mjs +++ b/browser/components/urlbar/UrlbarUtils.sys.mjs @@ -1510,11 +1510,14 @@ export var UrlbarUtils = { // Appends subtype to certain result types. function checkForSubType(type, res) { - if (res.providerName == "UrlbarProviderSemanticHistorySearch") { + if (res.providerName == "UrlbarProviderInputHistory") { + type += "_adaptive"; + } else if (res.providerName == "UrlbarProviderSemanticHistorySearch") { type += "_semantic"; } if ( lazy.UrlbarSearchUtils.resultIsSERP(res, [ + UrlbarUtils.RESULT_SOURCE.BOOKMARKS, UrlbarUtils.RESULT_SOURCE.HISTORY, UrlbarUtils.RESULT_SOURCE.TABS, ]) @@ -1611,7 +1614,7 @@ export var UrlbarUtils = { return "clipboard"; } if (result.source === this.RESULT_SOURCE.BOOKMARKS) { - return "bookmark"; + return checkForSubType("bookmark", result); } return checkForSubType("history", result); case this.RESULT_TYPE.RESTRICT: diff --git a/browser/components/urlbar/content/UrlbarInput.mjs b/browser/components/urlbar/content/UrlbarInput.mjs index 92117395f8a8b..2e6e2be9d7e28 100644 --- a/browser/components/urlbar/content/UrlbarInput.mjs +++ b/browser/components/urlbar/content/UrlbarInput.mjs @@ -301,6 +301,10 @@ export class UrlbarInput extends HTMLElement { this.inputField = /** @type {HTMLInputElement} */ ( this.querySelector(".urlbar-input") ); + if (this.#sapName == "searchbar") { + // This adds a native clear button. + this.inputField.setAttribute("type", "search"); + } this._inputContainer = this.querySelector(".urlbar-input-container"); this.controller = new lazy.UrlbarController({ input: this }); @@ -3003,7 +3007,7 @@ export class UrlbarInput extends HTMLElement { * @param {object} [options] Options for setting. * @param {boolean} [options.allowTrim] Whether the value can be trimmed. * @param {string} [options.untrimmedValue] Override for this._untrimmedValue. - * @param {boolean} [options.valueIsTyped] Override for this.valueIsTypede. + * @param {boolean} [options.valueIsTyped] Override for this.valueIsTyped. * @param {string} [options.actionType] Value for the `actiontype` attribute. * * @returns {string} The set value. diff --git a/browser/components/urlbar/metrics.yaml b/browser/components/urlbar/metrics.yaml index 69aedea1923d0..4ad6b92b2eaaf 100644 --- a/browser/components/urlbar/metrics.yaml +++ b/browser/components/urlbar/metrics.yaml @@ -102,10 +102,15 @@ urlbar: `autofill_unknown`, `autofill_url`, `bookmark`, + `bookmark_adaptive`, + `bookmark_adaptive_serp`, + `bookmark_serp`, `calc`, `clipboard`, `fxsuggest_data_sharing_opt_in`, `history`, + `history_adaptive`, + `history_adaptive_serp`, `history_semantic`, `history_semantic_serp`, `history_serp`, @@ -139,6 +144,8 @@ urlbar: `search_suggest`, `search_suggest_rich`, `tab`, + `tab_adaptive`, + `tab_adaptive_serp`, `tab_semantic`, `tab_semantic_serp`, `tab_serp`, @@ -467,11 +474,16 @@ urlbar: `autofill_unknown`, `autofill_url`, `bookmark`, + `bookmark_adaptive`, + `bookmark_adaptive_serp`, + `bookmark_serp`, `calc`, `clipboard`, `experimental_addon`, `fxsuggest_data_sharing_opt_in`, `history`, + `history_adaptive`, + `history_adaptive_serp`, `history_semantic`, `history_semantic_serp`, `history_serp`, @@ -508,6 +520,8 @@ urlbar: `search_suggest_rich`, `site_specific_contextual_search`, `tab`, + `tab_adaptive`, + `tab_adaptive_serp`, `tab_semantic`, `tab_semantic_serp`, `tab_serp`, @@ -596,10 +610,15 @@ urlbar: `autofill_unknown`, `autofill_url`, `bookmark`, + `bookmark_adaptive`, + `bookmark_adaptive_serp`, + `bookmark_serp`, `calc`, `clipboard`, `fxsuggest_data_sharing_opt_in`, `history`, + `history_adaptive`, + `history_adaptive_serp`, `history_semantic`, `history_semantic_serp`, `history_serp`, @@ -633,6 +652,8 @@ urlbar: `search_suggest`, `search_suggest_rich`, `tab`, + `tab_adaptive`, + `tab_adaptive_serp`, `tab_semantic`, `tab_semantic_serp`, `tab_serp`, diff --git a/browser/components/urlbar/tests/browser/browser.toml b/browser/components/urlbar/tests/browser/browser.toml index 641c458348ff9..aad23503b148f 100644 --- a/browser/components/urlbar/tests/browser/browser.toml +++ b/browser/components/urlbar/tests/browser/browser.toml @@ -717,6 +717,7 @@ https_first_disabled = true support-files = ["!/browser/base/content/test/protectionsUI/trackingPage.html"] ["browser_trust_panel_pages.js"] +https_first_disabled = true ["browser_trust_panel_security_view.js"] https_first_disabled = true diff --git a/browser/components/urlbar/tests/browser/browser_trust_panel.js b/browser/components/urlbar/tests/browser/browser_trust_panel.js index b9eb27300610b..c89405a186181 100644 --- a/browser/components/urlbar/tests/browser/browser_trust_panel.js +++ b/browser/components/urlbar/tests/browser/browser_trust_panel.js @@ -63,6 +63,14 @@ add_task(async function basic_test() { await BrowserTestUtils.waitForCondition(() => urlbarIcon(window) != "none"); Assert.equal(urlbarIcon(window), ETP_ACTIVE_ICON, "Showing trusted icon"); + Assert.equal( + window.document + .getElementById("trust-icon-container") + .getAttribute("tooltiptext"), + "Verified by: Mozilla Testing", + "Tooltip has been set" + ); + Assert.ok( !BrowserTestUtils.isVisible(urlbarLabel(window)), "Not showing Not Secure label" diff --git a/browser/components/urlbar/tests/browser/browser_trust_panel_pages.js b/browser/components/urlbar/tests/browser/browser_trust_panel_pages.js index b3d988d052a54..bd8d1f4684c5d 100644 --- a/browser/components/urlbar/tests/browser/browser_trust_panel_pages.js +++ b/browser/components/urlbar/tests/browser/browser_trust_panel_pages.js @@ -9,48 +9,95 @@ const ICONS = { active: "chrome://browser/skin/trust-icon-active.svg", insecure: "chrome://browser/skin/trust-icon-insecure.svg", file: "chrome://global/skin/icons/page-portrait.svg", + secure: "chrome://global/skin/icons/security.svg", + broken: "chrome://global/skin/icons/security-broken.svg", }; const TESTS = [ { url: "about:about", icon: ICONS.active, + connectionIcon: ICONS.secure, + descriptionSection: "trustpanel-header-enabled", }, { url: "https://example.com", icon: ICONS.active, + connectionIcon: ICONS.secure, + descriptionSection: "trustpanel-header-enabled", }, { - url: "http://127.0.0.1/", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + url: "http://example.com", icon: ICONS.insecure, - waitForLoad: false, + connectionIcon: ICONS.broken, + descriptionSection: "trustpanel-header-enabled-insecure", }, ]; -add_setup(async function setup() { +let fetchIconUrl = (doc, id) => { + let icon = doc.defaultView.getComputedStyle( + doc.getElementById(id) + ).listStyleImage; + return icon.match(/url\("([^"]+)"\)/)?.[1] ?? null; +}; + +add_task(async function () { await SpecialPowers.pushPrefEnv({ set: [["browser.urlbar.trustPanel.featureGate", true]], }); -}); -add_task(async function () { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + web_accessible_resources: ["test_page.html"], + }, + files: { + "test_page.html": `title`, + }, + }); + + await extension.startup(); + + TESTS.push({ + url: `moz-extension://${extension.uuid}/test_page.html`, + icon: ICONS.active, + connectionIcon: ICONS.secure, + descriptionSection: "trustpanel-header-enabled", + }); + for (let testData of TESTS) { info(`Testing state of for ${testData.url}`); const tab = await BrowserTestUtils.openNewForegroundTab({ gBrowser, opening: testData.url, - waitForLoad: testData.waitForLoad ?? true, }); - let doc = tab.ownerDocument; - let icon = doc.defaultView.getComputedStyle( - doc.getElementById("trust-icon") - ).listStyleImage; - let iconUrl = icon.match(/url\("([^"]+)"\)/)?.[1] ?? null; + Assert.equal( + fetchIconUrl(tab.ownerDocument, "trust-icon"), + testData.icon, + "Trustpanel urlbar icon is correct" + ); + + await UrlbarTestUtils.openTrustPanel(window); + Assert.equal( + fetchIconUrl(tab.ownerDocument, "trustpanel-connection-icon"), + testData.connectionIcon, + "Trustpanel connection icon is correct" + ); - Assert.equal(iconUrl, testData.icon, "Trustpanel urlbar icon is correct"); + Assert.ok( + BrowserTestUtils.isVisible( + tab.ownerDocument.querySelector( + `label[data-l10n-id=${testData.descriptionSection}]` + ) + ), + "Expected description section is visible" + ); + await UrlbarTestUtils.closeTrustPanel(window); BrowserTestUtils.removeTab(tab); } + + await extension.unload(); }); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_groups.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_groups.js index 36f550ff60fc1..3b4eb745015e6 100644 --- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_groups.js +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_abandonment_groups.js @@ -32,7 +32,19 @@ add_task(async function adaptive_history() { assertAbandonmentTelemetry([ { groups: "heuristic,adaptive_history", - results: "search_engine,history", + results: "search_engine,history_adaptive", + n_results: 2, + }, + ]), + }); + + await doAdaptiveHistoryBookmarkTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,adaptive_history", + results: "search_engine,bookmark_adaptive", n_results: 2, }, ]), @@ -279,6 +291,70 @@ add_task(async function history_serp() { }, ]), }); + + await doBookmarkSerpHistoryTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,general", + results: "search_engine,bookmark_serp", + n_results: 2, + }, + ]), + }); + + await doAdaptiveHistorySerpHistoryTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,adaptive_history", + results: "search_engine,history_adaptive_serp", + n_results: 2, + }, + ]), + }); + + await doAdaptiveHistoryBookmarkSerpHistoryTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,adaptive_history", + results: "search_engine,bookmark_adaptive_serp", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function tab_adaptive() { + await doTabAdaptiveTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,adaptive_history", + results: "search_engine,tab_adaptive", + n_results: 2, + }, + ]), + }); +}); + +add_task(async function tab_adaptive_serp() { + await doTabAdaptiveSerpHistoryTest({ + trigger: () => doBlur(), + assert: () => + assertAbandonmentTelemetry([ + { + groups: "heuristic,adaptive_history", + results: "search_engine,tab_adaptive_serp", + n_results: 2, + }, + ]), + }); }); add_task(async function tab_serp() { diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_groups.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_groups.js index d24169d2673f3..a4138615425ad 100644 --- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_groups.js +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_groups.js @@ -32,7 +32,7 @@ add_task(async function adaptive_history() { assertEngagementTelemetry([ { groups: "heuristic,adaptive_history", - results: "search_engine,history", + results: "search_engine,history_adaptive", n_results: 2, }, ]), diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_selected_result.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_selected_result.js index b5c80631f5625..08228060a1c7e 100644 --- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_selected_result.js +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_engagement_selected_result.js @@ -123,6 +123,101 @@ add_task(async function selected_result_bookmark() { }); }); +add_task(async function selected_result_bookmark_adaptive() { + await doTest(async () => { + await PlacesTestUtils.addVisits("https://example.com/test"); + + await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "https://example.com/test", + title: "bookmark", + }); + + await UrlbarUtils.addToInputHistory("https://example.com/test", "test"); + + await openPopup("test"); + await selectRowByProvider("UrlbarProviderInputHistory"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "bookmark_adaptive", + selected_position: 2, + provider: "UrlbarProviderInputHistory", + results: "search_engine,bookmark_adaptive", + }, + ]); + }); +}); + +add_task(async function selected_result_bookmark_serp() { + await doTest(async () => { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.autoFill", false], + ["browser.urlbar.secondaryActions.featureGate", false], + ], + }); + + let defaultEngine = await Services.search.getDefault(); + let serpUrl = defaultEngine.getSubmission("test search", null).uri.spec; + + await PlacesTestUtils.addVisits(serpUrl); + + await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: serpUrl, + title: "bookmark", + }); + + await openPopup("test"); + await selectRowByURL(serpUrl); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "bookmark_serp", + selected_position: 2, + provider: "UrlbarProviderPlaces", + results: "search_engine,bookmark_serp", + }, + ]); + }); +}); + +add_task(async function selected_result_bookmark_adaptive_serp() { + await doTest(async () => { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.autoFill", false]], + }); + + let defaultEngine = await Services.search.getDefault(); + let serpUrl = defaultEngine.getSubmission("test search", null).uri.spec; + + await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: serpUrl, + title: "bookmark", + }); + + await PlacesTestUtils.addVisits(serpUrl); + await UrlbarUtils.addToInputHistory(serpUrl, "test"); + + await openPopup("test"); + await selectRowByProvider("UrlbarProviderInputHistory"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "bookmark_adaptive_serp", + selected_position: 2, + provider: "UrlbarProviderInputHistory", + results: "search_engine,bookmark_adaptive_serp", + }, + ]); + }); +}); + add_task(async function selected_result_history() { await SpecialPowers.pushPrefEnv({ set: [["browser.urlbar.autoFill", false]], @@ -148,6 +243,49 @@ add_task(async function selected_result_history() { await SpecialPowers.popPrefEnv(); }); +add_task(async function selected_result_history_adaptive() { + await doTest(async () => { + await PlacesTestUtils.addVisits("https://example.com/test"); + await UrlbarUtils.addToInputHistory("https://example.com/test", "exa"); + + await openPopup("exa"); + await selectRowByProvider("UrlbarProviderInputHistory"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "history_adaptive", + selected_position: 2, + provider: "UrlbarProviderInputHistory", + results: "search_engine,history_adaptive", + }, + ]); + }); +}); + +add_task(async function selected_result_history_adaptive_serp() { + await doTest(async () => { + let defaultEngine = await Services.search.getDefault(); + let serpUrl = defaultEngine.getSubmission("test search", null).uri.spec; + + await PlacesTestUtils.addVisits(serpUrl); + await UrlbarUtils.addToInputHistory(serpUrl, "test sea"); + + await openPopup("test sea"); + await selectRowByProvider("UrlbarProviderInputHistory"); + await doEnter(); + + assertEngagementTelemetry([ + { + selected_result: "history_adaptive_serp", + selected_position: 2, + provider: "UrlbarProviderInputHistory", + results: "search_engine,history_adaptive_serp", + }, + ]); + }); +}); + add_task(async function selected_result_keyword() { await doTest(async () => { await PlacesUtils.keywords.insert({ @@ -280,6 +418,106 @@ add_task(async function selected_result_tab() { BrowserTestUtils.removeTab(tab); }); +add_task(async function selected_result_tab_adaptive() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.suggest.searches", false]], + }); + + const tab = BrowserTestUtils.addTab(gBrowser, "https://example.com/"); + + await doTest(async () => { + await PlacesTestUtils.addVisits("https://example.com/"); + await UrlbarUtils.addToInputHistory("https://example.com/", "exa"); + + await openPopup("exa"); + await selectRowByProvider("UrlbarProviderInputHistory"); + EventUtils.synthesizeKey("KEY_Enter"); + await BrowserTestUtils.waitForCondition(() => gBrowser.selectedTab === tab); + + assertEngagementTelemetry([ + { + selected_result: "tab_adaptive", + selected_position: 2, + provider: "UrlbarProviderInputHistory", + results: "search_engine,tab_adaptive", + }, + ]); + }); + + await SpecialPowers.popPrefEnv(); + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function selected_result_tab_adaptive_serp() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.suggest.searches", false]], + }); + + let defaultEngine = await Services.search.getDefault(); + let serpUrl = defaultEngine.getSubmission("test search", null).uri.spec; + let tab = BrowserTestUtils.addTab(gBrowser, serpUrl); + + await doTest(async () => { + await PlacesTestUtils.addVisits(serpUrl); + await UrlbarUtils.addToInputHistory(serpUrl, "test sea"); + + // For some reason, when this test is run first the provider is + // UrlbarProviderPlaces instead of InputHistory. So do a simple warm up + // operation. + await openPopup("warmup"); + await UrlbarTestUtils.promisePopupClose(window); + + await openPopup("test sea"); + await selectRowByProvider("UrlbarProviderInputHistory"); + + EventUtils.synthesizeKey("KEY_Enter"); + await BrowserTestUtils.waitForCondition(() => gBrowser.selectedTab === tab); + + assertEngagementTelemetry([ + { + selected_result: "tab_adaptive_serp", + selected_position: 2, + provider: "UrlbarProviderInputHistory", + results: "search_engine,tab_adaptive_serp", + }, + ]); + }); + + await SpecialPowers.popPrefEnv(); + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function selected_result_tab_serp() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.suggest.searches", false]], + }); + + let defaultEngine = await Services.search.getDefault(); + let serpUrl = defaultEngine.getSubmission("test search", null).uri.spec; + let tab = BrowserTestUtils.addTab(gBrowser, serpUrl); + + await doTest(async () => { + await PlacesTestUtils.addVisits(serpUrl); + + await openPopup("test sea"); + await selectRowByProvider("UrlbarProviderPlaces"); + EventUtils.synthesizeKey("KEY_Enter"); + await BrowserTestUtils.waitForCondition(() => gBrowser.selectedTab === tab); + + assertEngagementTelemetry([ + { + selected_result: "tab_serp", + selected_position: 2, + provider: "UrlbarProviderPlaces", + results: "search_engine,tab_serp", + }, + ]); + }); + + await SpecialPowers.popPrefEnv(); + BrowserTestUtils.removeTab(tab); +}); + add_task(async function selected_result_remote_tab() { const remoteTab = await loadRemoteTab("https://example.com"); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head-groups.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head-groups.js index 0d5fa23c30a87..d41dd0b5a8437 100644 --- a/browser/components/urlbar/tests/engagementTelemetry/browser/head-groups.js +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/head-groups.js @@ -37,6 +37,64 @@ async function doAdaptiveHistoryTest({ trigger, assert }) { await SpecialPowers.popPrefEnv(); } +async function doAdaptiveHistorySerpHistoryTest({ trigger, assert }) { + let defaultEngine = await Services.search.getDefault(); + let serpUrl = defaultEngine.getSubmission("test search", null).uri.spec; + + await doTest(async () => { + await PlacesTestUtils.addVisits(serpUrl); + await UrlbarUtils.addToInputHistory(serpUrl, "test sea"); + + await openPopup("test sea"); + await selectRowByProvider("UrlbarProviderInputHistory"); + + await trigger(); + await assert(); + }); +} + +async function doAdaptiveHistoryBookmarkTest({ trigger, assert }) { + await doTest(async () => { + await PlacesTestUtils.addVisits("https://example.com/test"); + + await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "https://example.com/test", + title: "bookmark", + }); + + await UrlbarUtils.addToInputHistory("https://example.com/test", "test"); + + await openPopup("test"); + await selectRowByProvider("UrlbarProviderInputHistory"); + + await trigger(); + await assert(); + }); +} + +async function doAdaptiveHistoryBookmarkSerpHistoryTest({ trigger, assert }) { + await doTest(async () => { + let defaultEngine = await Services.search.getDefault(); + let serpUrl = defaultEngine.getSubmission("test search", null).uri.spec; + + await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: serpUrl, + title: "bookmark", + }); + + await PlacesTestUtils.addVisits(serpUrl); + await UrlbarUtils.addToInputHistory(serpUrl, "test"); + + await openPopup("test"); + await selectRowByProvider("UrlbarProviderInputHistory"); + + await trigger(); + await assert(); + }); +} + async function doSearchHistoryTest({ trigger, assert }) { await SpecialPowers.pushPrefEnv({ set: [ @@ -350,6 +408,69 @@ async function doSerpHistoryTest({ trigger, assert }) { }); } +async function doBookmarkSerpHistoryTest({ trigger, assert }) { + await doTest(async () => { + let defaultEngine = await Services.search.getDefault(); + let serpUrl = defaultEngine.getSubmission("test search", null).uri.spec; + + await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: serpUrl, + title: "bookmark", + }); + + await openPopup("test"); + await selectRowByURL(serpUrl); + + await trigger(); + await assert(); + }); +} + +async function doTabAdaptiveTest({ trigger, assert }) { + let visited = PlacesTestUtils.waitForNotification("page-visited", visits => + visits.some(({ url }) => url == "https://example.com/") + ); + let tab = BrowserTestUtils.addTab(gBrowser, "https://example.com/"); + await visited; + + await doTest(async () => { + await PlacesTestUtils.addVisits("https://example.com/"); + await UrlbarUtils.addToInputHistory("https://example.com/", "exa"); + + await openPopup("exa"); + await selectRowByProvider("UrlbarProviderInputHistory"); + + await trigger(); + await assert(); + }); + + BrowserTestUtils.removeTab(tab); +} + +async function doTabAdaptiveSerpHistoryTest({ trigger, assert }) { + let defaultEngine = await Services.search.getDefault(); + const searchUrl = defaultEngine.getSubmission("serp history", null).uri.spec; + let visited = PlacesTestUtils.waitForNotification("page-visited", visits => + visits.some(({ url }) => url == searchUrl) + ); + let tab = BrowserTestUtils.addTab(gBrowser, searchUrl); + await visited; + + await doTest(async () => { + await PlacesTestUtils.addVisits(searchUrl); + await UrlbarUtils.addToInputHistory(searchUrl, "serp"); + + await openPopup("serp"); + await selectRowByURL(searchUrl); + + await trigger(); + await assert(); + }); + + BrowserTestUtils.removeTab(tab); +} + async function doTabSerpHistoryTest({ trigger, assert }) { let defaultEngine = await Services.search.getDefault(); const searchUrl = defaultEngine.getSubmission("serp history", null).uri.spec; diff --git a/browser/extensions/ipp-activator/extension/api/parent/ext-ipp.js b/browser/extensions/ipp-activator/extension/api/parent/ext-ipp.js index 2499008fe0449..f0a9554d3d8ea 100644 --- a/browser/extensions/ipp-activator/extension/api/parent/ext-ipp.js +++ b/browser/extensions/ipp-activator/extension/api/parent/ext-ipp.js @@ -7,6 +7,8 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs", + IPPExceptionsManager: + "moz-src:///browser/components/ipprotection/IPPExceptionsManager.sys.mjs", IPPProxyManager: "moz-src:///browser/components/ipprotection/IPPProxyManager.sys.mjs", IPPProxyStates: @@ -164,6 +166,25 @@ this.ippActivator = class extends ExtensionAPI { return { baseDomain: "", host: "" }; } }, + hasExclusion(url) { + if ( + !Services.prefs.getBoolPref( + "browser.ipProtection.features.siteExceptions", + false + ) + ) { + return false; + } + + try { + const uri = Services.io.newURI(url); + const principal = + Services.scriptSecurityManager.createContentPrincipal(uri, {}); + return lazy.IPPExceptionsManager.hasExclusion(principal); + } catch (e) { + return false; + } + }, async showMessage(message, tabId) { try { // Choose the target tab (by id if provided, else active tab) diff --git a/browser/extensions/ipp-activator/extension/api/schemas/ipp.json b/browser/extensions/ipp-activator/extension/api/schemas/ipp.json index 348430da4040b..618a1c7cb91cf 100644 --- a/browser/extensions/ipp-activator/extension/api/schemas/ipp.json +++ b/browser/extensions/ipp-activator/extension/api/schemas/ipp.json @@ -106,6 +106,19 @@ "description": "The URL to analyze." } ] + }, + { + "name": "hasExclusion", + "type": "function", + "description": "Return true if this URL has ipp-vpn permission set to DENY.", + "async": true, + "parameters": [ + { + "name": "url", + "type": "string", + "description": "The URL to analyze." + } + ] } ], "events": [ diff --git a/browser/extensions/ipp-activator/extension/bg.js b/browser/extensions/ipp-activator/extension/bg.js index ece81ffdb6c4f..acc6bdb0b9a70 100644 --- a/browser/extensions/ipp-activator/extension/bg.js +++ b/browser/extensions/ipp-activator/extension/bg.js @@ -290,6 +290,10 @@ class IPPAddonActivator { return false; } + if (await browser.ippActivator.hasExclusion(url)) { + return false; + } + let domain = info.baseDomain; let breakage = breakages.find( b => Array.isArray(b.domains) && b.domains.includes(info.baseDomain) diff --git a/browser/extensions/newtab/common/Actions.mjs b/browser/extensions/newtab/common/Actions.mjs index 201ae2da315fb..05a6faebe838a 100644 --- a/browser/extensions/newtab/common/Actions.mjs +++ b/browser/extensions/newtab/common/Actions.mjs @@ -84,6 +84,7 @@ for (const type of [ "DISCOVERY_STREAM_SPOCS_UPDATE", "DISCOVERY_STREAM_SPOC_BLOCKED", "DISCOVERY_STREAM_SPOC_IMPRESSION", + "DISCOVERY_STREAM_SPOC_PLACEHOLDER_DURATION", "DISCOVERY_STREAM_TOPICS_LOADING", "DISCOVERY_STREAM_USER_EVENT", "DOWNLOAD_CHANGED", diff --git a/browser/extensions/newtab/content-src/components/Base/Base.jsx b/browser/extensions/newtab/content-src/components/Base/Base.jsx index 20e70748bcd87..839202082ac91 100644 --- a/browser/extensions/newtab/content-src/components/Base/Base.jsx +++ b/browser/extensions/newtab/content-src/components/Base/Base.jsx @@ -123,6 +123,7 @@ export class BaseContent extends React.PureComponent { visible: false, showSectionsMgmtPanel: false, }; + this.spocPlaceholderStartTime = null; } setFirstVisibleTimestamp() { @@ -140,6 +141,10 @@ export class BaseContent extends React.PureComponent { this.setFirstVisibleTimestamp(); this.shouldDisplayTopicSelectionModal(); this.onVisibilityDispatch(); + + if (this.isSpocsOnDemandExpired && !this.spocPlaceholderStartTime) { + this.spocPlaceholderStartTime = Date.now(); + } } onVisibilityDispatch() { @@ -333,6 +338,36 @@ export class BaseContent extends React.PureComponent { } this.spocsOnDemandUpdated(); + this.trackSpocPlaceholderDuration(prevProps); + } + + trackSpocPlaceholderDuration(prevProps) { + // isExpired returns true when the current props have expired spocs (showing placeholders) + const isExpired = this.isSpocsOnDemandExpired; + + // Init tracking when placeholders become visible + if (isExpired && this.state.visible && !this.spocPlaceholderStartTime) { + this.spocPlaceholderStartTime = Date.now(); + } + + // wasExpired returns true when the previous props had expired spocs (showing placeholders) + const wasExpired = + prevProps.DiscoveryStream.spocs.onDemand?.enabled && + !prevProps.DiscoveryStream.spocs.onDemand?.loaded && + Date.now() - prevProps.DiscoveryStream.spocs.lastUpdated >= + prevProps.DiscoveryStream.spocs.cacheUpdateTime; + + // Record duration telemetry event when placeholders are replaced with real content + if (wasExpired && !isExpired && this.spocPlaceholderStartTime) { + const duration = Date.now() - this.spocPlaceholderStartTime; + this.props.dispatch( + ac.OnlyToMain({ + type: at.DISCOVERY_STREAM_SPOC_PLACEHOLDER_DURATION, + data: { duration }, + }) + ); + this.spocPlaceholderStartTime = null; + } } handleColorModeChange() { diff --git a/browser/extensions/newtab/content-src/components/DiscoveryStreamAdmin/DiscoveryStreamAdmin.jsx b/browser/extensions/newtab/content-src/components/DiscoveryStreamAdmin/DiscoveryStreamAdmin.jsx index 766b6eb653133..feaad942fadee 100644 --- a/browser/extensions/newtab/content-src/components/DiscoveryStreamAdmin/DiscoveryStreamAdmin.jsx +++ b/browser/extensions/newtab/content-src/components/DiscoveryStreamAdmin/DiscoveryStreamAdmin.jsx @@ -363,7 +363,7 @@ export class DiscoveryStreamAdminUI extends React.PureComponent { sendConversionEvent() { const detail = { - partnerId: "demo-partner", + partnerId: "295BEEF7-1E3B-4128-B8F8-858E12AA660B", lookbackDays: 7, impressionType: "default", }; diff --git a/browser/extensions/newtab/content-src/components/Weather/Weather.jsx b/browser/extensions/newtab/content-src/components/Weather/Weather.jsx index c6c120cc80ca0..4b1ae8d67edd8 100644 --- a/browser/extensions/newtab/content-src/components/Weather/Weather.jsx +++ b/browser/extensions/newtab/content-src/components/Weather/Weather.jsx @@ -285,6 +285,14 @@ export class _Weather extends React.PureComponent { const weatherOptIn = Prefs.values["system.showWeatherOptIn"]; const nimbusWeatherOptInEnabled = Prefs.values.trainhopConfig?.weather?.weatherOptInEnabled; + // Bug 2009484: Controls button order in opt-in dialog for A/B testing. + // When true, "Not now" gets slot="primary"; + // when false/undefined, "Yes" gets slot="primary". + // Also note the primary button's position varies by platform: + // on Windows, it appears on the left, + // while on Linux and macOS, it appears on the right. + const reverseOptInButtons = + Prefs.values.trainhopConfig?.weather?.reverseOptInButtons; const optInDisplayed = Prefs.values["weather.optInDisplayed"]; const optInUserChoice = Prefs.values["weather.optInAccepted"]; @@ -470,16 +478,18 @@ export class _Weather extends React.PureComponent { diff --git a/browser/extensions/newtab/content-src/components/Widgets/WeatherForecast/WeatherForecast.jsx b/browser/extensions/newtab/content-src/components/Widgets/WeatherForecast/WeatherForecast.jsx new file mode 100644 index 0000000000000..38aa9d6ea9b14 --- /dev/null +++ b/browser/extensions/newtab/content-src/components/Widgets/WeatherForecast/WeatherForecast.jsx @@ -0,0 +1,122 @@ +/* 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 { useSelector } from "react-redux"; + +function WeatherForecast() { + const prefs = useSelector(state => state.Prefs.values); + const weatherData = useSelector(state => state.Weather); + + const WEATHER_SUGGESTION = weatherData.suggestions?.[0]; + + const showDetailedView = prefs["weather.display"] === "detailed"; + + if (!showDetailedView || !weatherData?.initialized) { + return null; + } + + return ( +
+
+

{weatherData.locationData.city}

+
+
+
+ +
+
+ + { + WEATHER_SUGGESTION.current_conditions.temperature[ + prefs["weather.temperatureUnits"] + ] + } + °{prefs["weather.temperatureUnits"]} + + + {WEATHER_SUGGESTION.current_conditions.summary} + +
+
+ + + { + WEATHER_SUGGESTION.forecast.high[ + prefs["weather.temperatureUnits"] + ] + } + ° + + + + + {WEATHER_SUGGESTION.forecast.low[prefs["weather.temperatureUnits"]]} + ° + +
+
+
+
+

+
    +
  • + 80° + + 7:00 +
  • +
  • + 80° + + 7:00 +
  • +
  • + 80° + + 7:00 +
  • +
  • + 80° + + 7:00 +
  • +
  • + 80° + + 7:00 +
  • +
+
+ +
+ + +
+
+ ); +} + +export { WeatherForecast }; diff --git a/browser/extensions/newtab/content-src/components/Widgets/WeatherForecast/WeatherForecast.scss b/browser/extensions/newtab/content-src/components/Widgets/WeatherForecast/WeatherForecast.scss new file mode 100644 index 0000000000000..a0ce5a308782f --- /dev/null +++ b/browser/extensions/newtab/content-src/components/Widgets/WeatherForecast/WeatherForecast.scss @@ -0,0 +1,279 @@ +.weather-forecast-widget { + @include newtab-card-style; + + grid-column: span 1; + width: var(--newtab-card-grid-layout-width); + + // Match the new card width if sections are enabled + .has-sections-grid & { + width: var(--newtab-card-width-medium); + } + + border-radius: var(--border-radius-large); + padding: var(--space-medium); + height: var(--newtab-card-height); + display: flex; + flex-direction: column; + box-shadow: var(--box-shadow-card); + + --weather-sunny-background-color: light-dark(var(--color-yellow-0), var(--color-yellow-90)); + --weather-cloudy-background-color: light-dark(var(--color-gray-20), var(--color-gray-80)); + --weather-rainy-background-color: light-dark(var(--color-blue-0), var(--color-blue-90)); + --weather-snowy-background-color: light-dark(var(--color-violet-0), var(--color-violet-90)); + --weather-hot-background-color: light-dark(var(--color-orange-0), var(--color-orange-90)); + + .city-wrapper { + display: flex; + align-items: center; + + h3 { + margin-block: 0; + font-weight: var(--font-weight-semibold); + } + } + + .current-weather-wrapper { + display: flex; + align-items: center; + margin-block: var(--space-small); + padding: var(--space-medium); + border-radius: var(--border-radius-medium); + width: 100%; + + // Setting background color based on temperature icon + &:has(.iconId1, .iconId2, .iconId3, .iconId4, .iconId5, .iconId6, .iconId33) { + background-color: var(--weather-sunny-background-color); + } + + &:has(.iconId7, .iconId8, .iconI11, .iconId32, .iconId34, .iconId35, .iconId36, .iconId37, .iconId38) { + background-color: var(--weather-cloudy-background-color); + } + + &:has(.iconId12, .iconId13, .iconId14, .iconId15, .iconId16, .iconId17, .iconId18, .iconId39, .iconId40, .iconId41, .iconId42) { + background-color: var(--weather-rainy-background-color); + } + + &:has(.iconId19, .iconId20, .iconId21, .iconId22, .iconId23, .iconId24, .iconId25, .iconId26, .iconId29, .iconId31, .iconId43, .iconId44 ) { + background-color: var(--weather-snowy-background-color); + } + + &:has(.iconId30) { + background-color: var(--weather-hot-background-color); + } + } + + // Weather Symbol Icons + .weather-icon-column { + width: var(--size-item-large); + height: var(--size-item-large); + display: flex; + align-items: center; + justify-content: center; + align-self: center; + } + + .weather-icon { + width: var(--size-item-large); + height: auto; + vertical-align: middle; + + @media (prefers-contrast) { + -moz-context-properties: fill, stroke; + fill: currentColor; + stroke: currentColor; + } + + &.iconId1 { + content: url('chrome://browser/skin/weather/sunny.svg'); + } + + &.iconId2 { + content: url('chrome://browser/skin/weather/mostly-sunny.svg'); + } + + &:is(.iconId3, .iconId4, .iconId6) { + content: url('chrome://browser/skin/weather/partly-sunny.svg'); + } + + &.iconId5 { + content: url('chrome://browser/skin/weather/hazy-sunshine.svg'); + } + + &:is(.iconId7, .iconId8) { + content: url('chrome://browser/skin/weather/cloudy.svg'); + } + + &.iconId11 { + content: url('chrome://browser/skin/weather/fog.svg'); + } + + &.iconId12 { + content: url('chrome://browser/skin/weather/showers.svg'); + } + + &:is(.iconId13, .iconId14) { + content: url('chrome://browser/skin/weather/mostly-cloudy-with-showers.svg'); + } + + &.iconId15 { + content: url('chrome://browser/skin/weather/thunderstorms.svg'); + } + + &:is(.iconId16, .iconId17) { + content: url('chrome://browser/skin/weather/mostly-cloudy-with-thunderstorms.svg'); + } + + &.iconId18 { + content: url('chrome://browser/skin/weather/rain.svg'); + } + + &:is(.iconId19, .iconId20, .iconId25) { + content: url('chrome://browser/skin/weather/flurries.svg'); + } + + &.iconId21 { + content: url('chrome://browser/skin/weather/partly-sunny-with-flurries.svg'); + } + + &:is(.iconId22, .iconId23) { + content: url('chrome://browser/skin/weather/snow.svg'); + } + + &:is(.iconId24, .iconId31) { + content: url('chrome://browser/skin/weather/ice.svg'); + } + + &:is(.iconId26, .iconId29) { + content: url('chrome://browser/skin/weather/freezing-rain.svg'); + } + + &.iconId30 { + content: url('chrome://browser/skin/weather/hot.svg'); + } + + &.iconId32 { + content: url('chrome://browser/skin/weather/windy.svg'); + } + + &.iconId33 { + content: url('chrome://browser/skin/weather/night-clear.svg'); + } + + &:is(.iconId34, .iconId35, .iconId36, .iconId38) { + content: url('chrome://browser/skin/weather/night-mostly-clear.svg'); + } + + &.iconId37 { + content: url('chrome://browser/skin/weather/night-hazy-moonlight.svg'); + } + + &:is(.iconId39, .iconId40) { + content: url('chrome://browser/skin/weather/night-partly-cloudy-with-showers.svg'); + height: var(--size-item-large); + } + + &:is(.iconId41, .iconId42) { + content: url('chrome://browser/skin/weather/night-partly-cloudy-with-thunderstorms.svg'); + } + + &:is(.iconId43, .iconId44) { + content: url('chrome://browser/skin/weather/night-mostly-cloudy-with-flurries.svg'); + } + } +} + +.weather-info-column { + display: flex; + flex-direction: column; + margin-inline: var(--space-medium); + + .temperature-unit { + text-transform: uppercase; + font-weight: var(--font-weight-semibold); + } + + .temperature-description { + font-size: var(--font-size-small); + color: var(--text-color-deemphasized); + } +} + +.high-low-column { + display: flex; + align-items: flex-end; + align-self: flex-start; + margin-inline-start: auto; + color: var(--text-color-deemphasized); + + .high-temperature { + margin-inline-end: var(--space-small); + } + + .arrow-icon { + display: inline-block; + width: var(--size-item-xsmall); + height: var(--size-item-xsmall); + background-size: contain; + background-repeat: no-repeat; + background-position: center; + -moz-context-properties: fill, fill-opacity; + fill: currentColor; + + &.arrow-up { + background-image: url('chrome://global/skin/icons/shaft-arrow-up.svg'); + } + + &.arrow-down { + background-image: url('chrome://global/skin/icons/shaft-arrow-down.svg'); + } + } +} + +hr { + border: 0; + border-block-start: 1px solid var(--border-color); + width: 100%; +} + +.forecast-row { + .today-forecast { + margin-block-start: 0; + font-weight: var(--font-weight-semibold); + } + + .forecast-row-items { + list-style: none; + display: flex; + justify-content: center; + padding: 0; + margin-block-start: var(--space-small); + + li { + background: var(--newtab-background-color); + border-radius: var(--border-radius-small); + display: flex; + flex-direction: column; + align-items: center; + margin-inline: var(--space-xsmall); + padding-inline: var(--space-small); + padding-block: var(--space-xsmall); + font-size: var(--font-size-small); + } + } +} + +.weather-forecast-footer { + display: flex; + align-items: center; + justify-content: space-between; + margin-block-start: auto; + + a { + font-size: var(--font-size-small); + } + + span { + font-size: var(--font-size-xsmall); + } +} + diff --git a/browser/extensions/newtab/content-src/components/Widgets/Widgets.jsx b/browser/extensions/newtab/content-src/components/Widgets/Widgets.jsx index b5a2dccd2b233..9ebb62d3c59ef 100644 --- a/browser/extensions/newtab/content-src/components/Widgets/Widgets.jsx +++ b/browser/extensions/newtab/content-src/components/Widgets/Widgets.jsx @@ -6,6 +6,7 @@ import React, { useEffect, useRef } from "react"; import { useDispatch, useSelector, batch } from "react-redux"; import { Lists } from "./Lists/Lists"; import { FocusTimer } from "./FocusTimer/FocusTimer"; +import { WeatherForecast } from "./WeatherForecast/WeatherForecast"; import { MessageWrapper } from "content-src/components/MessageWrapper/MessageWrapper"; import { WidgetsFeatureHighlight } from "../DiscoveryStreamComponents/FeatureHighlight/WidgetsFeatureHighlight"; import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; @@ -14,6 +15,9 @@ const PREF_WIDGETS_LISTS_ENABLED = "widgets.lists.enabled"; const PREF_WIDGETS_SYSTEM_LISTS_ENABLED = "widgets.system.lists.enabled"; const PREF_WIDGETS_TIMER_ENABLED = "widgets.focusTimer.enabled"; const PREF_WIDGETS_SYSTEM_TIMER_ENABLED = "widgets.system.focusTimer.enabled"; +const PREF_WIDGETS_WEATHER_FORECAST_ENABLED = "widgets.weatherForecast.enabled"; +const PREF_WIDGETS_SYSTEM_WEATHER_FORECAST_ENABLED = + "widgets.system.weatherForecast.enabled"; const PREF_WIDGETS_MAXIMIZED = "widgets.maximized"; const PREF_WIDGETS_SYSTEM_MAXIMIZED = "widgets.system.maximized"; @@ -59,10 +63,14 @@ function Widgets() { const nimbusListsEnabled = prefs.widgetsConfig?.listsEnabled; const nimbusTimerEnabled = prefs.widgetsConfig?.timerEnabled; + const nimbusWeatherForecastEnabled = + prefs.widgetsConfig?.weatherForecastEnabled; const nimbusListsTrainhopEnabled = prefs.trainhopConfig?.widgets?.listsEnabled; const nimbusTimerTrainhopEnabled = prefs.trainhopConfig?.widgets?.timerEnabled; + const nimbusWeatherForecastTrainhopEnabled = + prefs.trainhopConfig?.widgets?.weatherForecastEnabled; const listsEnabled = (nimbusListsTrainhopEnabled || @@ -76,6 +84,12 @@ function Widgets() { prefs[PREF_WIDGETS_SYSTEM_TIMER_ENABLED]) && prefs[PREF_WIDGETS_TIMER_ENABLED]; + const weatherForecastEnabled = + (nimbusWeatherForecastTrainhopEnabled || + nimbusWeatherForecastEnabled || + prefs[PREF_WIDGETS_SYSTEM_WEATHER_FORECAST_ENABLED]) && + prefs[PREF_WIDGETS_WEATHER_FORECAST_ENABLED]; + // track previous timerEnabled state to detect when it becomes disabled const prevTimerEnabledRef = useRef(timerEnabled); @@ -134,7 +148,7 @@ function Widgets() { } } - if (!(listsEnabled || timerEnabled)) { + if (!(listsEnabled || timerEnabled || weatherForecastEnabled)) { return null; } @@ -186,6 +200,13 @@ function Widgets() { isMaximized={isMaximized} /> )} + {weatherForecastEnabled && ( + + )} {messageData?.content?.messageType === "WidgetMessage" && ( diff --git a/browser/extensions/newtab/content-src/styles/activity-stream.scss b/browser/extensions/newtab/content-src/styles/activity-stream.scss index c5ae0b586cc40..d3ffc2d7ceb07 100644 --- a/browser/extensions/newtab/content-src/styles/activity-stream.scss +++ b/browser/extensions/newtab/content-src/styles/activity-stream.scss @@ -170,6 +170,7 @@ input { @import '../components/Widgets/Widgets'; @import '../components/Widgets/Lists/Lists'; @import '../components/Widgets/FocusTimer/FocusTimer'; +@import '../components/Widgets/WeatherForecast/WeatherForecast'; // Discovery Stream Components @import '../components/DiscoveryStreamComponents/CardGrid/CardGrid'; diff --git a/browser/extensions/newtab/css/activity-stream.css b/browser/extensions/newtab/css/activity-stream.css index b151260359e00..1674adf61b978 100644 --- a/browser/extensions/newtab/css/activity-stream.css +++ b/browser/extensions/newtab/css/activity-stream.css @@ -4684,6 +4684,237 @@ dialog:dir(rtl)::after { margin-block-start: var(--space-small); } +.weather-forecast-widget { + background: var(--newtab-background-card); + transition: opacity 0.2s ease; + grid-column: span 1; + width: var(--newtab-card-grid-layout-width); + border-radius: var(--border-radius-large); + padding: var(--space-medium); + height: var(--newtab-card-height); + display: flex; + flex-direction: column; + box-shadow: var(--box-shadow-card); + --weather-sunny-background-color: light-dark(var(--color-yellow-0), var(--color-yellow-90)); + --weather-cloudy-background-color: light-dark(var(--color-gray-20), var(--color-gray-80)); + --weather-rainy-background-color: light-dark(var(--color-blue-0), var(--color-blue-90)); + --weather-snowy-background-color: light-dark(var(--color-violet-0), var(--color-violet-90)); + --weather-hot-background-color: light-dark(var(--color-orange-0), var(--color-orange-90)); +} +.weather-forecast-widget:hover { + background: var(--newtab-background-color-secondary); +} +.has-sections-grid .weather-forecast-widget { + width: var(--newtab-card-width-medium); +} +.weather-forecast-widget .city-wrapper { + display: flex; + align-items: center; +} +.weather-forecast-widget .city-wrapper h3 { + margin-block: 0; + font-weight: var(--font-weight-semibold); +} +.weather-forecast-widget .current-weather-wrapper { + display: flex; + align-items: center; + margin-block: var(--space-small); + padding: var(--space-medium); + border-radius: var(--border-radius-medium); + width: 100%; +} +.weather-forecast-widget .current-weather-wrapper:has(.iconId1, .iconId2, .iconId3, .iconId4, .iconId5, .iconId6, .iconId33) { + background-color: var(--weather-sunny-background-color); +} +.weather-forecast-widget .current-weather-wrapper:has(.iconId7, .iconId8, .iconI11, .iconId32, .iconId34, .iconId35, .iconId36, .iconId37, .iconId38) { + background-color: var(--weather-cloudy-background-color); +} +.weather-forecast-widget .current-weather-wrapper:has(.iconId12, .iconId13, .iconId14, .iconId15, .iconId16, .iconId17, .iconId18, .iconId39, .iconId40, .iconId41, .iconId42) { + background-color: var(--weather-rainy-background-color); +} +.weather-forecast-widget .current-weather-wrapper:has(.iconId19, .iconId20, .iconId21, .iconId22, .iconId23, .iconId24, .iconId25, .iconId26, .iconId29, .iconId31, .iconId43, .iconId44) { + background-color: var(--weather-snowy-background-color); +} +.weather-forecast-widget .current-weather-wrapper:has(.iconId30) { + background-color: var(--weather-hot-background-color); +} +.weather-forecast-widget .weather-icon-column { + width: var(--size-item-large); + height: var(--size-item-large); + display: flex; + align-items: center; + justify-content: center; + align-self: center; +} +.weather-forecast-widget .weather-icon { + width: var(--size-item-large); + height: auto; + vertical-align: middle; +} +@media (prefers-contrast) { + .weather-forecast-widget .weather-icon { + -moz-context-properties: fill, stroke; + fill: currentColor; + stroke: currentColor; + } +} +.weather-forecast-widget .weather-icon.iconId1 { + content: url("chrome://browser/skin/weather/sunny.svg"); +} +.weather-forecast-widget .weather-icon.iconId2 { + content: url("chrome://browser/skin/weather/mostly-sunny.svg"); +} +.weather-forecast-widget .weather-icon:is(.iconId3, .iconId4, .iconId6) { + content: url("chrome://browser/skin/weather/partly-sunny.svg"); +} +.weather-forecast-widget .weather-icon.iconId5 { + content: url("chrome://browser/skin/weather/hazy-sunshine.svg"); +} +.weather-forecast-widget .weather-icon:is(.iconId7, .iconId8) { + content: url("chrome://browser/skin/weather/cloudy.svg"); +} +.weather-forecast-widget .weather-icon.iconId11 { + content: url("chrome://browser/skin/weather/fog.svg"); +} +.weather-forecast-widget .weather-icon.iconId12 { + content: url("chrome://browser/skin/weather/showers.svg"); +} +.weather-forecast-widget .weather-icon:is(.iconId13, .iconId14) { + content: url("chrome://browser/skin/weather/mostly-cloudy-with-showers.svg"); +} +.weather-forecast-widget .weather-icon.iconId15 { + content: url("chrome://browser/skin/weather/thunderstorms.svg"); +} +.weather-forecast-widget .weather-icon:is(.iconId16, .iconId17) { + content: url("chrome://browser/skin/weather/mostly-cloudy-with-thunderstorms.svg"); +} +.weather-forecast-widget .weather-icon.iconId18 { + content: url("chrome://browser/skin/weather/rain.svg"); +} +.weather-forecast-widget .weather-icon:is(.iconId19, .iconId20, .iconId25) { + content: url("chrome://browser/skin/weather/flurries.svg"); +} +.weather-forecast-widget .weather-icon.iconId21 { + content: url("chrome://browser/skin/weather/partly-sunny-with-flurries.svg"); +} +.weather-forecast-widget .weather-icon:is(.iconId22, .iconId23) { + content: url("chrome://browser/skin/weather/snow.svg"); +} +.weather-forecast-widget .weather-icon:is(.iconId24, .iconId31) { + content: url("chrome://browser/skin/weather/ice.svg"); +} +.weather-forecast-widget .weather-icon:is(.iconId26, .iconId29) { + content: url("chrome://browser/skin/weather/freezing-rain.svg"); +} +.weather-forecast-widget .weather-icon.iconId30 { + content: url("chrome://browser/skin/weather/hot.svg"); +} +.weather-forecast-widget .weather-icon.iconId32 { + content: url("chrome://browser/skin/weather/windy.svg"); +} +.weather-forecast-widget .weather-icon.iconId33 { + content: url("chrome://browser/skin/weather/night-clear.svg"); +} +.weather-forecast-widget .weather-icon:is(.iconId34, .iconId35, .iconId36, .iconId38) { + content: url("chrome://browser/skin/weather/night-mostly-clear.svg"); +} +.weather-forecast-widget .weather-icon.iconId37 { + content: url("chrome://browser/skin/weather/night-hazy-moonlight.svg"); +} +.weather-forecast-widget .weather-icon:is(.iconId39, .iconId40) { + content: url("chrome://browser/skin/weather/night-partly-cloudy-with-showers.svg"); + height: var(--size-item-large); +} +.weather-forecast-widget .weather-icon:is(.iconId41, .iconId42) { + content: url("chrome://browser/skin/weather/night-partly-cloudy-with-thunderstorms.svg"); +} +.weather-forecast-widget .weather-icon:is(.iconId43, .iconId44) { + content: url("chrome://browser/skin/weather/night-mostly-cloudy-with-flurries.svg"); +} + +.weather-info-column { + display: flex; + flex-direction: column; + margin-inline: var(--space-medium); +} +.weather-info-column .temperature-unit { + text-transform: uppercase; + font-weight: var(--font-weight-semibold); +} +.weather-info-column .temperature-description { + font-size: var(--font-size-small); + color: var(--text-color-deemphasized); +} + +.high-low-column { + display: flex; + align-items: flex-end; + align-self: flex-start; + margin-inline-start: auto; + color: var(--text-color-deemphasized); +} +.high-low-column .high-temperature { + margin-inline-end: var(--space-small); +} +.high-low-column .arrow-icon { + display: inline-block; + width: var(--size-item-xsmall); + height: var(--size-item-xsmall); + background-size: contain; + background-repeat: no-repeat; + background-position: center; + -moz-context-properties: fill, fill-opacity; + fill: currentColor; +} +.high-low-column .arrow-icon.arrow-up { + background-image: url("chrome://global/skin/icons/shaft-arrow-up.svg"); +} +.high-low-column .arrow-icon.arrow-down { + background-image: url("chrome://global/skin/icons/shaft-arrow-down.svg"); +} + +hr { + border: 0; + border-block-start: 1px solid var(--border-color); + width: 100%; +} + +.forecast-row .today-forecast { + margin-block-start: 0; + font-weight: var(--font-weight-semibold); +} +.forecast-row .forecast-row-items { + list-style: none; + display: flex; + justify-content: center; + padding: 0; + margin-block-start: var(--space-small); +} +.forecast-row .forecast-row-items li { + background: var(--newtab-background-color); + border-radius: var(--border-radius-small); + display: flex; + flex-direction: column; + align-items: center; + margin-inline: var(--space-xsmall); + padding-inline: var(--space-small); + padding-block: var(--space-xsmall); + font-size: var(--font-size-small); +} + +.weather-forecast-footer { + display: flex; + align-items: center; + justify-content: space-between; + margin-block-start: auto; +} +.weather-forecast-footer a { + font-size: var(--font-size-small); +} +.weather-forecast-footer span { + font-size: var(--font-size-xsmall); +} + .ds-card-grid .ds-card { background: var(--newtab-background-color-secondary); border-radius: var(--border-radius-small); diff --git a/browser/extensions/newtab/data/content/activity-stream.bundle.js b/browser/extensions/newtab/data/content/activity-stream.bundle.js index fc93ed47ba6f4..725cb7c7cf1b1 100644 --- a/browser/extensions/newtab/data/content/activity-stream.bundle.js +++ b/browser/extensions/newtab/data/content/activity-stream.bundle.js @@ -157,6 +157,7 @@ for (const type of [ "DISCOVERY_STREAM_SPOCS_UPDATE", "DISCOVERY_STREAM_SPOC_BLOCKED", "DISCOVERY_STREAM_SPOC_IMPRESSION", + "DISCOVERY_STREAM_SPOC_PLACEHOLDER_DURATION", "DISCOVERY_STREAM_TOPICS_LOADING", "DISCOVERY_STREAM_USER_EVENT", "DOWNLOAD_CHANGED", @@ -892,7 +893,7 @@ class DiscoveryStreamAdminUI extends (external_React_default()).PureComponent { } sendConversionEvent() { const detail = { - partnerId: "demo-partner", + partnerId: "295BEEF7-1E3B-4128-B8F8-858E12AA660B", lookbackDays: 7, impressionType: "default" }; @@ -11030,6 +11031,13 @@ class _Weather extends (external_React_default()).PureComponent { const showDetailedView = Prefs.values["weather.display"] === "detailed"; const weatherOptIn = Prefs.values["system.showWeatherOptIn"]; const nimbusWeatherOptInEnabled = Prefs.values.trainhopConfig?.weather?.weatherOptInEnabled; + // Bug 2009484: Controls button order in opt-in dialog for A/B testing. + // When true, "Not now" gets slot="primary"; + // when false/undefined, "Yes" gets slot="primary". + // Also note the primary button's position varies by platform: + // on Windows, it appears on the left, + // while on Linux and macOS, it appears on the right. + const reverseOptInButtons = Prefs.values.trainhopConfig?.weather?.reverseOptInButtons; const optInDisplayed = Prefs.values["weather.optInDisplayed"]; const optInUserChoice = Prefs.values["weather.optInAccepted"]; const staticWeather = Prefs.values["weather.staticData.enabled"]; @@ -11146,15 +11154,17 @@ class _Weather extends (external_React_default()).PureComponent { }, /*#__PURE__*/external_React_default().createElement("moz-button", { size: "small", type: "default", - "data-l10n-id": "newtab-weather-opt-in-not-now", - onClick: this.handleRejectOptIn, - id: "reject-opt-in" + "data-l10n-id": "newtab-weather-opt-in-yes", + onClick: this.handleAcceptOptIn, + id: "accept-opt-in", + slot: reverseOptInButtons ? "" : "primary" }), /*#__PURE__*/external_React_default().createElement("moz-button", { size: "small", type: "default", - "data-l10n-id": "newtab-weather-opt-in-yes", - onClick: this.handleAcceptOptIn, - id: "accept-opt-in" + "data-l10n-id": "newtab-weather-opt-in-not-now", + onClick: this.handleRejectOptIn, + id: "reject-opt-in", + slot: reverseOptInButtons ? "primary" : "" })))))); } return /*#__PURE__*/external_React_default().createElement("div", { @@ -13064,6 +13074,76 @@ function EditableTimerFields({ tabIndex: tabIndex }, formatTime(props.timeLeft).split(":")[1])); } +;// CONCATENATED MODULE: ./content-src/components/Widgets/WeatherForecast/WeatherForecast.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/. */ + + +function WeatherForecast() { + const prefs = (0,external_ReactRedux_namespaceObject.useSelector)(state => state.Prefs.values); + const weatherData = (0,external_ReactRedux_namespaceObject.useSelector)(state => state.Weather); + const WEATHER_SUGGESTION = weatherData.suggestions?.[0]; + const showDetailedView = prefs["weather.display"] === "detailed"; + if (!showDetailedView || !weatherData?.initialized) { + return null; + } + return /*#__PURE__*/React.createElement("article", { + className: "weather-forecast-widget" + }, /*#__PURE__*/React.createElement("div", { + className: "city-wrapper" + }, /*#__PURE__*/React.createElement("h3", null, weatherData.locationData.city)), /*#__PURE__*/React.createElement("div", { + className: "current-weather-wrapper" + }, /*#__PURE__*/React.createElement("div", { + className: "weather-icon-column" + }, /*#__PURE__*/React.createElement("span", { + className: `weather-icon iconId${WEATHER_SUGGESTION.current_conditions.icon_id}` + })), /*#__PURE__*/React.createElement("div", { + className: "weather-info-column" + }, /*#__PURE__*/React.createElement("span", { + className: "temperature-unit" + }, WEATHER_SUGGESTION.current_conditions.temperature[prefs["weather.temperatureUnits"]], "\xB0", prefs["weather.temperatureUnits"]), /*#__PURE__*/React.createElement("span", { + className: "temperature-description" + }, WEATHER_SUGGESTION.current_conditions.summary)), /*#__PURE__*/React.createElement("div", { + className: "high-low-column" + }, /*#__PURE__*/React.createElement("span", { + className: "high-temperature" + }, /*#__PURE__*/React.createElement("span", { + className: "arrow-icon arrow-up" + }), WEATHER_SUGGESTION.forecast.high[prefs["weather.temperatureUnits"]], "\xB0"), /*#__PURE__*/React.createElement("span", { + className: "low-temperature" + }, /*#__PURE__*/React.createElement("span", { + className: "arrow-icon arrow-down" + }), WEATHER_SUGGESTION.forecast.low[prefs["weather.temperatureUnits"]], "\xB0"))), /*#__PURE__*/React.createElement("hr", null), /*#__PURE__*/React.createElement("div", { + className: "forecast-row" + }, /*#__PURE__*/React.createElement("p", { + className: "today-forecast", + "data-l10n-id": "newtab-weather-todays-forecast" + }), /*#__PURE__*/React.createElement("ul", { + className: "forecast-row-items" + }, /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("span", null, "80\xB0"), /*#__PURE__*/React.createElement("span", { + className: `weather-icon iconId${WEATHER_SUGGESTION.current_conditions.icon_id}` + }), /*#__PURE__*/React.createElement("span", null, "7:00")), /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("span", null, "80\xB0"), /*#__PURE__*/React.createElement("span", { + className: `weather-icon iconId${WEATHER_SUGGESTION.current_conditions.icon_id}` + }), /*#__PURE__*/React.createElement("span", null, "7:00")), /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("span", null, "80\xB0"), /*#__PURE__*/React.createElement("span", { + className: `weather-icon iconId${WEATHER_SUGGESTION.current_conditions.icon_id}` + }), /*#__PURE__*/React.createElement("span", null, "7:00")), /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("span", null, "80\xB0"), /*#__PURE__*/React.createElement("span", { + className: `weather-icon iconId${WEATHER_SUGGESTION.current_conditions.icon_id}` + }), /*#__PURE__*/React.createElement("span", null, "7:00")), /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("span", null, "80\xB0"), /*#__PURE__*/React.createElement("span", { + className: `weather-icon iconId${WEATHER_SUGGESTION.current_conditions.icon_id}` + }), /*#__PURE__*/React.createElement("span", null, "7:00")))), /*#__PURE__*/React.createElement("div", { + className: "weather-forecast-footer" + }, /*#__PURE__*/React.createElement("a", { + href: "#", + className: "full-forecast", + "data-l10n-id": "newtab-weather-see-full-forecast" + }), /*#__PURE__*/React.createElement("span", { + className: "sponsored-text", + "data-l10n-id": "newtab-weather-sponsored", + "data-l10n-args": "{\"provider\": \"AccuWeather\xAE\"}" + }))); +} + ;// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/FeatureHighlight/WidgetsFeatureHighlight.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 @@ -13117,10 +13197,13 @@ function WidgetsFeatureHighlight({ + const PREF_WIDGETS_LISTS_ENABLED = "widgets.lists.enabled"; const PREF_WIDGETS_SYSTEM_LISTS_ENABLED = "widgets.system.lists.enabled"; const PREF_WIDGETS_TIMER_ENABLED = "widgets.focusTimer.enabled"; const PREF_WIDGETS_SYSTEM_TIMER_ENABLED = "widgets.system.focusTimer.enabled"; +const PREF_WIDGETS_WEATHER_FORECAST_ENABLED = "widgets.weatherForecast.enabled"; +const PREF_WIDGETS_SYSTEM_WEATHER_FORECAST_ENABLED = "widgets.system.weatherForecast.enabled"; const PREF_WIDGETS_MAXIMIZED = "widgets.maximized"; const PREF_WIDGETS_SYSTEM_MAXIMIZED = "widgets.system.maximized"; @@ -13162,10 +13245,13 @@ function Widgets() { const dispatch = (0,external_ReactRedux_namespaceObject.useDispatch)(); const nimbusListsEnabled = prefs.widgetsConfig?.listsEnabled; const nimbusTimerEnabled = prefs.widgetsConfig?.timerEnabled; + const nimbusWeatherForecastEnabled = prefs.widgetsConfig?.weatherForecastEnabled; const nimbusListsTrainhopEnabled = prefs.trainhopConfig?.widgets?.listsEnabled; const nimbusTimerTrainhopEnabled = prefs.trainhopConfig?.widgets?.timerEnabled; + const nimbusWeatherForecastTrainhopEnabled = prefs.trainhopConfig?.widgets?.weatherForecastEnabled; const listsEnabled = (nimbusListsTrainhopEnabled || nimbusListsEnabled || prefs[PREF_WIDGETS_SYSTEM_LISTS_ENABLED]) && prefs[PREF_WIDGETS_LISTS_ENABLED]; const timerEnabled = (nimbusTimerTrainhopEnabled || nimbusTimerEnabled || prefs[PREF_WIDGETS_SYSTEM_TIMER_ENABLED]) && prefs[PREF_WIDGETS_TIMER_ENABLED]; + const weatherForecastEnabled = (nimbusWeatherForecastTrainhopEnabled || nimbusWeatherForecastEnabled || prefs[PREF_WIDGETS_SYSTEM_WEATHER_FORECAST_ENABLED]) && prefs[PREF_WIDGETS_WEATHER_FORECAST_ENABLED]; // track previous timerEnabled state to detect when it becomes disabled const prevTimerEnabledRef = (0,external_React_namespaceObject.useRef)(timerEnabled); @@ -13221,7 +13307,7 @@ function Widgets() { dispatch(actionCreators.SetPref(prefName, true)); } } - if (!(listsEnabled || timerEnabled)) { + if (!(listsEnabled || timerEnabled || weatherForecastEnabled)) { return null; } return /*#__PURE__*/external_React_default().createElement("div", { @@ -13260,6 +13346,10 @@ function Widgets() { dispatch: dispatch, handleUserInteraction: handleUserInteraction, isMaximized: isMaximized + }), weatherForecastEnabled && /*#__PURE__*/external_React_default().createElement(WeatherForecast, { + dispatch: dispatch, + handleUserInteraction: handleUserInteraction, + isMaximized: isMaximized }))), messageData?.content?.messageType === "WidgetMessage" && /*#__PURE__*/external_React_default().createElement(MessageWrapper, { dispatch: dispatch }, /*#__PURE__*/external_React_default().createElement(WidgetsFeatureHighlight, { @@ -15812,6 +15902,7 @@ class BaseContent extends (external_React_default()).PureComponent { visible: false, showSectionsMgmtPanel: false }; + this.spocPlaceholderStartTime = null; } setFirstVisibleTimestamp() { if (!this.state.firstVisibleTimestamp) { @@ -15827,6 +15918,9 @@ class BaseContent extends (external_React_default()).PureComponent { this.setFirstVisibleTimestamp(); this.shouldDisplayTopicSelectionModal(); this.onVisibilityDispatch(); + if (this.isSpocsOnDemandExpired && !this.spocPlaceholderStartTime) { + this.spocPlaceholderStartTime = Date.now(); + } } onVisibilityDispatch() { const { @@ -15988,6 +16082,31 @@ class BaseContent extends (external_React_default()).PureComponent { } } this.spocsOnDemandUpdated(); + this.trackSpocPlaceholderDuration(prevProps); + } + trackSpocPlaceholderDuration(prevProps) { + // isExpired returns true when the current props have expired spocs (showing placeholders) + const isExpired = this.isSpocsOnDemandExpired; + + // Init tracking when placeholders become visible + if (isExpired && this.state.visible && !this.spocPlaceholderStartTime) { + this.spocPlaceholderStartTime = Date.now(); + } + + // wasExpired returns true when the previous props had expired spocs (showing placeholders) + const wasExpired = prevProps.DiscoveryStream.spocs.onDemand?.enabled && !prevProps.DiscoveryStream.spocs.onDemand?.loaded && Date.now() - prevProps.DiscoveryStream.spocs.lastUpdated >= prevProps.DiscoveryStream.spocs.cacheUpdateTime; + + // Record duration telemetry event when placeholders are replaced with real content + if (wasExpired && !isExpired && this.spocPlaceholderStartTime) { + const duration = Date.now() - this.spocPlaceholderStartTime; + this.props.dispatch(actionCreators.OnlyToMain({ + type: actionTypes.DISCOVERY_STREAM_SPOC_PLACEHOLDER_DURATION, + data: { + duration + } + })); + this.spocPlaceholderStartTime = null; + } } handleColorModeChange() { const colorMode = this.prefersDarkQuery?.matches ? "dark" : "light"; diff --git a/browser/extensions/newtab/karma.mc.config.js b/browser/extensions/newtab/karma.mc.config.js index d2b41cec3f5c0..1f0d1b79a4bb6 100644 --- a/browser/extensions/newtab/karma.mc.config.js +++ b/browser/extensions/newtab/karma.mc.config.js @@ -297,6 +297,13 @@ module.exports = function (config) { functions: 31.2, branches: 31.2, }, + "content-src/components/Widgets/WeatherForecast/WeatherForecast.jsx": + { + statements: 0, + lines: 0, + functions: 0, + branches: 0, + }, "content-src/components/Weather/LocationSearch.jsx": { statements: 0, lines: 0, diff --git a/browser/extensions/newtab/lib/ActivityStream.sys.mjs b/browser/extensions/newtab/lib/ActivityStream.sys.mjs index 0e1b46cfd1dce..3705c23df60d3 100644 --- a/browser/extensions/newtab/lib/ActivityStream.sys.mjs +++ b/browser/extensions/newtab/lib/ActivityStream.sys.mjs @@ -1128,6 +1128,28 @@ export const PREFS_CONFIG = new Map([ value: false, }, ], + [ + "widgets.weatherForecast.enabled", + { + title: "Enables the weather forecast widget", + value: true, + }, + ], + [ + "widgets.system.weatherForecast.enabled", + { + title: "Enables the weather forecast widget experiment in Nimbus", + value: false, + }, + ], + [ + "widgets.weatherForecast.interaction", + { + title: + "Boolean flag for determining if a user has interacted with the weather forecast widget", + value: false, + }, + ], [ "improvesearch.noDefaultSearchTile", { diff --git a/browser/extensions/newtab/lib/FrecencyBoostProvider/FrecencyBoostProvider.mjs b/browser/extensions/newtab/lib/FrecencyBoostProvider/FrecencyBoostProvider.mjs index f14b97494b8c6..ec5530dde6dc2 100644 --- a/browser/extensions/newtab/lib/FrecencyBoostProvider/FrecencyBoostProvider.mjs +++ b/browser/extensions/newtab/lib/FrecencyBoostProvider/FrecencyBoostProvider.mjs @@ -237,4 +237,48 @@ export class FrecencyBoostProvider { link => !lazy.NewTabUtils.blockedLinks.isBlocked({ url: link.url }) ); } + + async retrieveRandomFrecencyTile() { + if (!this._frecencyBoostedSponsors.size) { + await this._importFrecencyBoostedSponsors(); + } + + const storedTile = await this.cache.get("randomFrecencyTile"); + if (storedTile) { + const tile = JSON.parse(storedTile); + if ( + this._frecencyBoostedSponsors.has(tile.hostname) && + !lazy.NewTabUtils.blockedLinks.isBlocked({ url: tile.url }) + ) { + return tile; + } + await this.cache.set("randomFrecencyTile", null); + } + + const candidates = Array.from( + this._frecencyBoostedSponsors.values() + ).filter(s => !lazy.NewTabUtils.blockedLinks.isBlocked({ url: s.domain })); + + if (!candidates.length) { + return null; + } + + const selected = candidates[Math.floor(Math.random() * candidates.length)]; + const tile = { + hostname: selected.hostname, + url: selected.redirectURL, + label: selected.title, + partner: SPONSORED_TILE_PARTNER_FREC_BOOST, + type: "frecency-boost-random", + show_sponsored_label: true, + favicon: selected.faviconDataURI, + faviconSize: 96, + }; + await this.cache.set("randomFrecencyTile", JSON.stringify(tile)); + return tile; + } + + async clearRandomFrecencyTile() { + await this.cache.set("randomFrecencyTile", null); + } } diff --git a/browser/extensions/newtab/lib/NewTabAttributionService.sys.mjs b/browser/extensions/newtab/lib/NewTabAttributionService.sys.mjs index 1f8652c1106dd..5cfc07c2b501c 100644 --- a/browser/extensions/newtab/lib/NewTabAttributionService.sys.mjs +++ b/browser/extensions/newtab/lib/NewTabAttributionService.sys.mjs @@ -8,15 +8,24 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { IndexedDB: "resource://gre/modules/IndexedDB.sys.mjs", DAPSender: "resource://gre/modules/DAPSender.sys.mjs", + ObliviousHTTP: "resource://gre/modules/ObliviousHTTP.sys.mjs", + HPKEConfigManager: "resource://gre/modules/HPKEConfigManager.sys.mjs", + AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs", }); -const MAX_CONVERSIONS = 5; +const MAX_CONVERSIONS = 2; const MAX_LOOKBACK_DAYS = 30; const DAY_IN_MILLI = 1000 * 60 * 60 * 24; const CONVERSION_RESET_MILLI = 7 * DAY_IN_MILLI; const DAP_HPKE_PREF = "dap.ohttp.hpke"; const DAP_RELAY_PREF = "dap.ohttp.relayURL"; +const MARS_ENDPOINT_PREF = + "browser.newtabpage.activity-stream.unifiedAds.endpoint"; +const PREF_MARS_OHTTP_CONFIG = + "browser.newtabpage.activity-stream.discoverystream.ohttp.configURL"; +const PREF_MARS_OHTTP_RELAY = + "browser.newtabpage.activity-stream.discoverystream.ohttp.relayURL"; /** * @@ -30,7 +39,6 @@ class NewTabAttributionService { * @typedef {object} task - DAP task settings. * @property {string} id - task id. * @property {string} vdaf - vdaf type. - * @property {number} bits - datatype size. * @property {number} length - number of buckets. * @property {number} time_precision - time precision. * @@ -79,6 +87,13 @@ class NewTabAttributionService { return this.#dateProvider.now(); } + #getTrainhopConfig() { + return ( + lazy.AboutNewTab.activityStream?.store.getState().Prefs.values + .trainhopConfig ?? {} + ); + } + /** * onAttributionEvent stores an event locally for an attributable interaction on Newtab. * @@ -90,39 +105,26 @@ class NewTabAttributionService { try { const now = this.#now(); - const impressionStore = await this.#getImpressionStore(); - - if (!params || !params.conversion) { + if ( + !params || + !params.partner_id || + params.index === undefined || + params.index === null + ) { return; } - const impression = await this.#getImpression( - impressionStore, - params.partner_id, - { - conversion: { - task: { - id: params.conversion.task_id, - vdaf: params.conversion.vdaf, - bits: params.conversion.bits, - length: params.conversion.length, - time_precision: params.conversion.time_precision, - }, - defaultMeasurement: params.conversion.default_measurement, - index: params.conversion.index, - }, - } - ); + const impression = await this.#getImpression(params.partner_id, { + conversion: { + index: params.index, + }, + }); const prop = this.#getModelProp(type); impression.lastImpression = now; impression[prop] = now; - await this.#updateImpression( - impressionStore, - params.partner_id, - impression - ); + await this.#updateImpression(params.partner_id, impression); } catch (e) { console.error(e); } @@ -175,12 +177,21 @@ class NewTabAttributionService { */ async onAttributionConversion(partnerId, lookbackDays, impressionType) { try { - if (lookbackDays > MAX_LOOKBACK_DAYS) { + const trainhopConfig = this.#getTrainhopConfig(); + const attributionConfig = trainhopConfig.attribution || {}; + + const maxLookbackDays = + attributionConfig.maxLookbackDays ?? MAX_LOOKBACK_DAYS; + const maxConversions = + attributionConfig.maxConversions ?? MAX_CONVERSIONS; + + if (lookbackDays > maxLookbackDays) { return; } + // we don't want to request the gateway key at time of conversion to avoid an IP address leak const dapHpke = Services.prefs.getCharPref( DAP_HPKE_PREF, - "https://dap-09-3.api.divviup.org/ohttp-configs" + "gAAgJSO22Y3HKzRSese15JtQVuuFfOIcTrZ56lQ5kDQwS0oABAABAAE" ); const ohttpRelayURL = Services.prefs.getCharPref( DAP_RELAY_PREF, @@ -196,21 +207,28 @@ class NewTabAttributionService { now ); - let conversion = impression?.conversion; - if (!conversion) { - // retreive "conversion" for conversions with no found impression - // conversion = await this.#getUnattributedTask(partnerId); - if (!conversion) { - return; - } + const receivedTaskConfig = await this.#getTaskConfig(partnerId); + + if (!receivedTaskConfig) { + return; } - let measurement = conversion.defaultMeasurement; + // Need to rename task_id to id for DAP report submission. + const taskConfig = { + ...receivedTaskConfig, + id: receivedTaskConfig.task_id, + }; + + let measurement = receivedTaskConfig.default_measurement; let budgetSpend = 0; - if (budget.conversions < MAX_CONVERSIONS && conversion) { + if (budget.conversions < maxConversions && impression) { budgetSpend = 1; - if (conversion.task && conversion.task.length > conversion.index) { - measurement = conversion.index; + const conversionIndex = impression.conversion.index; + if ( + receivedTaskConfig.length > conversionIndex && + conversionIndex !== undefined + ) { + measurement = conversionIndex; } } @@ -218,7 +236,7 @@ class NewTabAttributionService { const options = {}; if (dapHpke) { - options.ohttp_hpke = dapHpke; + options.ohttp_hpke = lazy.HPKEConfigManager.decodeKey(dapHpke); } if (ohttpRelayURL) { @@ -226,7 +244,7 @@ class NewTabAttributionService { } await this.#dapSender.sendDAPMeasurement( - conversion.task, + taskConfig, measurement, options ); @@ -278,12 +296,12 @@ class NewTabAttributionService { * if it is found, defaulting to the passed in impression if there are none. This * enables timestamp fields of the stored event to be updated or carried forward. * - * @param {ObjectStore} impressionStore - Promise-based wrapped IDBObjectStore. * @param {string} partnerId - partner this event is associated with. * @param {impression} defaultImpression - event to use if it has not been seen previously. * @returns {Promise} */ - async #getImpression(impressionStore, partnerId, defaultImpression) { + async #getImpression(partnerId, defaultImpression) { + const impressionStore = await this.#getImpressionStore(); const impressions = await this.#getPartnerImpressions( impressionStore, partnerId @@ -295,15 +313,60 @@ class NewTabAttributionService { return impression ?? defaultImpression; } + async #getTaskConfig(partnerId) { + const baseUrl = Services.prefs.getCharPref(MARS_ENDPOINT_PREF, ""); + const endpoint = `${baseUrl}/v1/attribution?partner_id=${encodeURIComponent( + partnerId + )}`; + const ohttpConfigURL = Services.prefs.getCharPref( + PREF_MARS_OHTTP_CONFIG, + "" + ); + const ohttpRelayURL = Services.prefs.getCharPref(PREF_MARS_OHTTP_RELAY, ""); + + if (!partnerId || !endpoint || !ohttpRelayURL || !ohttpConfigURL) { + return null; + } + const controller = new AbortController(); + const { signal } = controller; + let config = await lazy.ObliviousHTTP.getOHTTPConfig(ohttpConfigURL); + if (!config) { + console.error( + new Error( + `OHTTP was configured for ${endpoint} but we couldn't fetch a valid config` + ) + ); + return null; + } + try { + const response = await lazy.ObliviousHTTP.ohttpRequest( + ohttpRelayURL, + config, + endpoint, + { + headers: {}, + signal, + } + ); + return response.json(); + } catch (error) { + console.error( + `Failed to make OHTTP request for unattributed task: ${error.message}`, + error + ); + return null; + } + } + /** * updateImpression stores the passed event, either updating the record * if this event was already seen, or appending to the list of events if it is new. * - * @param {ObjectStore} impressionStore - Promise-based wrapped IDBObjectStore. * @param {string} partnerId - partner this event is associated with. * @param {impression} impression - event to update. */ - async #updateImpression(impressionStore, partnerId, impression) { + async #updateImpression(partnerId, impression) { + const impressionStore = await this.#getImpressionStore(); let impressions = await this.#getPartnerImpressions( impressionStore, partnerId @@ -324,13 +387,10 @@ class NewTabAttributionService { /** * @param {impression} cur * @param {impression} impression - * @returns {boolean} true if cur and impression have the same DAP allocation, else false. + * @returns {boolean} true if cur and impression have the same index */ #compareImpression(cur, impression) { - return ( - cur.conversion.task.id === impression.conversion.task.id && - cur.conversion.index === impression.conversion.index - ); + return cur.conversion.index === impression.conversion.index; } /** diff --git a/browser/extensions/newtab/lib/NewTabGleanUtils.sys.mjs b/browser/extensions/newtab/lib/NewTabGleanUtils.sys.mjs index fe4ed14432b99..2e4637afad9fc 100644 --- a/browser/extensions/newtab/lib/NewTabGleanUtils.sys.mjs +++ b/browser/extensions/newtab/lib/NewTabGleanUtils.sys.mjs @@ -16,7 +16,11 @@ ChromeUtils.defineLazyGetter(lazy, "logConsole", function () { }); }); -const EXTRA_ARGS_TYPES_ALLOWLIST = ["event", "memory_distribution"]; +const EXTRA_ARGS_TYPES_ALLOWLIST = [ + "event", + "memory_distribution", + "timing_distribution", +]; /** * Module for managing Glean telemetry metrics and pings in the New Tab page. diff --git a/browser/extensions/newtab/lib/TelemetryFeed.sys.mjs b/browser/extensions/newtab/lib/TelemetryFeed.sys.mjs index cdce397512914..8d48f486087ff 100644 --- a/browser/extensions/newtab/lib/TelemetryFeed.sys.mjs +++ b/browser/extensions/newtab/lib/TelemetryFeed.sys.mjs @@ -744,6 +744,20 @@ export class TelemetryFeed { } } + /** + * Records the duration that spoc (ads) placeholders were visible to the user. + * This tracks how long placeholder content is shown before being replaced + * with actual sponsored content when using onDemand mode. + * + * @param {number} action.data.duration - Duration in milliseconds + */ + handleSpocPlaceholderDuration(action) { + const { duration } = action.data; + if (duration !== undefined && duration >= 0) { + Glean.pocket.spocPlaceholderDuration.accumulateSingleSample(duration); + } + } + handleUserEvent(action) { let userEvent = this.createUserEvent(action); try { @@ -1301,6 +1315,9 @@ export class TelemetryFeed { action.data ); break; + case at.DISCOVERY_STREAM_SPOC_PLACEHOLDER_DURATION: + this.handleSpocPlaceholderDuration(action); + break; case at.DISCOVERY_STREAM_USER_EVENT: this.handleDiscoveryStreamUserEvent(action); break; diff --git a/browser/extensions/newtab/lib/TopSitesFeed.sys.mjs b/browser/extensions/newtab/lib/TopSitesFeed.sys.mjs index 620b95c3424fb..992e6215783b4 100644 --- a/browser/extensions/newtab/lib/TopSitesFeed.sys.mjs +++ b/browser/extensions/newtab/lib/TopSitesFeed.sys.mjs @@ -1391,13 +1391,23 @@ export class TopSitesFeed { ) { const { values } = this.store.getState().Prefs; const numItems = values?.trainhopConfig?.sov?.numItems; + const randomSponsorEnabled = values?.trainhopConfig?.sov?.random_sponsor; + + if (!randomSponsorEnabled) { + candidates = await this.frecencyBoostProvider.fetch(numItems); + // If we have a matched set of candidates, + // we can check if it's an exposure event. + if (candidates.length) { + this.frecencyBoostedSpocsExposureEvent(); + } + } - candidates = await this.frecencyBoostProvider.fetch(numItems); - - // If we have a matched set of candidates, - // we can check if it's an exposure event. - if (candidates.length) { - this.frecencyBoostedSpocsExposureEvent(); + if (!candidates.length) { + const randomTile = + await this.frecencyBoostProvider.retrieveRandomFrecencyTile(); + if (randomTile) { + candidates = [randomTile]; + } } } return candidates; diff --git a/browser/extensions/newtab/mach_commands.py b/browser/extensions/newtab/mach_commands.py index fce18f0ddf7eb..0f6c349cb5eff 100644 --- a/browser/extensions/newtab/mach_commands.py +++ b/browser/extensions/newtab/mach_commands.py @@ -104,9 +104,13 @@ def watch(command_context): processes = [] try: - p1 = subprocess.Popen( - ["./mach", "npm", "run", "watchmc", "--prefix=browser/extensions/newtab"] - ) + p1 = subprocess.Popen([ + "./mach", + "npm", + "run", + "watchmc", + "--prefix=browser/extensions/newtab", + ]) p2 = subprocess.Popen(["./mach", "watch"]) processes.extend([p1, p2]) print("Watching subprocesses started. Press Ctrl-C to terminate them.") @@ -145,9 +149,13 @@ def update_locales(command_context): # Step 1: We download the latest reckoning of strings from firefox-l10n print("Cloning the latest HEAD of firefox-l10n repository") with tempfile.TemporaryDirectory() as clone_dir: - subprocess.check_call( - ["git", "clone", "--depth=1", FIREFOX_L10N_REPO, clone_dir] - ) + subprocess.check_call([ + "git", + "clone", + "--depth=1", + FIREFOX_L10N_REPO, + clone_dir, + ]) # Step 2: Get some metadata about what we just pulled down - # specifically, the revision. revision = subprocess.check_output( @@ -831,9 +839,13 @@ def bundle(command_context): proc = None try: - proc = subprocess.Popen( - ["./mach", "npm", "run", "bundle", "--prefix=browser/extensions/newtab"] - ) + proc = subprocess.Popen([ + "./mach", + "npm", + "run", + "bundle", + "--prefix=browser/extensions/newtab", + ]) print("Bundling newtab started. Press Ctrl-C to terminate.") proc.wait() except KeyboardInterrupt: @@ -871,9 +883,12 @@ def install(command_context): proc = None try: - proc = subprocess.Popen( - ["./mach", "npm", "install", "--prefix=browser/extensions/newtab"] - ) + proc = subprocess.Popen([ + "./mach", + "npm", + "install", + "--prefix=browser/extensions/newtab", + ]) print( "Installing node dependencies for newtab started. Press Ctrl-C to terminate." ) diff --git a/browser/extensions/newtab/test/unit/content-src/components/Base.test.jsx b/browser/extensions/newtab/test/unit/content-src/components/Base.test.jsx index ecdcd039175e3..bc54746e86eb6 100644 --- a/browser/extensions/newtab/test/unit/content-src/components/Base.test.jsx +++ b/browser/extensions/newtab/test/unit/content-src/components/Base.test.jsx @@ -220,4 +220,215 @@ describe("WithDsAdmin", () => { assert.lengthOf(wrapper.find(BaseContent), 0); }); }); + + describe("SPOC Placeholder Duration Tracking", () => { + let wrapper; + let instance; + let dispatch; + let clock; + let baseProps; + + beforeEach(() => { + // Setup: Create a component with expired spocs (showing placeholders) + // - useFakeTimers allows us to control time for duration testing + // - lastUpdated is 120000ms (2 mins) ago, exceeding cacheUpdateTime of 60000ms (1 min) + // - In this setup, spocs are expired and placeholders should be visible + clock = sinon.useFakeTimers(); + dispatch = sinon.spy(); + baseProps = { + store: { getState: () => {} }, + App: { initialized: true }, + Prefs: { values: {} }, + Sections: [], + Weather: {}, + document: { + visibilityState: "visible", + addEventListener: sinon.stub(), + removeEventListener: sinon.stub(), + }, + }; + const props = { + ...baseProps, + dispatch, + DiscoveryStream: { + config: { enabled: true }, + spocs: { + onDemand: { enabled: true, loaded: false }, + lastUpdated: Date.now() - 120000, // Expired (120s ago) + cacheUpdateTime: 60000, // Cache expires after 60s + }, + }, + }; + wrapper = shallow(); + instance = wrapper.instance(); + instance.setState({ visible: true }); + }); + + afterEach(() => { + clock.restore(); + }); + + it("should start tracking when placeholders become visible", () => { + const prevProps = { + ...baseProps, + DiscoveryStream: { + config: { enabled: true }, + spocs: { + onDemand: { enabled: true, loaded: false }, + lastUpdated: Date.now() - 30000, + cacheUpdateTime: 60000, + }, + }, + }; + + clock.tick(1000); + instance.trackSpocPlaceholderDuration(prevProps); + + assert.isNotNull(instance.spocPlaceholderStartTime); + }); + + it("should record duration when placeholders are replaced", () => { + // Create a fresh wrapper with expired spocs + const freshDispatch = sinon.spy(); + const expiredTime = Date.now() - 120000; + const freshWrapper = shallow( + + ); + const freshInstance = freshWrapper.instance(); + freshInstance.setState({ visible: true }); + + // Advance clock a bit first so startTime is not 0 (which is falsy) + clock.tick(100); + + // Set start time and advance clock + const startTime = Date.now(); + freshInstance.spocPlaceholderStartTime = startTime; + clock.tick(150); + + // Update to fresh spocs - this triggers componentDidUpdate + // which automatically calls trackSpocPlaceholderDuration + freshWrapper.setProps({ + ...baseProps, + dispatch: freshDispatch, + DiscoveryStream: { + config: { enabled: true }, + spocs: { + onDemand: { enabled: true, loaded: false }, + lastUpdated: Date.now(), + cacheUpdateTime: 60000, + }, + }, + }); + + // componentDidUpdate should have dispatched the placeholder duration action + const placeholderCall = freshDispatch + .getCalls() + .find( + call => + call.args[0].type === "DISCOVERY_STREAM_SPOC_PLACEHOLDER_DURATION" + ); + + assert.isNotNull( + placeholderCall, + "Placeholder duration action should be dispatched" + ); + const [action] = placeholderCall.args; + assert.equal(action.data.duration, 150); + assert.deepEqual(action.meta, { + from: "ActivityStream:Content", + to: "ActivityStream:Main", + skipLocal: true, + }); + + assert.isNull(freshInstance.spocPlaceholderStartTime); + }); + + it("should start tracking on onVisible if placeholders already expired", () => { + wrapper.setProps({ + DiscoveryStream: { + config: { enabled: true }, + spocs: { + onDemand: { enabled: true, loaded: false }, + lastUpdated: Date.now() - 120000, + cacheUpdateTime: 60000, + }, + }, + }); + + instance.setState({ visible: false }); + instance.spocPlaceholderStartTime = null; + + instance.onVisible(); + + assert.isNotNull(instance.spocPlaceholderStartTime); + }); + + it("should not start tracking if tab is not visible", () => { + instance.setState({ visible: false }); + instance.spocPlaceholderStartTime = null; + + const prevProps = { + ...baseProps, + DiscoveryStream: { + config: { enabled: true }, + spocs: { + onDemand: { enabled: true, loaded: false }, + lastUpdated: Date.now() - 30000, + cacheUpdateTime: 60000, + }, + }, + }; + + instance.trackSpocPlaceholderDuration(prevProps); + + assert.isNull(instance.spocPlaceholderStartTime); + }); + + it("should not start tracking if onDemand is disabled", () => { + // Reset instance to have onDemand disabled from the start + const props = { + ...baseProps, + dispatch, + DiscoveryStream: { + config: { enabled: true }, + spocs: { + onDemand: { enabled: false, loaded: false }, + lastUpdated: Date.now() - 120000, + cacheUpdateTime: 60000, + }, + }, + }; + wrapper = shallow(); + instance = wrapper.instance(); + instance.setState({ visible: true }); + instance.spocPlaceholderStartTime = null; + + const prevProps = { + ...baseProps, + DiscoveryStream: { + config: { enabled: true }, + spocs: { + onDemand: { enabled: false, loaded: false }, + lastUpdated: Date.now() - 120000, + cacheUpdateTime: 60000, + }, + }, + }; + + instance.trackSpocPlaceholderDuration(prevProps); + + assert.isNull(instance.spocPlaceholderStartTime); + }); + }); }); diff --git a/browser/extensions/newtab/test/unit/content-src/components/ModalOverlay.test.jsx b/browser/extensions/newtab/test/unit/content-src/components/ModalOverlay.test.jsx index ea3461488aeb5..445a5a29412de 100644 --- a/browser/extensions/newtab/test/unit/content-src/components/ModalOverlay.test.jsx +++ b/browser/extensions/newtab/test/unit/content-src/components/ModalOverlay.test.jsx @@ -2,6 +2,18 @@ import { ModalOverlayWrapper } from "content-src/components/ModalOverlay/ModalOv import { mount } from "enzyme"; import React from "react"; +// Patch dialog element's .showModal()/close() functions to prevent errors in tests +before(() => { + if (typeof HTMLDialogElement !== "undefined") { + HTMLDialogElement.prototype.showModal = function () { + this.open = true; + }; + HTMLDialogElement.prototype.close = function () { + this.open = false; + }; + } +}); + describe("ModalOverlayWrapper", () => { let sandbox; beforeEach(() => { diff --git a/browser/extensions/newtab/test/unit/content-src/components/TopSites.test.jsx b/browser/extensions/newtab/test/unit/content-src/components/TopSites.test.jsx index 6423cf2f16083..ac8a933403059 100644 --- a/browser/extensions/newtab/test/unit/content-src/components/TopSites.test.jsx +++ b/browser/extensions/newtab/test/unit/content-src/components/TopSites.test.jsx @@ -25,6 +25,18 @@ import { TopSiteFormInput } from "content-src/components/TopSites/TopSiteFormInp import { _TopSites as TopSites } from "content-src/components/TopSites/TopSites"; import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton"; +// Patch dialog element's .showModal()/close() functions to prevent errors in tests +before(() => { + if (typeof HTMLDialogElement !== "undefined") { + HTMLDialogElement.prototype.showModal = function () { + this.open = true; + }; + HTMLDialogElement.prototype.close = function () { + this.open = false; + }; + } +}); + const perfSvc = { mark() {}, getMostRecentAbsMarkStartByName() {}, diff --git a/browser/extensions/newtab/test/unit/unit-entry.js b/browser/extensions/newtab/test/unit/unit-entry.js index 21a08db06df48..aeb142d6c9030 100644 --- a/browser/extensions/newtab/test/unit/unit-entry.js +++ b/browser/extensions/newtab/test/unit/unit-entry.js @@ -37,17 +37,6 @@ chai.use(chaiAssertions); const overrider = new GlobalOverrider(); -// Patch dialog element's .showModal()/close() functions to prevent errors in tests -// Some test environments may not have proper HTMLDialogElement support -if (typeof HTMLDialogElement !== "undefined") { - HTMLDialogElement.prototype.showModal = function () { - this.open = true; - }; - HTMLDialogElement.prototype.close = function () { - this.open = false; - }; -} - const RemoteSettings = name => ({ get: () => { if (name === "attachment") { diff --git a/browser/extensions/newtab/test/xpcshell/test_NewTabAttributionService.js b/browser/extensions/newtab/test/xpcshell/test_NewTabAttributionService.js index 4ce5a870b7308..3165ab5d11fa5 100644 --- a/browser/extensions/newtab/test/xpcshell/test_NewTabAttributionService.js +++ b/browser/extensions/newtab/test/xpcshell/test_NewTabAttributionService.js @@ -6,6 +6,9 @@ https://creativecommons.org/publicdomain/zero/1.0/ */ ChromeUtils.defineESModuleGetters(this, { NewTabAttributionServiceClass: "resource://newtab/lib/NewTabAttributionService.sys.mjs", + ObliviousHTTP: "resource://gre/modules/ObliviousHTTP.sys.mjs", + AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs", + sinon: "resource://testing-common/Sinon.sys.mjs", }); const { HttpServer } = ChromeUtils.importESModule( @@ -21,7 +24,7 @@ const BinaryInputStream = Components.Constructor( const PREF_LEADER = "toolkit.telemetry.dap.leader.url"; const PREF_HELPER = "toolkit.telemetry.dap.helper.url"; const TASK_ID = "DSZGMFh26hBYXNaKvhL_N4AHA3P5lDn19on1vFPBxJM"; -const MAX_CONVERSIONS = 5; +const MAX_CONVERSIONS = 2; const DAY_IN_MILLI = 1000 * 60 * 60 * 24; const LOOKBACK_DAYS = 1; const MAX_LOOKBACK_DAYS = 30; @@ -100,8 +103,68 @@ class MockServer { } } +let globalSandbox; + add_setup(async function () { do_get_profile(); + Services.prefs.setStringPref( + "browser.newtabpage.activity-stream.unifiedAds.endpoint", + "https://test.example.com" + ); + Services.prefs.setStringPref( + "browser.newtabpage.activity-stream.discoverystream.ohttp.configURL", + "https://test.example.com/config" + ); + Services.prefs.setStringPref( + "browser.newtabpage.activity-stream.discoverystream.ohttp.relayURL", + "https://test.example.com/relay" + ); + + globalSandbox = sinon.createSandbox(); + globalSandbox.stub(ObliviousHTTP, "getOHTTPConfig").resolves({}); + globalSandbox.stub(ObliviousHTTP, "ohttpRequest").resolves({ + status: 200, + json: () => { + return Promise.resolve({ + task_id: TASK_ID, + vdaf: "histogram", + bits: 1, + length: HISTOGRAM_SIZE, + time_precision: 60, + default_measurement: 0, + }); + }, + }); + + const mockStore = { + getState: () => ({ + Prefs: { + values: { + trainhopConfig: { + attribution: {}, + }, + }, + }, + }), + }; + + globalSandbox.stub(AboutNewTab, "activityStream").value({ + store: mockStore, + }); +}); + +registerCleanupFunction(() => { + Services.prefs.clearUserPref( + "browser.newtabpage.activity-stream.unifiedAds.endpoint" + ); + Services.prefs.clearUserPref( + "browser.newtabpage.activity-stream.discoverystream.ohttp.configURL" + ); + Services.prefs.clearUserPref( + "browser.newtabpage.activity-stream.discoverystream.ohttp.relayURL" + ); + + globalSandbox.restore(); }); add_task(async function testSuccessfulConversion() { @@ -111,24 +174,56 @@ add_task(async function testSuccessfulConversion() { }); const partnerIdentifier = "partner_identifier"; - const conversionSettings = { + const index = 1; + + await privateAttribution.onAttributionEvent("view", { + partner_id: partnerIdentifier, + index, + }); + + await privateAttribution.onAttributionEvent("click", { + partner_id: partnerIdentifier, + index, + }); + + await privateAttribution.onAttributionConversion( + partnerIdentifier, + LOOKBACK_DAYS, + "view" + ); + + const receivedMeasurement = mockSender.receivedMeasurements.pop(); + Assert.deepEqual(receivedMeasurement.task, { task_id: TASK_ID, + id: TASK_ID, vdaf: "histogram", bits: 1, length: HISTOGRAM_SIZE, time_precision: 60, default_measurement: 0, - index: 1, - }; + }); + Assert.equal(receivedMeasurement.measurement, index); + Assert.ok(receivedMeasurement.options.ohttp_hpke); + Assert.equal(receivedMeasurement.options.ohttp_hpke.length, 41); + Assert.equal( + receivedMeasurement.options.ohttp_relay, + Services.prefs.getStringPref("dap.ohttp.relayURL") + ); + Assert.equal(mockSender.receivedMeasurements.length, 0); +}); - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier, - conversion: conversionSettings, +add_task(async function testZeroIndex() { + const mockSender = new MockDAPSender(); + const privateAttribution = new NewTabAttributionServiceClass({ + dapSender: mockSender, }); - await privateAttribution.onAttributionEvent("click", { + const partnerIdentifier = "partner_identifier_zero"; + const index = 0; + + await privateAttribution.onAttributionEvent("view", { partner_id: partnerIdentifier, - conversion: conversionSettings, + index, }); await privateAttribution.onAttributionConversion( @@ -137,23 +232,8 @@ add_task(async function testSuccessfulConversion() { "view" ); - const expectedMeasurement = { - task: { - id: conversionSettings.task_id, - vdaf: conversionSettings.vdaf, - bits: conversionSettings.bits, - length: conversionSettings.length, - time_precision: conversionSettings.time_precision, - }, - measurement: conversionSettings.index, - options: { - ohttp_hpke: Services.prefs.getStringPref("dap.ohttp.hpke"), - ohttp_relay: Services.prefs.getStringPref("dap.ohttp.relayURL"), - }, - }; - const receivedMeasurement = mockSender.receivedMeasurements.pop(); - Assert.deepEqual(receivedMeasurement, expectedMeasurement); + Assert.equal(receivedMeasurement.measurement, index); Assert.equal(mockSender.receivedMeasurements.length, 0); }); @@ -171,6 +251,17 @@ add_task(async function testConversionWithoutImpression() { "view" ); + const receivedMeasurement = mockSender.receivedMeasurements.pop(); + Assert.deepEqual(receivedMeasurement.task, { + task_id: TASK_ID, + id: TASK_ID, + vdaf: "histogram", + bits: 1, + length: HISTOGRAM_SIZE, + time_precision: 60, + default_measurement: 0, + }); + Assert.equal(receivedMeasurement.measurement, 0); Assert.equal(mockSender.receivedMeasurements.length, 0); }); @@ -181,19 +272,11 @@ add_task(async function testConversionWithInvalidLookbackDays() { }); const partnerIdentifier = "partner_identifier"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; + const index = 1; await privateAttribution.onAttributionEvent("view", { partner_id: partnerIdentifier, - conversion: conversionSettings, + index, }); await privateAttribution.onAttributionConversion( @@ -214,15 +297,6 @@ add_task(async function testSelectionByLastView() { }); const partnerIdentifier = "partner_identifier_last_view"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; const selectedViewIndex = 1; const ignoredViewIndex = 2; const clickIndex = 3; @@ -230,10 +304,7 @@ add_task(async function testSelectionByLastView() { // View event that will be ignored, as a more recent view will exist await privateAttribution.onAttributionEvent("view", { partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: ignoredViewIndex, - }, + index: ignoredViewIndex, }); // step forward time @@ -242,10 +313,7 @@ add_task(async function testSelectionByLastView() { // View event that will be selected, as no more recent view exists await privateAttribution.onAttributionEvent("view", { partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: selectedViewIndex, - }, + index: selectedViewIndex, }); // step forward time @@ -254,10 +322,7 @@ add_task(async function testSelectionByLastView() { // Click event that will be ignored because the match type is "view" await privateAttribution.onAttributionEvent("click", { partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: clickIndex, - }, + index: clickIndex, }); // Conversion filtering for "view" finds the view event @@ -281,15 +346,6 @@ add_task(async function testSelectionByLastClick() { }); const partnerIdentifier = "partner_identifier_last_click"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; const viewIndex = 1; const ignoredClickIndex = 2; const selectedClickIndex = 3; @@ -297,10 +353,7 @@ add_task(async function testSelectionByLastClick() { // Click event that will be ignored, as a more recent click will exist await privateAttribution.onAttributionEvent("click", { partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: ignoredClickIndex, - }, + index: ignoredClickIndex, }); // step forward time @@ -309,10 +362,7 @@ add_task(async function testSelectionByLastClick() { // Click event that will be selected, as no more recent click exists await privateAttribution.onAttributionEvent("click", { partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: selectedClickIndex, - }, + index: selectedClickIndex, }); // step forward time @@ -321,10 +371,7 @@ add_task(async function testSelectionByLastClick() { // View event that will be ignored because the match type is "click" await privateAttribution.onAttributionEvent("view", { partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: viewIndex, - }, + index: viewIndex, }); // Conversion filtering for "click" finds the click event @@ -348,25 +395,13 @@ add_task(async function testSelectionByLastTouch() { }); const partnerIdentifier = "partner_identifier_last_touch"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; const viewIndex = 1; const clickIndex = 2; // Click at clickIndex await privateAttribution.onAttributionEvent("click", { partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: clickIndex, - }, + index: clickIndex, }); // step forward time so the view event occurs most recently @@ -375,10 +410,7 @@ add_task(async function testSelectionByLastTouch() { // View at viewIndex await privateAttribution.onAttributionEvent("view", { partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: viewIndex, - }, + index: viewIndex, }); // Conversion filtering for "default" finds the view event @@ -403,25 +435,13 @@ add_task(async function testSelectionByPartnerId() { const partnerIdentifier1 = "partner_identifier_1"; const partnerIdentifier2 = "partner_identifier_2"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; const partner1Index = 1; const partner2Index = 2; // view event associated with partner 1 await privateAttribution.onAttributionEvent("view", { partner_id: partnerIdentifier1, - conversion: { - ...conversionSettings, - index: partner1Index, - }, + index: partner1Index, }); // step forward time so the partner 2 event occurs most recently @@ -430,10 +450,7 @@ add_task(async function testSelectionByPartnerId() { // view event associated with partner 2 await privateAttribution.onAttributionEvent("view", { partner_id: partnerIdentifier2, - conversion: { - ...conversionSettings, - index: partner2Index, - }, + index: partner2Index, }); // Conversion filtering for "default" finds the correct view event @@ -457,32 +474,26 @@ add_task(async function testExpiredImpressions() { }); const partnerIdentifier = "partner_identifier"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; + const index = 1; + const defaultMeasurement = 0; // Register impression await privateAttribution.onAttributionEvent("view", { partner_id: partnerIdentifier, - conversion: conversionSettings, + index, }); // Fast-forward time by LOOKBACK_DAYS days + 1 ms mockDateProvider.add(LOOKBACK_DAYS * DAY_IN_MILLI + 1); - // Conversion doesn't match expired impression await privateAttribution.onAttributionConversion( partnerIdentifier, LOOKBACK_DAYS, "view" ); + const receivedMeasurement = mockSender.receivedMeasurements.pop(); + Assert.deepEqual(receivedMeasurement.measurement, defaultMeasurement); Assert.equal(mockSender.receivedMeasurements.length, 0); }); @@ -493,19 +504,12 @@ add_task(async function testConversionBudget() { }); const partnerIdentifier = "partner_identifier_budget"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; + const index = 1; + const defaultMeasurement = 0; await privateAttribution.onAttributionEvent("view", { partner_id: partnerIdentifier, - conversion: conversionSettings, + index, }); // Measurements uploaded for conversions up to MAX_CONVERSIONS @@ -517,7 +521,7 @@ add_task(async function testConversionBudget() { ); const receivedMeasurement = mockSender.receivedMeasurements.pop(); - Assert.deepEqual(receivedMeasurement.measurement, conversionSettings.index); + Assert.deepEqual(receivedMeasurement.measurement, index); Assert.equal(mockSender.receivedMeasurements.length, 0); } @@ -529,10 +533,7 @@ add_task(async function testConversionBudget() { ); const receivedMeasurement = mockSender.receivedMeasurements.pop(); - Assert.deepEqual( - receivedMeasurement.measurement, - conversionSettings.default_measurement - ); + Assert.deepEqual(receivedMeasurement.measurement, defaultMeasurement); Assert.equal(mockSender.receivedMeasurements.length, 0); }); @@ -543,20 +544,13 @@ add_task(async function testHistogramSize() { }); const partnerIdentifier = "partner_identifier_bad_settings"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - // Zero-based index equal to histogram size is out of bounds - index: HISTOGRAM_SIZE, - }; + const defaultMeasurement = 0; + // Zero-based index equal to histogram size is out of bounds + const index = HISTOGRAM_SIZE; await privateAttribution.onAttributionEvent("view", { partner_id: partnerIdentifier, - conversion: conversionSettings, + index, }); await privateAttribution.onAttributionConversion( @@ -566,10 +560,7 @@ add_task(async function testHistogramSize() { ); const receivedMeasurement = mockSender.receivedMeasurements.pop(); - Assert.deepEqual( - receivedMeasurement.measurement, - conversionSettings.default_measurement - ); + Assert.deepEqual(receivedMeasurement.measurement, defaultMeasurement); Assert.equal(mockSender.receivedMeasurements.length, 0); }); @@ -584,19 +575,11 @@ add_task(async function testWithRealDAPSender() { const privateAttribution = new NewTabAttributionServiceClass(); const partnerIdentifier = "partner_identifier_real_dap"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; + const index = 1; await privateAttribution.onAttributionEvent("view", { partner_id: partnerIdentifier, - conversion: conversionSettings, + index, }); await privateAttribution.onAttributionConversion( diff --git a/browser/extensions/newtab/test/xpcshell/test_TelemetryFeed.js b/browser/extensions/newtab/test/xpcshell/test_TelemetryFeed.js index 2763f5367f021..a94497831261a 100644 --- a/browser/extensions/newtab/test/xpcshell/test_TelemetryFeed.js +++ b/browser/extensions/newtab/test/xpcshell/test_TelemetryFeed.js @@ -2668,3 +2668,76 @@ add_task(function test_randomizeOrganicContentEvent() { sandbox.restore(); }); + +add_task(async function test_handleSpocPlaceholderDuration_records_metric() { + info( + "TelemetryFeed.handleSpocPlaceholderDuration should record the " + + "spoc_placeholder_duration metric" + ); + + let instance = new TelemetryFeed(); + Services.fog.testResetFOG(); + + const DURATION_MS = 150; + let action = { + type: actionTypes.DISCOVERY_STREAM_SPOC_PLACEHOLDER_DURATION, + data: { duration: DURATION_MS }, + }; + + instance.handleSpocPlaceholderDuration(action); + + let recordedDuration = Glean.pocket.spocPlaceholderDuration.testGetValue(); + Assert.ok(recordedDuration, "Metric should be recorded"); + Assert.equal(recordedDuration.count, 1, "Should have 1 sample"); + Assert.greaterOrEqual( + recordedDuration.sum, + DURATION_MS, + "Duration should be at least the input value" + ); +}); + +add_task(async function test_handleSpocPlaceholderDuration_ignores_negative() { + info( + "TelemetryFeed.handleSpocPlaceholderDuration should ignore negative durations" + ); + + let instance = new TelemetryFeed(); + Services.fog.testResetFOG(); + + let action = { + type: actionTypes.DISCOVERY_STREAM_SPOC_PLACEHOLDER_DURATION, + data: { duration: -1 }, + }; + + instance.handleSpocPlaceholderDuration(action); + + let recordedDuration = Glean.pocket.spocPlaceholderDuration.testGetValue(); + Assert.equal( + recordedDuration, + null, + "Metric should not be recorded for negative duration" + ); +}); + +add_task(async function test_handleSpocPlaceholderDuration_ignores_undefined() { + info( + "TelemetryFeed.handleSpocPlaceholderDuration should ignore undefined durations" + ); + + let instance = new TelemetryFeed(); + Services.fog.testResetFOG(); + + let action = { + type: actionTypes.DISCOVERY_STREAM_SPOC_PLACEHOLDER_DURATION, + data: {}, + }; + + instance.handleSpocPlaceholderDuration(action); + + let recordedDuration = Glean.pocket.spocPlaceholderDuration.testGetValue(); + Assert.equal( + recordedDuration, + null, + "Metric should not be recorded for undefined duration" + ); +}); diff --git a/browser/extensions/newtab/test/xpcshell/test_TopSitesFeed_frecencyDeduping.js b/browser/extensions/newtab/test/xpcshell/test_TopSitesFeed_frecencyDeduping.js index 52d1007c95218..58828a66112dc 100644 --- a/browser/extensions/newtab/test/xpcshell/test_TopSitesFeed_frecencyDeduping.js +++ b/browser/extensions/newtab/test/xpcshell/test_TopSitesFeed_frecencyDeduping.js @@ -85,6 +85,11 @@ async function getTopSitesFeedForTest( .stub(feed.frecencyBoostProvider, "_frecencyBoostedSponsors") .value(frecencyBoostedSponsors); + // Stub random fallback to return null for testing deduping logic. + sandbox + .stub(feed.frecencyBoostProvider, "retrieveRandomFrecencyTile") + .returns(null); + // We need to refresh, because TopSitesFeed's // DEFAULT_TOP_SITES acts like a singleton. DEFAULT_TOP_SITES.length = 0; diff --git a/browser/extensions/newtab/test/xpcshell/test_TopSitesFeed_frecencyRanking.js b/browser/extensions/newtab/test/xpcshell/test_TopSitesFeed_frecencyRanking.js index 6eb5dbb3d3a4e..2ff187dd6d651 100644 --- a/browser/extensions/newtab/test/xpcshell/test_TopSitesFeed_frecencyRanking.js +++ b/browser/extensions/newtab/test/xpcshell/test_TopSitesFeed_frecencyRanking.js @@ -109,12 +109,13 @@ add_task(async function test_frecency_sponsored_topsites() { { info( "TopSitesFeed.fetchFrecencyBoostedSpocs - " + - "Should return an empty array with no history" + "Should return random fallback tile with no history" ); const feed = await getTopSitesFeedForTest(sandbox); const frecencyBoostedSpocs = await feed.fetchFrecencyBoostedSpocs(); - Assert.equal(frecencyBoostedSpocs.length, 0); + Assert.equal(frecencyBoostedSpocs.length, 1); + Assert.equal(frecencyBoostedSpocs[0].type, "frecency-boost-random"); sandbox.restore(); } @@ -190,6 +191,7 @@ add_task(async function test_frecency_sponsored_topsites() { const frecencyBoostedSpocs = await feed.fetchFrecencyBoostedSpocs(); Assert.equal(frecencyBoostedSpocs.length, 1); Assert.equal(frecencyBoostedSpocs[0].hostname, "domain1"); + Assert.equal(frecencyBoostedSpocs[0].type, "frecency-boost"); sandbox.restore(); } @@ -210,13 +212,14 @@ add_task(async function test_frecency_sponsored_topsites() { const frecencyBoostedSpocs = await feed.fetchFrecencyBoostedSpocs(); Assert.equal(frecencyBoostedSpocs.length, 1); Assert.equal(frecencyBoostedSpocs[0].hostname, "domain1"); + Assert.equal(frecencyBoostedSpocs[0].type, "frecency-boost"); sandbox.restore(); } { info( "TopSitesFeed.fetchFrecencyBoostedSpocs - " + - "Should not return a match with a different subdomain" + "Should not return a match with a different subdomain (returns random fallback)" ); const feed = await getTopSitesFeedForTest(sandbox, { frecent: [ @@ -228,7 +231,8 @@ add_task(async function test_frecency_sponsored_topsites() { }); const frecencyBoostedSpocs = await feed.fetchFrecencyBoostedSpocs(); - Assert.equal(frecencyBoostedSpocs.length, 0); + Assert.equal(frecencyBoostedSpocs.length, 1); + Assert.equal(frecencyBoostedSpocs[0].type, "frecency-boost-random"); sandbox.restore(); } @@ -249,13 +253,14 @@ add_task(async function test_frecency_sponsored_topsites() { const frecencyBoostedSpocs = await feed.fetchFrecencyBoostedSpocs(); Assert.equal(frecencyBoostedSpocs.length, 1); Assert.equal(frecencyBoostedSpocs[0].hostname, "sub.domain1"); + Assert.equal(frecencyBoostedSpocs[0].type, "frecency-boost"); sandbox.restore(); } { info( "TopSitesFeed.fetchFrecencyBoostedSpocs - " + - "Should not match a partial domain" + "Should not match a partial domain (returns random fallback)" ); const feed = await getTopSitesFeedForTest(sandbox, { frecent: [ @@ -267,7 +272,34 @@ add_task(async function test_frecency_sponsored_topsites() { }); const frecencyBoostedSpocs = await feed.fetchFrecencyBoostedSpocs(); - Assert.equal(frecencyBoostedSpocs.length, 0); + Assert.equal(frecencyBoostedSpocs.length, 1); + Assert.equal(frecencyBoostedSpocs[0].type, "frecency-boost-random"); + + sandbox.restore(); + } + { + info( + "TopSitesFeed.fetchFrecencyBoostedSpocs - " + + "Control group always returns random tile (ignores frecency matches)" + ); + const feed = await getTopSitesFeedForTest(sandbox, { + frecent: [ + { + url: "https://domain1.com", + frecency: 1234, + }, + ], + }); + + feed.store.state.Prefs.values.trainhopConfig = { + sov: { + random_sponsor: true, + }, + }; + + const frecencyBoostedSpocs = await feed.fetchFrecencyBoostedSpocs(); + Assert.equal(frecencyBoostedSpocs.length, 1); + Assert.equal(frecencyBoostedSpocs[0].type, "frecency-boost-random"); sandbox.restore(); } diff --git a/browser/extensions/webcompat/data/interventions.json b/browser/extensions/webcompat/data/interventions.json index 451cfcb15768c..125f968267d94 100644 --- a/browser/extensions/webcompat/data/interventions.json +++ b/browser/extensions/webcompat/data/interventions.json @@ -2146,21 +2146,6 @@ } ] }, - "1898928": { - "label": "cleanfeed.net", - "bugs": { - "1898928": { - "issue": "firefox-blocked-completely", - "matches": ["*://cleanfeed.net/*"] - } - }, - "interventions": [ - { - "platforms": ["all"], - "ua_string": ["add_Chrome"] - } - ] - }, "1898929": { "label": "app.sessionlinkpro.com", "bugs": { @@ -2446,21 +2431,6 @@ } ] }, - "1898994": { - "label": "eportal.uestc.edu.cn", - "bugs": { - "1898994": { - "issue": "firefox-blocked-completely", - "matches": ["*://eportal.uestc.edu.cn/*"] - } - }, - "interventions": [ - { - "platforms": ["all"], - "ua_string": ["add_Chrome"] - } - ] - }, "1898996": { "label": "oculus.com and meta.com", "bugs": { @@ -2719,21 +2689,6 @@ } ] }, - "1902378": { - "label": "acesso.processo.rio", - "bugs": { - "1902378": { - "issue": "unsupported-warning", - "matches": ["*://acesso.processo.rio/*"] - } - }, - "interventions": [ - { - "platforms": ["all"], - "ua_string": ["add_Chrome"] - } - ] - }, "1902379": { "label": "adl.edu.tw", "bugs": { @@ -3558,7 +3513,7 @@ "bugs": { "1913720": { "issue": "firefox-blocked-completely", - "matches": ["*://cotota.app/*", "https://is-sets.com/*"] + "matches": ["https://h2breeze.com/*", "https://is-sets.com/*"] } }, "interventions": [ @@ -5503,25 +5458,6 @@ } ] }, - "1974686": { - "label": "hermes.admin.ch", - "bugs": { - "1974686": { - "issue": "broken-layout", - "matches": [ - "*://www.hermes.admin.ch/de/projektmanagement/szenarien/it-entwicklung/szenariouebersicht.html*" - ] - } - }, - "interventions": [ - { - "platforms": ["all"], - "content_scripts": { - "css": ["bug1974686-hermes_admin_ch.css"] - } - } - ] - }, "1980129": { "label": "app.simplenote.com", "bugs": { @@ -5699,24 +5635,6 @@ } ] }, - "1987351": { - "label": "mirage.decart.ai", - "bugs": { - "1987351": { - "issue": "firefox-blocked-completely", - "matches": ["*://mirage.decart.ai/*"] - } - }, - "interventions": [ - { - "platforms": ["all"], - "content_scripts": { - "js": ["bug1928941-oasis.decart.ai-window-chrome-shim.js"] - }, - "ua_string": ["Chrome", "add_Firefox_as_Gecko"] - } - ] - }, "1989241": { "label": "useinsider.com", "bugs": { @@ -5777,23 +5695,6 @@ } ] }, - "1992923": { - "label": "godotfest.com", - "bugs": { - "1992923": { - "issue": "broken-images", - "matches": ["*://godotfest.com/*"] - } - }, - "interventions": [ - { - "platforms": ["android"], - "content_scripts": { - "css": ["1992923-godotfest.com-fix-blur-effects.css"] - } - } - ] - }, "1993201": { "label": "portal.pilot.ly", "bugs": { diff --git a/browser/extensions/webcompat/injections/css/1992923-godotfest.com-fix-blur-effects.css b/browser/extensions/webcompat/injections/css/1992923-godotfest.com-fix-blur-effects.css deleted file mode 100644 index 785b81b805693..0000000000000 --- a/browser/extensions/webcompat/injections/css/1992923-godotfest.com-fix-blur-effects.css +++ /dev/null @@ -1,30 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * godotfest.com - Blur effects are disabled on Android - * Bug #1992923 - https://bugzilla.mozilla.org/show_bug.cgi?id=1992923 - * WebCompat issue #41822 - https://webcompat.com/issues/41822 - * - * The site appears to be intentionally disabling blur effects on Firefox for - * an unknown reason, with broken-looking fallbacks. Let's enable blur again. - */ -@supports (-moz-appearance: none) { - @media (hover: none) and (pointer: coarse) { - #background-blur, - #menu-blur { - display: revert !important; - } - .backdrop-blur, - .backdrop-blur-sm, - .backdrop-blur-2xl { - --tw-backdrop-blur: blur(var(--blur-2xl)); - backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) - var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,) !important; - } - } - .talk-card__bg { - filter: var(--talk-bg-filter) !important; - } -} diff --git a/browser/extensions/webcompat/injections/css/bug1836872-docs.google.com-font-submenus-inaccessible.css b/browser/extensions/webcompat/injections/css/bug1836872-docs.google.com-font-submenus-inaccessible.css index fa332c3eafa04..5f7f1187fedc2 100644 --- a/browser/extensions/webcompat/injections/css/bug1836872-docs.google.com-font-submenus-inaccessible.css +++ b/browser/extensions/webcompat/injections/css/bug1836872-docs.google.com-font-submenus-inaccessible.css @@ -16,3 +16,9 @@ .goog-menu.apps-menu-hide-mnemonics { margin-inline-start: -20px; } + +/* sort options on recent docs page (bz2009417) */ +.docsshared-menu-bubble [class="goog-menu goog-menu-vertical"], +.goog-menu.apps-menu-hide-mnemonics { + margin-inline-start: auto; +} diff --git a/browser/extensions/webcompat/injections/css/bug1974686-hermes_admin_ch.css b/browser/extensions/webcompat/injections/css/bug1974686-hermes_admin_ch.css deleted file mode 100644 index 4f9c089c8f0ee..0000000000000 --- a/browser/extensions/webcompat/injections/css/bug1974686-hermes_admin_ch.css +++ /dev/null @@ -1,17 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * hermes.admin.ch - table cells overlap - * Bug #1974686 - https://bugzilla.mozilla.org/show_bug.cgi?id=1974686 - * - * The page sets a 1px height on its table cells, which has interop issues. - * We can unset that and adjust their other CSS to improve the situation. - */ -.hermespjm-projektuebersicht-row { - height: unset; -} -.hermespjm-projektuebersicht-cell-verantwortlicher { - position: static; -} diff --git a/browser/extensions/webcompat/injections/js/bug1928941-oasis.decart.ai-window-chrome-shim.js b/browser/extensions/webcompat/injections/js/bug1928941-oasis.decart.ai-window-chrome-shim.js index 6cacae3d286fd..61f8b4d34eae7 100644 --- a/browser/extensions/webcompat/injections/js/bug1928941-oasis.decart.ai-window-chrome-shim.js +++ b/browser/extensions/webcompat/injections/js/bug1928941-oasis.decart.ai-window-chrome-shim.js @@ -6,16 +6,14 @@ /** * Bug 1928941 - UA spoof for oasis.decart.ai - * Bug 1987351 - UA spoof for mirage.decart.ai * - * These sites are checking for window.chrome, so let's spoof that. + * This sites is checking for window.chrome, so let's spoof that. */ /* globals exportFunction */ -const bug = location.origin.includes("mirage.decart.ai") ? 1987351 : 1928941; console.info( - `window.chrome has been shimmed for compatibility reasons. https://bugzilla.mozilla.org/show_bug.cgi?id=${bug} for details.` + "window.chrome has been shimmed for compatibility reasons. https://bugzilla.mozilla.org/show_bug.cgi?id=1928941 for details." ); window.wrappedJSObject.chrome = new window.wrappedJSObject.Object(); diff --git a/browser/extensions/webcompat/lib/interventions.js b/browser/extensions/webcompat/lib/interventions.js index de0dba54483aa..46f7641cfc185 100644 --- a/browser/extensions/webcompat/lib/interventions.js +++ b/browser/extensions/webcompat/lib/interventions.js @@ -564,13 +564,10 @@ class Interventions { const contentScripts = this._contentScriptsPerIntervention.get(intervention); if (contentScripts) { - const ids = ( - await browser.scripting.getRegisteredContentScripts({ - ids: contentScripts.map(script => script.id), - }) - )?.map(script => script.id); - if (ids?.length) { - await browser.scripting.unregisterContentScripts({ ids }); + for (const id of contentScripts.map(s => s.id)) { + try { + await browser.scripting.unregisterContentScripts({ ids: [id] }); + } catch (_) {} } } } diff --git a/browser/extensions/webcompat/lib/shims.js b/browser/extensions/webcompat/lib/shims.js index 5fce64947f721..144841b618d48 100644 --- a/browser/extensions/webcompat/lib/shims.js +++ b/browser/extensions/webcompat/lib/shims.js @@ -353,13 +353,10 @@ class Shim { async _unregisterContentScripts() { if (this.shouldUseScriptingAPI) { - const ids = ( - await browser.scripting.getRegisteredContentScripts({ - ids: this._contentScriptRegistrations, - }) - )?.map(script => script.id); - if (ids?.length) { - await browser.scripting.unregisterContentScripts({ ids }); + for (const id of this._contentScriptRegistrations) { + try { + await browser.scripting.unregisterContentScripts({ ids: [id] }); + } catch (_) {} } } else { for (const registration of this._contentScriptRegistrations) { diff --git a/browser/extensions/webcompat/lib/ua_helpers.js b/browser/extensions/webcompat/lib/ua_helpers.js index c4c0c38816afc..063958bd9dd4b 100644 --- a/browser/extensions/webcompat/lib/ua_helpers.js +++ b/browser/extensions/webcompat/lib/ua_helpers.js @@ -47,7 +47,7 @@ var UAHelpers = { `Mozilla/5.0 (Linux; ${AndroidVersion}; ${tablet}) ${fxQuantum}AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Safari/537.36`; } } else { - const WIN_SEGMENT = "Windows NT 10.0; Win64; x64"; + const WIN_SEGMENT = "Windows NT 11.0; Win64; x64"; let osSegment; if (OS === "macOS" || (noOSGiven && userAgent.includes("Macintosh"))) { osSegment = "Macintosh; Intel Mac OS X 10_15_7"; @@ -83,7 +83,7 @@ var UAHelpers = { return `${ua} AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} ${isMobile ? "Mobile " : ""}Safari/537.36`; }, safari(config = {}) { - const version = config.version || "18.1"; + const version = config.version || "26.1"; const webkitVersion = config.webkitVersion || "605.1.15"; const osVersion = config.osVersion?.replace(".", "_") || "10_15_7"; const arch = config.arch || "Intel"; diff --git a/browser/extensions/webcompat/manifest.json b/browser/extensions/webcompat/manifest.json index ec2f3f2f74940..8272444d242f8 100644 --- a/browser/extensions/webcompat/manifest.json +++ b/browser/extensions/webcompat/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "Web Compatibility Interventions", "description": "Urgent post-release fixes for web compatibility.", - "version": "148.4.0", + "version": "148.6.0", "browser_specific_settings": { "gecko": { "id": "webcompat@mozilla.org", diff --git a/browser/extensions/webcompat/tests/browser/browser_ua_helpers.js b/browser/extensions/webcompat/tests/browser/browser_ua_helpers.js index 027e429e52f47..9a5b576190748 100644 --- a/browser/extensions/webcompat/tests/browser/browser_ua_helpers.js +++ b/browser/extensions/webcompat/tests/browser/browser_ua_helpers.js @@ -18,7 +18,7 @@ function shimUA() { const WEBKIT = "AppleWebKit/537.36 (KHTML, like Gecko)"; const SAFARI = " Safari/537.36"; -const PREFIX_WIN = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"; +const PREFIX_WIN = "Mozilla/5.0 (Windows NT 11.0; Win64; x64)"; const PREFIX_LIN = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64)"; const PREFIX_MAC = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"; const PREFIX_AND = "Mozilla/5.0 (Linux; Android 6.0; Nexus 7 Build/JSS15Q)"; @@ -391,7 +391,7 @@ const TESTS = { UA = "Firefox/1.0"; is( helper(), - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1 Safari/605.1.15" + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.1 Safari/605.1.15" ); is( helper({ osVersion: "1.2", version: "VER", webkitVersion: "WKVER" }), diff --git a/browser/locales-preview/aiFeatures.ftl b/browser/locales-preview/aiFeatures.ftl index 653b5b8367893..739437d3dd084 100644 --- a/browser/locales-preview/aiFeatures.ftl +++ b/browser/locales-preview/aiFeatures.ftl @@ -2,6 +2,17 @@ # 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/. +debug-model-management-group = + .label = DEBUG model management +debug-model-management-feature = + .label = features +debug-model-management-install = + .label = install feature +debug-model-management-uninstall = + .label = uninstall feature +debug-model-management-uninstall-all = + .label = uninstall all features + pane-ai-features-title = AI Features category-ai-features = .tooltiptext = { pane-ai-features-title } @@ -19,3 +30,13 @@ try-ai-features-chatbot-provider = # This labels the unset option for AI Chatbot selection, the other options are brand names like "ChatGPT" and "Anthropic Claude" try-ai-features-chatbot-choose-label = .label = Choose provider + +try-ai-features-ai-window = + .label = AI Window + .description = A separate window that learns as you browse. Get quick answers about your tabs and a more personalized experience. +try-ai-features-ai-window-activate-link = + .label = Activate AI Window + +ai-window-features-group = + .label = AI Window + .description = Choose which model powers the assistant and control what AI Window learns from your activity. diff --git a/browser/locales-preview/aiWindow.ftl b/browser/locales-preview/aiWindow.ftl index a0328243469cf..46261f924049e 100644 --- a/browser/locales-preview/aiWindow.ftl +++ b/browser/locales-preview/aiWindow.ftl @@ -67,3 +67,15 @@ aiwindow-firstrun-model-allpurpose-body = Best for a variety of quick and comple aiwindow-firstrun-model-personal-label = Personalization aiwindow-firstrun-model-personal-body = Best for learning with you aiwindow-firstrun-button = Let’s go + +## Assistant Message footer + +aiwindow-memories-used = Memories used +aiwindow-retry-without-memories = + .label = Retry without memories +aiwindow-retry = + .tooltiptext = Retry + .aria-label = Retry +aiwindow-copy-message = + .tooltiptext = Copy + .aria-label = Copy message diff --git a/browser/locales-preview/ipProtection.ftl b/browser/locales-preview/ipProtection.ftl index 923cf3fd22b9c..07668592641f7 100644 --- a/browser/locales-preview/ipProtection.ftl +++ b/browser/locales-preview/ipProtection.ftl @@ -108,9 +108,16 @@ ip-protection-learn-more = Learn more ip-protection-site-exceptions = .label = Site specific settings + +# Variables: +# $count (number) - The number of sites saved as VPN exclusions. ip-protection-site-exceptions-all-sites-button = .label = Manage website settings - .description = No websites added yet + .description = + { $count -> + [one] { $count } website + *[other] { $count } websites + } ip-protection-autostart = .label = Turn on VPN automatically diff --git a/browser/locales/en-US/browser/newtab/newtab.ftl b/browser/locales/en-US/browser/newtab/newtab.ftl index 80b7cf02c027e..a4b981b7c5668 100644 --- a/browser/locales/en-US/browser/newtab/newtab.ftl +++ b/browser/locales/en-US/browser/newtab/newtab.ftl @@ -379,6 +379,8 @@ newtab-weather-change-location-search-input-placeholder = .placeholder = Search location .aria-label = Search location newtab-weather-menu-weather-display = Weather display +newtab-weather-todays-forecast = Today’s forecast +newtab-weather-see-full-forecast = See full forecast # Display options are: # - Simple: Displays a current weather condition icon and the current temperature # - Detailed: Include simple information plus a short text summary: e.g. "Mostly cloudy" diff --git a/browser/locales/l10n-changesets.json b/browser/locales/l10n-changesets.json index eb0478d17c589..9a3a5a7c7c6ec 100644 --- a/browser/locales/l10n-changesets.json +++ b/browser/locales/l10n-changesets.json @@ -15,7 +15,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "af": { "pin": false, @@ -33,7 +33,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "an": { "pin": false, @@ -51,7 +51,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ar": { "pin": false, @@ -69,7 +69,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ast": { "pin": false, @@ -87,7 +87,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "az": { "pin": false, @@ -105,7 +105,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "be": { "pin": false, @@ -123,7 +123,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "bg": { "pin": false, @@ -141,7 +141,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "bn": { "pin": false, @@ -159,7 +159,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "bo": { "pin": false, @@ -177,7 +177,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "br": { "pin": false, @@ -195,7 +195,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "brx": { "pin": false, @@ -213,7 +213,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "bs": { "pin": false, @@ -231,7 +231,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ca": { "pin": false, @@ -249,7 +249,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ca-valencia": { "pin": false, @@ -267,7 +267,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "cak": { "pin": false, @@ -285,7 +285,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ckb": { "pin": false, @@ -303,7 +303,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "cs": { "pin": false, @@ -321,7 +321,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "cy": { "pin": false, @@ -339,7 +339,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "da": { "pin": false, @@ -357,7 +357,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "de": { "pin": false, @@ -375,7 +375,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "dsb": { "pin": false, @@ -393,7 +393,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "el": { "pin": false, @@ -411,7 +411,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "en-CA": { "pin": false, @@ -429,7 +429,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "en-GB": { "pin": false, @@ -447,7 +447,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "eo": { "pin": false, @@ -465,7 +465,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "es-AR": { "pin": false, @@ -483,7 +483,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "es-CL": { "pin": false, @@ -501,7 +501,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "es-ES": { "pin": false, @@ -519,7 +519,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "es-MX": { "pin": false, @@ -537,7 +537,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "et": { "pin": false, @@ -555,7 +555,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "eu": { "pin": false, @@ -573,7 +573,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "fa": { "pin": false, @@ -591,7 +591,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ff": { "pin": false, @@ -609,7 +609,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "fi": { "pin": false, @@ -627,7 +627,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "fr": { "pin": false, @@ -645,7 +645,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "fur": { "pin": false, @@ -663,7 +663,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "fy-NL": { "pin": false, @@ -681,7 +681,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ga-IE": { "pin": false, @@ -699,7 +699,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "gd": { "pin": false, @@ -717,7 +717,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "gl": { "pin": false, @@ -735,7 +735,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "gn": { "pin": false, @@ -753,7 +753,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "gu-IN": { "pin": false, @@ -771,7 +771,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "he": { "pin": false, @@ -789,7 +789,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "hi-IN": { "pin": false, @@ -807,7 +807,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "hr": { "pin": false, @@ -825,7 +825,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "hsb": { "pin": false, @@ -843,7 +843,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "hu": { "pin": false, @@ -861,7 +861,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "hy-AM": { "pin": false, @@ -879,7 +879,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "hye": { "pin": false, @@ -897,7 +897,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ia": { "pin": false, @@ -915,7 +915,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "id": { "pin": false, @@ -933,7 +933,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "is": { "pin": false, @@ -951,7 +951,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "it": { "pin": false, @@ -969,7 +969,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ja": { "pin": false, @@ -985,7 +985,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ja-JP-mac": { "pin": false, @@ -993,7 +993,7 @@ "macosx64", "macosx64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ka": { "pin": false, @@ -1011,7 +1011,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "kab": { "pin": false, @@ -1029,7 +1029,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "kk": { "pin": false, @@ -1047,7 +1047,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "km": { "pin": false, @@ -1065,7 +1065,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "kn": { "pin": false, @@ -1083,7 +1083,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ko": { "pin": false, @@ -1101,7 +1101,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "lij": { "pin": false, @@ -1119,7 +1119,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "lo": { "pin": false, @@ -1137,7 +1137,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "lt": { "pin": false, @@ -1155,7 +1155,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ltg": { "pin": false, @@ -1173,7 +1173,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "lv": { "pin": false, @@ -1191,7 +1191,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "meh": { "pin": false, @@ -1209,7 +1209,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "mk": { "pin": false, @@ -1227,7 +1227,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ml": { "pin": false, @@ -1245,7 +1245,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "mr": { "pin": false, @@ -1263,7 +1263,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ms": { "pin": false, @@ -1281,7 +1281,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "my": { "pin": false, @@ -1299,7 +1299,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "nb-NO": { "pin": false, @@ -1317,7 +1317,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ne-NP": { "pin": false, @@ -1335,7 +1335,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "nl": { "pin": false, @@ -1353,7 +1353,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "nn-NO": { "pin": false, @@ -1371,7 +1371,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "oc": { "pin": false, @@ -1389,7 +1389,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "pa-IN": { "pin": false, @@ -1407,7 +1407,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "pl": { "pin": false, @@ -1425,7 +1425,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "pt-BR": { "pin": false, @@ -1443,7 +1443,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "pt-PT": { "pin": false, @@ -1461,7 +1461,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "rm": { "pin": false, @@ -1479,7 +1479,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ro": { "pin": false, @@ -1497,7 +1497,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ru": { "pin": false, @@ -1515,7 +1515,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sat": { "pin": false, @@ -1533,7 +1533,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sc": { "pin": false, @@ -1551,7 +1551,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "scn": { "pin": false, @@ -1569,7 +1569,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sco": { "pin": false, @@ -1587,7 +1587,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "si": { "pin": false, @@ -1605,7 +1605,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sk": { "pin": false, @@ -1623,7 +1623,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "skr": { "pin": false, @@ -1641,7 +1641,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sl": { "pin": false, @@ -1659,7 +1659,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "son": { "pin": false, @@ -1677,7 +1677,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sq": { "pin": false, @@ -1695,7 +1695,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sr": { "pin": false, @@ -1713,7 +1713,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sv-SE": { "pin": false, @@ -1731,7 +1731,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "szl": { "pin": false, @@ -1749,7 +1749,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ta": { "pin": false, @@ -1767,7 +1767,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "te": { "pin": false, @@ -1785,7 +1785,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "tg": { "pin": false, @@ -1803,7 +1803,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "th": { "pin": false, @@ -1821,7 +1821,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "tl": { "pin": false, @@ -1839,7 +1839,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "tr": { "pin": false, @@ -1857,7 +1857,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "trs": { "pin": false, @@ -1875,7 +1875,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "uk": { "pin": false, @@ -1893,7 +1893,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ur": { "pin": false, @@ -1911,7 +1911,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "uz": { "pin": false, @@ -1929,7 +1929,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "vi": { "pin": false, @@ -1947,7 +1947,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "wo": { "pin": false, @@ -1965,7 +1965,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "xh": { "pin": false, @@ -1983,7 +1983,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "zh-CN": { "pin": false, @@ -2001,7 +2001,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "zh-TW": { "pin": false, @@ -2019,6 +2019,6 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" } } \ No newline at end of file diff --git a/browser/themes/shared/controlcenter/panel.css b/browser/themes/shared/controlcenter/panel.css index 1cc999e5db67c..32491eeb26117 100644 --- a/browser/themes/shared/controlcenter/panel.css +++ b/browser/themes/shared/controlcenter/panel.css @@ -897,7 +897,7 @@ #trustpanel-siteinfo-icon { list-style-image: url(chrome://global/skin/icons/security-broken.svg); - #trustpanel-popup[connection^="secure"] & { + #trustpanel-popup:is([connection^="secure"], [connection="extension"], [connection="chrome"], [connection="file"]) & { list-style-image: url(chrome://global/skin/icons/security.svg); } } @@ -960,6 +960,7 @@ background-repeat: no-repeat; background-size: var(--icon-size); padding-inline-start: calc(var(--icon-size) + var(--space-small)); + -moz-context-properties: fill; &:-moz-locale-dir(rtl) { background-position-x: right; diff --git a/browser/themes/shared/urlbar-searchbar.css b/browser/themes/shared/urlbar-searchbar.css index 5da1760c60e0e..78a2c85af51f9 100644 --- a/browser/themes/shared/urlbar-searchbar.css +++ b/browser/themes/shared/urlbar-searchbar.css @@ -51,8 +51,9 @@ toolbar[inactive="true"] .urlbar, .urlbar-input-container[pageproxystate="invalid"] > #page-action-buttons > .urlbar-page-action, #identity-box.chromeUI ~ #page-action-buttons > .urlbar-page-action:not(#star-button-box, #split-view-button), #urlbar[usertyping] > .urlbar-input-container > #page-action-buttons > #urlbar-zoom-button, -.urlbar:is(:not([usertyping]), :not([focused])) > .urlbar-input-container > .urlbar-go-button, -.urlbar-revert-button-container { +.urlbar:not([usertyping]) > .urlbar-input-container > .urlbar-go-button, +.urlbar:not(#searchbar-new, [focused]) > .urlbar-input-container > .urlbar-go-button, +#urlbar-revert-button-container { display: none; } @@ -848,7 +849,7 @@ toolbar[inactive="true"] .urlbar, /* Persisted Search revert button */ -.urlbar-revert-button { +#urlbar-revert-button { list-style-image: url(chrome://global/skin/icons/defaultFavicon.svg); fill: var(--toolbarbutton-icon-fill-attention); @@ -857,8 +858,8 @@ toolbar[inactive="true"] .urlbar, } } -.urlbar[persistsearchterms] > .urlbar-input-container { - > .urlbar-revert-button-container { +#urlbar[persistsearchterms] > .urlbar-input-container { + > #urlbar-revert-button-container { display: inherit; } diff --git a/build/RunCbindgen.py b/build/RunCbindgen.py index fbbe9d05842f5..bb0de610edb98 100644 --- a/build/RunCbindgen.py +++ b/build/RunCbindgen.py @@ -71,19 +71,17 @@ def generate_metadata(output, cargo_config): def generate(output, metadata_path, cbindgen_crate_path, *in_tree_dependencies): - stdout, returncode = _run_process( - [ - buildconfig.substs["CBINDGEN"], - buildconfig.topsrcdir, - "--lockfile", - CARGO_LOCK, - "--crate", - _get_crate_name(cbindgen_crate_path), - "--metadata", - metadata_path, - "--cpp-compat", - ] - ) + stdout, returncode = _run_process([ + buildconfig.substs["CBINDGEN"], + buildconfig.topsrcdir, + "--lockfile", + CARGO_LOCK, + "--crate", + _get_crate_name(cbindgen_crate_path), + "--metadata", + metadata_path, + "--cpp-compat", + ]) if returncode != 0: return returncode diff --git a/build/appini_header.py b/build/appini_header.py index 4cf9481ea099c..eb384d24592ce 100644 --- a/build/appini_header.py +++ b/build/appini_header.py @@ -3,7 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. """Parses a given application.ini file and outputs the corresponding - StaticXREAppData structure as a C++ header file""" +StaticXREAppData structure as a C++ header file""" import configparser import sys diff --git a/build/gen_symverscript.py b/build/gen_symverscript.py index d1a5abd07d4e3..22583aa571f1b 100644 --- a/build/gen_symverscript.py +++ b/build/gen_symverscript.py @@ -11,11 +11,9 @@ def main(output, input_file, version): pp = Preprocessor() - pp.context.update( - { - "VERSION": version, - } - ) + pp.context.update({ + "VERSION": version, + }) pp.out = output pp.do_include(input_file) diff --git a/build/gen_test_packages_manifest.py b/build/gen_test_packages_manifest.py index 9baea9997e016..33af31b8d5399 100644 --- a/build/gen_test_packages_manifest.py +++ b/build/gen_test_packages_manifest.py @@ -62,7 +62,7 @@ def parse_args(): required=True, action="store", dest="tests_common", - help='Name of the "common" archive, a package to be used by all ' "harnesses.", + help='Name of the "common" archive, a package to be used by all harnesses.', ) parser.add_argument( "--jsshell", diff --git a/build/gn_processor.py b/build/gn_processor.py index 4e4295870b491..87b73b486f040 100644 --- a/build/gn_processor.py +++ b/build/gn_processor.py @@ -609,13 +609,11 @@ def write_mozbuild(topsrcdir, write_mozbuild_variables, relsrcdir, configs): conditions.add(cond) for cond in sorted(conditions): - common_attrs = find_common_attrs( - [ - attrs - for args, attrs in configs - if all((args.get(k) or "") == v for k, v in cond) - ] - ) + common_attrs = find_common_attrs([ + attrs + for args, attrs in configs + if all((args.get(k) or "") == v for k, v in cond) + ]) if any(common_attrs.values()): if cond: mb.write_condition(dict(cond)) @@ -726,31 +724,25 @@ def str_for_arg(v): srcdir = build_root_dir / target_dir input_variables = input_variables.copy() - input_variables.update( - { - f"{moz_build_flag}": True, - "concurrent_links": 1, - "action_pool_depth": 1, - } - ) + input_variables.update({ + f"{moz_build_flag}": True, + "concurrent_links": 1, + "action_pool_depth": 1, + }) if input_variables["target_os"] == "win": - input_variables.update( - { - "visual_studio_path": "/", - "visual_studio_version": 2015, - "wdk_path": "/", - "windows_sdk_version": "n/a", - } - ) + input_variables.update({ + "visual_studio_path": "/", + "visual_studio_version": 2015, + "wdk_path": "/", + "windows_sdk_version": "n/a", + }) if input_variables["target_os"] == "mac": - input_variables.update( - { - "mac_sdk_path": "/", - } - ) + input_variables.update({ + "mac_sdk_path": "/", + }) - gn_args = f'--args={" ".join([f"{k}={str_for_arg(v)}" for k, v in input_variables.items()])}' + gn_args = f"--args={' '.join([f'{k}={str_for_arg(v)}' for k, v in input_variables.items()])}" with tempfile.TemporaryDirectory() as tempdir: # On Mac, `tempdir` starts with /var which is a symlink to /private/var. # We resolve the symlinks in `tempdir` here so later usage with diff --git a/build/test/python/test_android_gradle_build.py b/build/test/python/test_android_gradle_build.py index a84d328843d0d..9feecbe1b42f7 100644 --- a/build/test/python/test_android_gradle_build.py +++ b/build/test/python/test_android_gradle_build.py @@ -186,13 +186,13 @@ def assert_all_task_statuses(objdir, acceptable_statuses, always_executed_tasks= actual_status = task.get("status") if task_name in always_executed_tasks: - assert ( - actual_status == "EXECUTED" - ), f"Task {task_name} should always execute, got '{actual_status}'" + assert actual_status == "EXECUTED", ( + f"Task {task_name} should always execute, got '{actual_status}'" + ) else: - assert ( - actual_status in acceptable_statuses - ), f"Task {task_name} had status '{actual_status}', expected one of {acceptable_statuses}" + assert actual_status in acceptable_statuses, ( + f"Task {task_name} had status '{actual_status}', expected one of {acceptable_statuses}" + ) def assert_ordered_task_outcomes(objdir, ordered_expected_task_statuses): @@ -219,18 +219,18 @@ def assert_ordered_task_outcomes(objdir, ordered_expected_task_statuses): assert not missing_tasks, f"Tasks not found in build metrics: {missing_tasks}" # Check order matches expectation - assert ( - task_order == expected_order - ), f"Task execution order mismatch. Expected: {expected_order}, Got: {task_order}" + assert task_order == expected_order, ( + f"Task execution order mismatch. Expected: {expected_order}, Got: {task_order}" + ) # Check statuses for each task task_lookup = {task.get("path"): task for task in metrics_tasks} for task_name, expected_status in ordered_expected_task_statuses: task_info = task_lookup[task_name] actual_status = task_info.get("status") - assert ( - actual_status == expected_status - ), f"Task {task_name} had status '{actual_status}', expected '{expected_status}'" + assert actual_status == expected_status, ( + f"Task {task_name} had status '{actual_status}', expected '{expected_status}'" + ) def test_artifact_build(objdir, mozconfig, run_mach): diff --git a/build/variables.py b/build/variables.py index 8b510b3ed9e87..6ba3cb50f1699 100644 --- a/build/variables.py +++ b/build/variables.py @@ -97,7 +97,7 @@ def source_repo_header(output): changeset = get_hg_changeset(buildconfig.topsrcdir) if not changeset: raise Exception( - "could not resolve changeset; " "try setting MOZ_SOURCE_CHANGESET" + "could not resolve changeset; try setting MOZ_SOURCE_CHANGESET" ) if changeset: diff --git a/client.py b/client.py index e662343a76d9f..77829fab4906a 100755 --- a/client.py +++ b/client.py @@ -32,9 +32,13 @@ def do_hg_pull(dir, repository, hg): if repository is not None: cmd.append(repository) check_call_noisy(cmd) - check_call( - [hg, "parent", "-R", fulldir, "--template=Updated to revision {node}.\n"] - ) + check_call([ + hg, + "parent", + "-R", + fulldir, + "--template=Updated to revision {node}.\n", + ]) def do_hg_replace(dir, repository, tag, exclusions, hg): diff --git a/config/check_macroassembler_style.py b/config/check_macroassembler_style.py index 675db90a8360e..4515bc31139a6 100644 --- a/config/check_macroassembler_style.py +++ b/config/check_macroassembler_style.py @@ -27,12 +27,23 @@ architecture_independent = set(["generic"]) all_unsupported_architectures_names = set(["mips64", "mips_shared"]) -all_architecture_names = set( - ["x86", "x64", "arm", "arm64", "loong64", "riscv64", "wasm32"] -) -all_shared_architecture_names = set( - ["x86_shared", "arm", "arm64", "loong64", "riscv64", "wasm32"] -) +all_architecture_names = set([ + "x86", + "x64", + "arm", + "arm64", + "loong64", + "riscv64", + "wasm32", +]) +all_shared_architecture_names = set([ + "x86_shared", + "arm", + "arm64", + "loong64", + "riscv64", + "wasm32", +]) reBeforeArg = r"(?<=[(,\s])" reArgType = r"(?P[\w\s:*&<>]+)" @@ -101,14 +112,12 @@ def get_normalized_signatures(signature, fileAnnot=None): return signatures -file_suffixes = set( - [ - a.replace("_", "-") - for a in all_architecture_names.union(all_shared_architecture_names).union( - all_unsupported_architectures_names - ) - ] -) +file_suffixes = set([ + a.replace("_", "-") + for a in all_architecture_names.union(all_shared_architecture_names).union( + all_unsupported_architectures_names + ) +]) def get_file_annotation(filename): diff --git a/config/check_spidermonkey_style.py b/config/check_spidermonkey_style.py index 0b717e5c93acd..2f2e3b3d420f0 100644 --- a/config/check_spidermonkey_style.py +++ b/config/check_spidermonkey_style.py @@ -54,63 +54,61 @@ ] # We ignore #includes of these files, because they don't follow the usual rules. -included_inclnames_to_ignore = set( - [ - "ffi.h", # generated in ctypes/libffi/ - "devtools/Instruments.h", # we ignore devtools/ in general - "diplomat_runtime.hpp", # ICU4X - "double-conversion/double-conversion.h", # strange MFBT case - "frontend/ReservedWordsGenerated.h", # generated in $OBJDIR - "gc/StatsPhasesGenerated.h", # generated in $OBJDIR - "gc/StatsPhasesGenerated.inc", # generated in $OBJDIR - "icu4x/Calendar.hpp", # ICU4X - "icu4x/Date.hpp", # ICU4X - "icu4x/GraphemeClusterSegmenter.hpp", # ICU4X - "icu4x/IsoDate.hpp", # ICU4X - "icu4x/Locale.hpp", # ICU4X - "icu4x/SentenceSegmenter.hpp", # ICU4X - "icu4x/WordSegmenter.hpp", # ICU4X - "jit/ABIFunctionTypeGenerated.h", # generated in $OBJDIR" - "jit/AtomicOperationsGenerated.h", # generated in $OBJDIR - "jit/CacheIROpsGenerated.h", # generated in $OBJDIR - "jit/CacheIRAOTGenerated.h", # generated in $OBJDIR - "jit/LIROpsGenerated.h", # generated in $OBJDIR - "jit/MIROpsGenerated.h", # generated in $OBJDIR - "js/PrefsGenerated.h", # generated in $OBJDIR - "mozilla/ProfilingCategoryList.h", # comes from mozglue/baseprofiler - "mozilla/glue/Debug.h", # comes from mozglue/misc, shadowed by - "jscustomallocator.h", # provided by embedders; allowed to be missing - "js-config.h", # generated in $OBJDIR - "fdlibm.h", # fdlibm - "FuzzerDefs.h", # included without a path - "FuzzingInterface.h", # included without a path - "mozmemory.h", # included without a path - "mozmemory_stall.h", # included without a path - "pratom.h", # NSPR - "prcvar.h", # NSPR - "prerror.h", # NSPR - "prinit.h", # NSPR - "prio.h", # NSPR - "private/pprio.h", # NSPR - "prlink.h", # NSPR - "prlock.h", # NSPR - "prprf.h", # NSPR - "prthread.h", # NSPR - "prtypes.h", # NSPR - "selfhosted.out.h", # generated in $OBJDIR - "shellmoduleloader.out.h", # generated in $OBJDIR - "unicode/locid.h", # ICU - "unicode/uchar.h", # ICU - "unicode/uniset.h", # ICU - "unicode/unistr.h", # ICU - "unicode/utypes.h", # ICU - "vtune/VTuneWrapper.h", # VTune - "wasm/WasmBuiltinModuleGenerated.h", # generated in $OBJDIR" - "zydis/ZydisAPI.h", # Zydis - "xsum/xsum.h", # xsum - "fmt/format.h", # {fmt} main header - ] -) +included_inclnames_to_ignore = set([ + "ffi.h", # generated in ctypes/libffi/ + "devtools/Instruments.h", # we ignore devtools/ in general + "diplomat_runtime.hpp", # ICU4X + "double-conversion/double-conversion.h", # strange MFBT case + "frontend/ReservedWordsGenerated.h", # generated in $OBJDIR + "gc/StatsPhasesGenerated.h", # generated in $OBJDIR + "gc/StatsPhasesGenerated.inc", # generated in $OBJDIR + "icu4x/Calendar.hpp", # ICU4X + "icu4x/Date.hpp", # ICU4X + "icu4x/GraphemeClusterSegmenter.hpp", # ICU4X + "icu4x/IsoDate.hpp", # ICU4X + "icu4x/Locale.hpp", # ICU4X + "icu4x/SentenceSegmenter.hpp", # ICU4X + "icu4x/WordSegmenter.hpp", # ICU4X + "jit/ABIFunctionTypeGenerated.h", # generated in $OBJDIR" + "jit/AtomicOperationsGenerated.h", # generated in $OBJDIR + "jit/CacheIROpsGenerated.h", # generated in $OBJDIR + "jit/CacheIRAOTGenerated.h", # generated in $OBJDIR + "jit/LIROpsGenerated.h", # generated in $OBJDIR + "jit/MIROpsGenerated.h", # generated in $OBJDIR + "js/PrefsGenerated.h", # generated in $OBJDIR + "mozilla/ProfilingCategoryList.h", # comes from mozglue/baseprofiler + "mozilla/glue/Debug.h", # comes from mozglue/misc, shadowed by + "jscustomallocator.h", # provided by embedders; allowed to be missing + "js-config.h", # generated in $OBJDIR + "fdlibm.h", # fdlibm + "FuzzerDefs.h", # included without a path + "FuzzingInterface.h", # included without a path + "mozmemory.h", # included without a path + "mozmemory_stall.h", # included without a path + "pratom.h", # NSPR + "prcvar.h", # NSPR + "prerror.h", # NSPR + "prinit.h", # NSPR + "prio.h", # NSPR + "private/pprio.h", # NSPR + "prlink.h", # NSPR + "prlock.h", # NSPR + "prprf.h", # NSPR + "prthread.h", # NSPR + "prtypes.h", # NSPR + "selfhosted.out.h", # generated in $OBJDIR + "shellmoduleloader.out.h", # generated in $OBJDIR + "unicode/locid.h", # ICU + "unicode/uchar.h", # ICU + "unicode/uniset.h", # ICU + "unicode/unistr.h", # ICU + "unicode/utypes.h", # ICU + "vtune/VTuneWrapper.h", # VTune + "wasm/WasmBuiltinModuleGenerated.h", # generated in $OBJDIR" + "zydis/ZydisAPI.h", # Zydis + "xsum/xsum.h", # xsum + "fmt/format.h", # {fmt} main header +]) # JSAPI functions should be included through headers from js/public instead of # using the old, catch-all jsapi.h file. @@ -126,19 +124,17 @@ # These files have additional constraints on where they are #included, so we # ignore #includes of them when checking #include ordering. -oddly_ordered_inclnames = set( - [ - "ctypes/typedefs.h", # Included multiple times in the body of ctypes/CTypes.h - # Included in the body of frontend/TokenStream.h - "frontend/ReservedWordsGenerated.h", - "gc/StatsPhasesGenerated.h", # Included in the body of gc/Statistics.h - "gc/StatsPhasesGenerated.inc", # Included in the body of gc/Statistics.cpp - "psapi.h", # Must be included after "util/WindowsWrapper.h" on Windows - "machine/endian.h", # Must be included after on BSD - "process.h", # Windows-specific - "util/WindowsWrapper.h", # Must precede other system headers(?) - ] -) +oddly_ordered_inclnames = set([ + "ctypes/typedefs.h", # Included multiple times in the body of ctypes/CTypes.h + # Included in the body of frontend/TokenStream.h + "frontend/ReservedWordsGenerated.h", + "gc/StatsPhasesGenerated.h", # Included in the body of gc/Statistics.h + "gc/StatsPhasesGenerated.inc", # Included in the body of gc/Statistics.cpp + "psapi.h", # Must be included after "util/WindowsWrapper.h" on Windows + "machine/endian.h", # Must be included after on BSD + "process.h", # Windows-specific + "util/WindowsWrapper.h", # Must precede other system headers(?) +]) # System headers which shouldn't be included directly, but instead use the # designated wrapper. @@ -217,9 +213,7 @@ -> tests/style/HeaderCycleB1-inl.h -> tests/style/HeaderCycleB4-inl.h -""".splitlines( - True -) +""".splitlines(True) actual_output = [] @@ -381,9 +375,7 @@ def check_style(enable_fixup): def module_name(name): """Strip the trailing .cpp, .h, or -inl.h from a filename.""" - return ( - name.replace("-inl.h", "").replace(".h", "").replace(".cpp", "") - ) # NOQA: E501 + return name.replace("-inl.h", "").replace(".h", "").replace(".cpp", "") # NOQA: E501 def is_module_header(enclosing_inclname, header_inclname): diff --git a/config/external/ffi/subst_header.py b/config/external/ffi/subst_header.py index f8fa5b47534be..71fdceabd0d06 100644 --- a/config/external/ffi/subst_header.py +++ b/config/external/ffi/subst_header.py @@ -12,14 +12,12 @@ def main(output, input_file, *defines): pp = Preprocessor() - pp.context.update( - { - "FFI_EXEC_TRAMPOLINE_TABLE": "0", - "HAVE_LONG_DOUBLE": "0", - "TARGET": buildconfig.substs["FFI_TARGET"], - "VERSION": "", - } - ) + pp.context.update({ + "FFI_EXEC_TRAMPOLINE_TABLE": "0", + "HAVE_LONG_DOUBLE": "0", + "TARGET": buildconfig.substs["FFI_TARGET"], + "VERSION": "", + }) for d in defines: pp.context.update({d: "1"}) pp.do_filter("substitution") diff --git a/config/mozunit/mozunit/mozunit.py b/config/mozunit/mozunit/mozunit.py index 56676787a02bd..bbb2ca0f09b5b 100644 --- a/config/mozunit/mozunit/mozunit.py +++ b/config/mozunit/mozunit/mozunit.py @@ -312,22 +312,20 @@ def main(*args, **kwargs): args.append("--color=yes") module = __import__("__main__") - args.extend( - [ - "--rootdir", - str(topsrcdir), - "-c", - os.path.join(here, "pytest.ini"), - "-vv", - "--tb=short", - "-p", - "mozlog.pytest_mozlog.plugin", - "-p", - "mozunit.pytest_plugin", - "-p", - "no:cacheprovider", - "-rsx", # show reasons for skip / xfail - module.__file__, - ] - ) + args.extend([ + "--rootdir", + str(topsrcdir), + "-c", + os.path.join(here, "pytest.ini"), + "-vv", + "--tb=short", + "-p", + "mozlog.pytest_mozlog.plugin", + "-p", + "mozunit.pytest_plugin", + "-p", + "no:cacheprovider", + "-rsx", # show reasons for skip / xfail + module.__file__, + ]) sys.exit(pytest.main(args)) diff --git a/config/tests/unit-nsinstall.py b/config/tests/unit-nsinstall.py index 6c5c094a845c1..d24178f2ca120 100644 --- a/config/tests/unit-nsinstall.py +++ b/config/tests/unit-nsinstall.py @@ -139,7 +139,7 @@ def test_nsinstall_non_ascii(self): "Test that nsinstall handles non-ASCII files" filename = "\u2325\u3452\u2415\u5081" testfile = self.touch(filename) - testdir = self.mkdirs("\u4241\u1D04\u1414") + testdir = self.mkdirs("\u4241\u1d04\u1414") self.assertEqual( nsinstall([testfile.encode("utf-8"), testdir.encode("utf-8")]), 0 ) @@ -155,7 +155,7 @@ def test_nsinstall_non_ascii_subprocess(self): "Test that nsinstall as a subprocess handles non-ASCII files" filename = "\u2325\u3452\u2415\u5081" testfile = self.touch(filename) - testdir = self.mkdirs("\u4241\u1D04\u1414") + testdir = self.mkdirs("\u4241\u1d04\u1414") p = subprocess.Popen([sys.executable, NSINSTALL_PATH, testfile, testdir]) rv = p.wait() diff --git a/config/tests/unitMozZipFile.py b/config/tests/unitMozZipFile.py index e7fec74d750df..beba41ce9678e 100644 --- a/config/tests/unitMozZipFile.py +++ b/config/tests/unitMozZipFile.py @@ -207,6 +207,7 @@ def _write(self, zf, seed=None, leaf=0, length=0): class TestExtensiveDeflated(TestExtensiveStored): "Test all that has been tested with ZIP_STORED with DEFLATED, too." + compression = zipfile.ZIP_DEFLATED diff --git a/configure.py b/configure.py index 0cdb5c48f8748..bb36ac156c2fa 100644 --- a/configure.py +++ b/configure.py @@ -241,7 +241,7 @@ def sanitize_config(v): fh.write("%s = " % k) pprint.pprint(v, stream=fh, indent=4) fh.write( - "__all__ = ['topobjdir', 'topsrcdir', 'defines', " "'substs', 'mozconfig']" + "__all__ = ['topobjdir', 'topsrcdir', 'defines', 'substs', 'mozconfig']" ) if execute: diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp index 7ebeb1503f987..1709e410d9b24 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/docshell/base/CanonicalBrowsingContext.cpp @@ -3774,7 +3774,7 @@ void CanonicalBrowsingContext::CreateRedactedAncestorOriginsList( mPossiblyRedactedAncestorOriginsList = std::move(ancestorPrincipals); return; } - MOZ_DIAGNOSTIC_ASSERT(!parent->IsChrome()); + // 7. Let ancestorOrigins be parentLocation's internal ancestor origin objects // list. const Span> parentAncestorOriginsList = diff --git a/dom/base/CCGCScheduler.h b/dom/base/CCGCScheduler.h index 1a68eeeee33b1..141de43fa1fca 100644 --- a/dom/base/CCGCScheduler.h +++ b/dom/base/CCGCScheduler.h @@ -2,6 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef mozilla_dom_CCGCScheduler_h +#define mozilla_dom_CCGCScheduler_h + #include "js/SliceBudget.h" #include "mozilla/CycleCollectedJSContext.h" #include "mozilla/IdleTaskRunner.h" @@ -541,3 +544,5 @@ class CCGCScheduler { }; } // namespace mozilla + +#endif diff --git a/dom/base/usecounters.py b/dom/base/usecounters.py index 79fbeb6574f28..eb842c8fe4013 100644 --- a/dom/base/usecounters.py +++ b/dom/base/usecounters.py @@ -383,42 +383,38 @@ def parse_use_counters(): ) name = f"{counter['interface_name']}_{counter['attribute_name']}".lower() attr = f"{counter['interface_name']}.{counter['attribute_name']}" - page.append( - (f"{enum_root}_getter", f"{name}_getter", f"Whether a page got {attr}.") - ) - page.append( - (f"{enum_root}_setter", f"{name}_setter", f"Whether a page set {attr}.") - ) - doc.append( - ( - f"{enum_root}_getter", - f"{name}_getter", - f"Whether a document got {attr}.", - ) - ) - doc.append( - ( - f"{enum_root}_setter", - f"{name}_setter", - f"Whether a document set {attr}.", - ) - ) + page.append(( + f"{enum_root}_getter", + f"{name}_getter", + f"Whether a page got {attr}.", + )) + page.append(( + f"{enum_root}_setter", + f"{name}_setter", + f"Whether a page set {attr}.", + )) + doc.append(( + f"{enum_root}_getter", + f"{name}_getter", + f"Whether a document got {attr}.", + )) + doc.append(( + f"{enum_root}_setter", + f"{name}_setter", + f"Whether a document set {attr}.", + )) elif counter["type"] == "custom": enum_name = f"eUseCounter_custom_{counter['name']}" - page.append( - ( - enum_name, - to_snake_case(counter["name"]), - f"Whether a page {counter['desc']}.", - ) - ) - doc.append( - ( - enum_name, - to_snake_case(counter["name"]), - f"Whether a document {counter['desc']}.", - ) - ) + page.append(( + enum_name, + to_snake_case(counter["name"]), + f"Whether a page {counter['desc']}.", + )) + doc.append(( + enum_name, + to_snake_case(counter["name"]), + f"Whether a document {counter['desc']}.", + )) else: print(f"Found unexpected use counter type {counter['type']}. Returning 1.") return 1 @@ -434,84 +430,72 @@ def parse_use_counters(): enum_name = f"{counter['interface_name']}_{counter['method_name']}" name = f"{counter['interface_name']}_{counter['method_name']}".lower() method = f"called {counter['interface_name']}.{counter['method_name']}" - dedicated.append( - (enum_name, name, f"Whether a dedicated worker called {method}.") - ) - shared.append( - (enum_name, name, f"Whether a shared worker called {method}.") - ) - service.append( - (enum_name, name, f"Whether a service worker called {method}.") - ) + dedicated.append(( + enum_name, + name, + f"Whether a dedicated worker called {method}.", + )) + shared.append(( + enum_name, + name, + f"Whether a shared worker called {method}.", + )) + service.append(( + enum_name, + name, + f"Whether a service worker called {method}.", + )) elif counter["type"] == "attribute": enum_root = f"{counter['interface_name']}_{counter['attribute_name']}" name = f"{counter['interface_name']}_{counter['attribute_name']}".lower() attr = f"{counter['interface_name']}.{counter['attribute_name']}" - dedicated.append( - ( - f"{enum_root}_getter", - f"{name}_getter", - f"Whether a dedicated worker got {attr}.", - ) - ) - dedicated.append( - ( - f"{enum_root}_setter", - f"{name}_setter", - f"Whether a dedicated worker set {attr}.", - ) - ) - shared.append( - ( - f"{enum_root}_getter", - f"{name}_getter", - f"Whether a shared worker got {attr}.", - ) - ) - shared.append( - ( - f"{enum_root}_setter", - f"{name}_setter", - f"Whether a shared worker set {attr}.", - ) - ) - service.append( - ( - f"{enum_root}_getter", - f"{name}_getter", - f"Whether a service worker got {attr}.", - ) - ) - service.append( - ( - f"{enum_root}_setter", - f"{name}_setter", - f"Whether a service worker set {attr}.", - ) - ) + dedicated.append(( + f"{enum_root}_getter", + f"{name}_getter", + f"Whether a dedicated worker got {attr}.", + )) + dedicated.append(( + f"{enum_root}_setter", + f"{name}_setter", + f"Whether a dedicated worker set {attr}.", + )) + shared.append(( + f"{enum_root}_getter", + f"{name}_getter", + f"Whether a shared worker got {attr}.", + )) + shared.append(( + f"{enum_root}_setter", + f"{name}_setter", + f"Whether a shared worker set {attr}.", + )) + service.append(( + f"{enum_root}_getter", + f"{name}_getter", + f"Whether a service worker got {attr}.", + )) + service.append(( + f"{enum_root}_setter", + f"{name}_setter", + f"Whether a service worker set {attr}.", + )) elif counter["type"] == "custom": enum_name = f"Custom_{counter['name']}" - dedicated.append( - ( - enum_name, - to_snake_case(counter["name"]), - f"Whether a dedicated worker {counter['desc']}.", - ) - ) - shared.append( - ( - enum_name, - to_snake_case(counter["name"]), - f"Whether a shared worker {counter['desc']}.", - ) - ) - service.append( - ( - enum_name, - to_snake_case(counter["name"]), - f"Whether a service worker {counter['desc']}.", - ) - ) + dedicated.append(( + enum_name, + to_snake_case(counter["name"]), + f"Whether a dedicated worker {counter['desc']}.", + )) + shared.append(( + enum_name, + to_snake_case(counter["name"]), + f"Whether a shared worker {counter['desc']}.", + )) + service.append(( + enum_name, + to_snake_case(counter["name"]), + f"Whether a service worker {counter['desc']}.", + )) else: print( f"Found unexpected worker use counter type {counter['type']}. Returning 1." @@ -564,16 +548,16 @@ def parse_use_counters(): method = method[3:] # remove the moz prefix enum_name = f"eUseCounter_property_{method}" - css_page.append( - (enum_name, prop_name, f"Whether a page used the CSS property {prop.name}.") - ) - css_doc.append( - ( - enum_name, - prop_name, - f"Whether a document used the CSS property {prop.name}.", - ) - ) + css_page.append(( + enum_name, + prop_name, + f"Whether a page used the CSS property {prop.name}.", + )) + css_doc.append(( + enum_name, + prop_name, + f"Whether a document used the CSS property {prop.name}.", + )) # Counted unknown properties: AKA - stuff that doesn't exist, but we want # to count uses of anyway. @@ -600,20 +584,16 @@ def parse_use_counters(): for prop in unknown_properties: enum_name = f"eUseCounter_unknown_property_{to_camel_case(prop)}" prop_name = to_snake_case(prop) - css_page.append( - ( - enum_name, - prop_name, - f"Whether a page used the (unknown, counted) CSS property {prop}.", - ) - ) - css_doc.append( - ( - enum_name, - prop_name, - f"Whether a document used the (unknown, counted) CSS property {prop}.", - ) - ) + css_page.append(( + enum_name, + prop_name, + f"Whether a page used the (unknown, counted) CSS property {prop}.", + )) + css_doc.append(( + enum_name, + prop_name, + f"Whether a document used the (unknown, counted) CSS property {prop}.", + )) return (page, doc, dedicated, shared, service, ops_page, ops_doc, css_page, css_doc) diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index e69e23a930036..8b26d797bfd13 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -837,8 +837,7 @@ def addOperation(operation, m): ) if len(m.signatures()) != 1: raise TypeError( - "We don't support overloaded " - "legacycaller.\n%s" % m.location + "We don't support overloaded legacycaller.\n%s" % m.location ) addOperation("LegacyCaller", m) diff --git a/dom/bindings/parser/tests/test_attr_sequence_type.py b/dom/bindings/parser/tests/test_attr_sequence_type.py index 7a23265daf673..80c89aff7474f 100644 --- a/dom/bindings/parser/tests/test_attr_sequence_type.py +++ b/dom/bindings/parser/tests/test_attr_sequence_type.py @@ -54,7 +54,7 @@ def WebIDLTest(parser, harness): harness.ok( threw, - "Attribute type must not be a union with a nullable sequence " "member type", + "Attribute type must not be a union with a nullable sequence member type", ) parser.reset() diff --git a/dom/bindings/parser/tests/test_constructor.py b/dom/bindings/parser/tests/test_constructor.py index 22712245cd31f..bb9032ec06f19 100644 --- a/dom/bindings/parser/tests/test_constructor.py +++ b/dom/bindings/parser/tests/test_constructor.py @@ -446,7 +446,7 @@ def checkResults(results): harness.ok( threw, - "Can't have both a HTMLConstructor and a throwing constructor " "operation", + "Can't have both a HTMLConstructor and a throwing constructor operation", ) # Test HTMLConstructor and [ChromeOnly] constructor operation @@ -486,7 +486,7 @@ def checkResults(results): harness.ok( threw, - "Can't have both a throwing chromeonly constructor and a " "HTMLConstructor", + "Can't have both a throwing chromeonly constructor and a HTMLConstructor", ) parser = parser.reset() @@ -507,7 +507,7 @@ def checkResults(results): harness.ok( threw, - "Can't have both a HTMLConstructor and a chromeonly constructor " "operation", + "Can't have both a HTMLConstructor and a chromeonly constructor operation", ) parser = parser.reset() @@ -549,8 +549,7 @@ def checkResults(results): harness.ok( threw, - "Can't have a constructor operation on a [LegacyNoInterfaceObject] " - "interface", + "Can't have a constructor operation on a [LegacyNoInterfaceObject] interface", ) parser = parser.reset() diff --git a/dom/bindings/parser/tests/test_dictionary.py b/dom/bindings/parser/tests/test_dictionary.py index 80c13a4238e8d..2c9efd6f8bd3b 100644 --- a/dom/bindings/parser/tests/test_dictionary.py +++ b/dom/bindings/parser/tests/test_dictionary.py @@ -120,7 +120,7 @@ def WebIDLTest(parser, harness): threw = True harness.ok( - threw, "Should not allow name duplication in a dictionary and " "its ancestor" + threw, "Should not allow name duplication in a dictionary and its ancestor" ) # More reset @@ -324,7 +324,7 @@ def WebIDLTest(parser, harness): harness.ok( threw, - "Union arg containing dictionary followed by optional arg must " "be optional", + "Union arg containing dictionary followed by optional arg must be optional", ) parser = parser.reset() @@ -404,7 +404,7 @@ def WebIDLTest(parser, harness): harness.ok(threw, "Required dictionary arg must not be nullable") harness.ok( "nullable" in str(threw), - "Must have the expected exception for required nullable " "dictionary arg", + "Must have the expected exception for required nullable dictionary arg", ) parser = parser.reset() @@ -605,7 +605,7 @@ def WebIDLTest(parser, harness): harness.ok( threw, - "Member type must not be a Dictionary that " "inherits from its Dictionary.", + "Member type must not be a Dictionary that inherits from its Dictionary.", ) parser = parser.reset() diff --git a/dom/bindings/parser/tests/test_distinguishability.py b/dom/bindings/parser/tests/test_distinguishability.py index c1365ce16800c..a89db729b3384 100644 --- a/dom/bindings/parser/tests/test_distinguishability.py +++ b/dom/bindings/parser/tests/test_distinguishability.py @@ -58,11 +58,11 @@ def WebIDLTest(parser, harness): harness.ok( not nullableUnionType.isDistinguishableFrom(nullableIfaceType), - "Nullable type not distinguishable from union with nullable " "member type", + "Nullable type not distinguishable from union with nullable member type", ) harness.ok( not nullableIfaceType.isDistinguishableFrom(nullableUnionType), - "Union with nullable member type not distinguishable from " "nullable type", + "Union with nullable member type not distinguishable from nullable type", ) parser = parser.reset() diff --git a/dom/bindings/parser/tests/test_empty_sequence_default_value.py b/dom/bindings/parser/tests/test_empty_sequence_default_value.py index 4771a6b1e54bb..a89703f16f193 100644 --- a/dom/bindings/parser/tests/test_empty_sequence_default_value.py +++ b/dom/bindings/parser/tests/test_empty_sequence_default_value.py @@ -50,5 +50,5 @@ def WebIDLTest(parser, harness): harness.ok( isinstance(results[0].members[0].defaultValue, WebIDL.IDLEmptySequenceValue), - "Should have IDLEmptySequenceValue as default value of " "dictionary member", + "Should have IDLEmptySequenceValue as default value of dictionary member", ) diff --git a/dom/bindings/parser/tests/test_global_extended_attr.py b/dom/bindings/parser/tests/test_global_extended_attr.py index 9d52747a1dabc..ef2c9e9dffbc9 100644 --- a/dom/bindings/parser/tests/test_global_extended_attr.py +++ b/dom/bindings/parser/tests/test_global_extended_attr.py @@ -42,7 +42,7 @@ def WebIDLTest(parser, harness): harness.ok( threw, - "Should have thrown for [Global] used on an interface with a " "named setter", + "Should have thrown for [Global] used on an interface with a named setter", ) parser = parser.reset() @@ -63,7 +63,7 @@ def WebIDLTest(parser, harness): harness.ok( threw, - "Should have thrown for [Global] used on an interface with a " "named deleter", + "Should have thrown for [Global] used on an interface with a named deleter", ) parser = parser.reset() @@ -128,7 +128,7 @@ def WebIDLTest(parser, harness): harness.ok( threw, - "Should have thrown for [Global] used on an interface with a " "descendant", + "Should have thrown for [Global] used on an interface with a descendant", ) parser = parser.reset() diff --git a/dom/bindings/parser/tests/test_interface.py b/dom/bindings/parser/tests/test_interface.py index 5b32a27f4d326..82b7e6bea61cf 100644 --- a/dom/bindings/parser/tests/test_interface.py +++ b/dom/bindings/parser/tests/test_interface.py @@ -263,8 +263,7 @@ def WebIDLTest(parser, harness): threw = True harness.ok( threw, - "Should not allow a name collision between partial interface " - "and other object", + "Should not allow a name collision between partial interface and other object", ) parser = parser.reset() @@ -284,7 +283,7 @@ def WebIDLTest(parser, harness): except WebIDL.WebIDLError: threw = True harness.ok( - threw, "Should not allow a name collision between interface " "and other object" + threw, "Should not allow a name collision between interface and other object" ) parser = parser.reset() @@ -303,8 +302,7 @@ def WebIDLTest(parser, harness): threw = True harness.ok( threw, - "Should not allow a name collision between external interface " - "and other object", + "Should not allow a name collision between external interface and other object", ) parser = parser.reset() @@ -323,7 +321,7 @@ def WebIDLTest(parser, harness): threw = True harness.ok( threw, - "Should not allow a name collision between external interface " "and interface", + "Should not allow a name collision between external interface and interface", ) parser = parser.reset() @@ -336,7 +334,7 @@ def WebIDLTest(parser, harness): results = parser.finish() harness.ok( len(results) == 1 and isinstance(results[0], WebIDL.IDLExternalInterface), - "Should allow name collisions between external interface " "declarations", + "Should allow name collisions between external interface declarations", ) parser = parser.reset() diff --git a/dom/bindings/parser/tests/test_interfacemixin.py b/dom/bindings/parser/tests/test_interfacemixin.py index 297cc10915093..3b11e5eeb7e5a 100644 --- a/dom/bindings/parser/tests/test_interfacemixin.py +++ b/dom/bindings/parser/tests/test_interfacemixin.py @@ -219,7 +219,7 @@ def WebIDLTest(parser, harness): threw = True harness.ok( threw, - "Should not allow a name collision between interface mixin " "and other object", + "Should not allow a name collision between interface mixin and other object", ) parser = parser.reset() @@ -523,12 +523,12 @@ def WebIDLTest(parser, harness): harness.check( attr.exposureSet, set(["Window", "Worker"]), - "Should expose on all globals where including interfaces are " "exposed", + "Should expose on all globals where including interfaces are exposed", ) base = results[3] attr = base.members[0] harness.check( attr.exposureSet, set(["Window", "Worker"]), - "Should expose on all globals where including interfaces are " "exposed", + "Should expose on all globals where including interfaces are exposed", ) diff --git a/dom/bindings/parser/tests/test_optional_constraints.py b/dom/bindings/parser/tests/test_optional_constraints.py index 34f03467621f8..3391179535b2c 100644 --- a/dom/bindings/parser/tests/test_optional_constraints.py +++ b/dom/bindings/parser/tests/test_optional_constraints.py @@ -18,8 +18,7 @@ def WebIDLTest(parser, harness): harness.ok( not threw, - "Should not have thrown on non-optional argument following " - "optional argument.", + "Should not have thrown on non-optional argument following optional argument.", ) parser = parser.reset() diff --git a/dom/bindings/parser/tests/test_promise.py b/dom/bindings/parser/tests/test_promise.py index 1dfb6d4643c38..9e59eb5d98701 100644 --- a/dom/bindings/parser/tests/test_promise.py +++ b/dom/bindings/parser/tests/test_promise.py @@ -98,9 +98,7 @@ def WebIDLTest(parser, harness): ) parser.finish() - harness.ok( - True, "Should allow overloads which only have Promise and return " "types." - ) + harness.ok(True, "Should allow overloads which only have Promise and return types.") parser = parser.reset() threw = False diff --git a/dom/bindings/parser/tests/test_toJSON.py b/dom/bindings/parser/tests/test_toJSON.py index ec81080becae4..fe008afe4e451 100644 --- a/dom/bindings/parser/tests/test_toJSON.py +++ b/dom/bindings/parser/tests/test_toJSON.py @@ -167,7 +167,7 @@ def doTest(testIDL, shouldThrow, description): ) doTest( - "dictionary Foo { %s foo; }; " "interface Test { Foo toJSON(); }; " % type, + "dictionary Foo { %s foo; }; interface Test { Foo toJSON(); }; " % type, False, "dictionary containing only JSON type (%s) should be a JSON type" % type, ) @@ -233,7 +233,7 @@ def doTest(testIDL, shouldThrow, description): ) doTest( - "interface Foo : InterfaceWithToJSON { };" "interface Test { Foo toJSON(); };", + "interface Foo : InterfaceWithToJSON { };interface Test { Foo toJSON(); };", False, "inherited interface with toJSON should be a JSON type", ) @@ -252,7 +252,7 @@ def doTest(testIDL, shouldThrow, description): ) doTest( - "dictionary Foo { %s foo; }; " "interface Test { Foo toJSON(); }; " % type, + "dictionary Foo { %s foo; }; interface Test { Foo toJSON(); }; " % type, True, "Dictionary containing a non-JSON type (%s) should not be a JSON type" % type, @@ -299,14 +299,13 @@ def doTest(testIDL, shouldThrow, description): ) doTest( - "dictionary Foo { long foo; any bar; };" "interface Test { Foo toJSON(); };", + "dictionary Foo { long foo; any bar; };interface Test { Foo toJSON(); };", True, "dictionary containing a non-JSON type should not be a JSON type", ) doTest( - "interface Foo : InterfaceWithoutToJSON { }; " - "interface Test { Foo toJSON(); };", + "interface Foo : InterfaceWithoutToJSON { }; interface Test { Foo toJSON(); };", True, "interface without toJSON should not be a JSON type", ) diff --git a/dom/bindings/parser/tests/test_typedef.py b/dom/bindings/parser/tests/test_typedef.py index 9f2fd1f3c8c9c..d4f658f0b3ada 100644 --- a/dom/bindings/parser/tests/test_typedef.py +++ b/dom/bindings/parser/tests/test_typedef.py @@ -74,8 +74,7 @@ def WebIDLTest(parser, harness): harness.ok( threw, - "Should have thrown on nullable inside nullable const typedef " - "after interface.", + "Should have thrown on nullable inside nullable const typedef after interface.", ) parser = parser.reset() diff --git a/dom/bindings/parser/tests/test_union.py b/dom/bindings/parser/tests/test_union.py index 20a4c0238dbdc..247c5eb5dbefe 100644 --- a/dom/bindings/parser/tests/test_union.py +++ b/dom/bindings/parser/tests/test_union.py @@ -174,16 +174,13 @@ def invalidUnionWithUnion(typeCombinations): parser = parser.reset() for invalid in invalidUnionTypes: - interface = ( - testPre - + string.Template( - """ + interface = testPre + string.Template( + """ interface TestUnion { undefined method(${type} arg); }; """ - ).substitute(type=invalid) - ) + ).substitute(type=invalid) threw = False try: diff --git a/dom/bindings/parser/tests/test_union_nullable.py b/dom/bindings/parser/tests/test_union_nullable.py index d5ae2e1e74cb7..7c2a1b26f69fa 100644 --- a/dom/bindings/parser/tests/test_union_nullable.py +++ b/dom/bindings/parser/tests/test_union_nullable.py @@ -36,7 +36,7 @@ def WebIDLTest(parser, harness): harness.ok( threw, - "A nullable union type with a nullable member type should have " "thrown.", + "A nullable union type with a nullable member type should have thrown.", ) parser.reset() @@ -59,5 +59,5 @@ def WebIDLTest(parser, harness): harness.ok( threw, - "A nullable union type with a nullable member type should have " "thrown.", + "A nullable union type with a nullable member type should have thrown.", ) diff --git a/dom/bindings/parser/tests/test_variadic_constraints.py b/dom/bindings/parser/tests/test_variadic_constraints.py index 749c4a7812a25..6a7743b164208 100644 --- a/dom/bindings/parser/tests/test_variadic_constraints.py +++ b/dom/bindings/parser/tests/test_variadic_constraints.py @@ -18,7 +18,7 @@ def WebIDLTest(parser, harness): harness.ok( threw, - "Should have thrown on variadic argument followed by required " "argument.", + "Should have thrown on variadic argument followed by required argument.", ) parser = parser.reset() @@ -37,7 +37,7 @@ def WebIDLTest(parser, harness): harness.ok( threw, - "Should have thrown on variadic argument followed by optional " "argument.", + "Should have thrown on variadic argument followed by optional argument.", ) parser = parser.reset() @@ -57,7 +57,7 @@ def WebIDLTest(parser, harness): harness.ok( threw, - "Should have thrown on variadic argument explicitly flagged as " "optional.", + "Should have thrown on variadic argument explicitly flagged as optional.", ) parser = parser.reset() diff --git a/dom/canvas/test/reftest/colors/generate_color_canvas_reftests.py b/dom/canvas/test/reftest/colors/generate_color_canvas_reftests.py index 48621d00c1ba7..7ee8930722abb 100644 --- a/dom/canvas/test/reftest/colors/generate_color_canvas_reftests.py +++ b/dom/canvas/test/reftest/colors/generate_color_canvas_reftests.py @@ -332,7 +332,7 @@ def encode_url_v(k, v): def url_from_config(kvs: Config) -> str: - parts = [f"{k}={encode_url_v(k,v)}" for k, v in kvs.items()] + parts = [f"{k}={encode_url_v(k, v)}" for k, v in kvs.items()] url = "color_canvas.html?" + "&".join(parts) return url @@ -405,7 +405,7 @@ def aligned_lines_from_table( {GENERATED_FILE_LINE} {GENERATED_FILE_LINE} -# Generated by `{' '.join(GENERATED_BY_ARGS)}`. +# Generated by `{" ".join(GENERATED_BY_ARGS)}`. # - defaults pref(webgl.drawing_buffer_color_space,true) @@ -414,9 +414,9 @@ def aligned_lines_from_table( # - # Ensure not white-screening: -!= {url_from_config({})+'='} about:blank +!= {url_from_config({}) + "="} about:blank # Ensure differing results with different args: -!= {url_from_config({'e_color':'color(srgb 1 0 0)'})} {url_from_config({'e_color':'color(srgb 0 1 0)'})} +!= {url_from_config({"e_color": "color(srgb 1 0 0)"})} {url_from_config({"e_color": "color(srgb 0 1 0)"})} {GENERATED_FILE_LINE} # - diff --git a/dom/canvas/test/webgl-conf/generate-wrappers-and-manifest.py b/dom/canvas/test/webgl-conf/generate-wrappers-and-manifest.py index 12080689ec68e..dce1bdeea6cfb 100755 --- a/dom/canvas/test/webgl-conf/generate-wrappers-and-manifest.py +++ b/dom/canvas/test/webgl-conf/generate-wrappers-and-manifest.py @@ -32,14 +32,12 @@ "mochi-single.html", ] -ACCEPTABLE_ERRATA_KEYS = set( - [ - "fail-if", - "prefs", - "skip-if", - "tags", - ] -) +ACCEPTABLE_ERRATA_KEYS = set([ + "fail-if", + "prefs", + "skip-if", + "tags", +]) def ChooseSubsuite(name): @@ -475,9 +473,9 @@ def LoadTOML(path): if line[0] == "[": assert line[-1] == "]", f"{path}:{lineNum}" curSectionName = line[1:-1].strip('"') - assert ( - curSectionName not in ret - ), f"Line {lineNum}: Duplicate section: {line}" + assert curSectionName not in ret, ( + f"Line {lineNum}: Duplicate section: {line}" + ) curSectionMap = {} ret[curSectionName] = (lineNum, curSectionMap) continue @@ -506,9 +504,9 @@ def LoadErrata(): continue elif sectionName != "DEFAULT": path = sectionName.replace("/", os.sep) - assert os.path.exists( - path - ), f"Errata line {sectionLineNum}: Invalid file: {sectionName}" + assert os.path.exists(path), ( + f"Errata line {sectionLineNum}: Invalid file: {sectionName}" + ) for key, (lineNum, val) in sectionMap.items(): assert key in ACCEPTABLE_ERRATA_KEYS, f"Line {lineNum}: {key}" diff --git a/dom/canvas/test/webgl-conf/import.py b/dom/canvas/test/webgl-conf/import.py index dbf49cb15488e..7ce1e955a16cd 100755 --- a/dom/canvas/test/webgl-conf/import.py +++ b/dom/canvas/test/webgl-conf/import.py @@ -13,9 +13,9 @@ REL_PATH = "/dom/canvas/test/webgl-conf" REPO_DIR = Path.cwd() DIR_IN_GECKO = Path(__file__).parent -assert not REPO_DIR.samefile( - DIR_IN_GECKO -), "Run this script from the source git checkout." +assert not REPO_DIR.samefile(DIR_IN_GECKO), ( + "Run this script from the source git checkout." +) assert DIR_IN_GECKO.as_posix().endswith(REL_PATH) # Be paranoid with rm -rf. gecko_base_dir = DIR_IN_GECKO.as_posix()[: -len(REL_PATH)] diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp index 927b0b37feb08..9176a93aef525 100644 --- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -332,6 +332,19 @@ EventTarget* Event::GetOriginalTarget() const { return mEvent->GetOriginalDOMEventTarget(); } +EventTarget* Event::GetOriginalTarget(CallerType aCallerType) const { + if (aCallerType == CallerType::System || nsContentUtils::IsCallerUAWidget()) { + return GetOriginalTarget(); + } + + EventTarget* et = mEvent->GetOriginalDOMEventTarget(); + nsIContent* content = nsIContent::FromEventTargetOrNull(et); + if (!content) { + return et; + } + return content->FindFirstNonChromeOnlyAccessContent(); +} + EventTarget* Event::GetComposedTarget() const { EventTarget* et = GetOriginalTarget(); nsIContent* content = nsIContent::FromEventTargetOrNull(et); diff --git a/dom/events/Event.h b/dom/events/Event.h index 7eb8b206d1df4..57c4114f2ad5b 100644 --- a/dom/events/Event.h +++ b/dom/events/Event.h @@ -360,6 +360,7 @@ class Event : public nsISupports, public nsWrapperCache { double TimeStamp(); EventTarget* GetOriginalTarget() const; + EventTarget* GetOriginalTarget(CallerType aCallerType) const; EventTarget* GetExplicitOriginalTarget() const; EventTarget* GetComposedTarget() const; diff --git a/dom/media/driftcontrol/plot.py b/dom/media/driftcontrol/plot.py index c30542cdf3ff2..2a9f98a7aee62 100755 --- a/dom/media/driftcontrol/plot.py +++ b/dom/media/driftcontrol/plot.py @@ -106,12 +106,10 @@ def main(): absAvgError = [abs(e) for e in avgError] slow_offset = [e / slowConvergenceSecs - slowHysteresis for e in absAvgError] fast_offset = [e / adjustmentInterval for e in absAvgError] - low_offset, high_offset = zip( - *[ - (s, f) if e >= 0 else (-f, -s) - for (e, s, f) in zip(avgError, slow_offset, fast_offset) - ] - ) + low_offset, high_offset = zip(*[ + (s, f) if e >= 0 else (-f, -s) + for (e, s, f) in zip(avgError, slow_offset, fast_offset) + ]) fig2 = figure(x_range=fig1.x_range) fig2.varea( diff --git a/dom/media/gtest/MockGraphInterface.h b/dom/media/gtest/MockGraphInterface.h index 17e40b5e13785..d381213fb8a67 100644 --- a/dom/media/gtest/MockGraphInterface.h +++ b/dom/media/gtest/MockGraphInterface.h @@ -4,6 +4,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef MOCK_GRAPH_INTERFACE_H_ +#define MOCK_GRAPH_INTERFACE_H_ + #include #include "GraphDriver.h" @@ -95,3 +98,5 @@ class MockGraphInterface : public GraphInterface { }; } // namespace mozilla + +#endif diff --git a/dom/media/test/reftest/gen_combos.py b/dom/media/test/reftest/gen_combos.py index 6e3f337223677..e85f32d603a99 100644 --- a/dom/media/test/reftest/gen_combos.py +++ b/dom/media/test/reftest/gen_combos.py @@ -69,83 +69,75 @@ def eprint(*args, **kwargs): # - -FORMAT_LIST = set( - [ +FORMAT_LIST = set([ + "yuv420p", + "yuv420p10", + # 'yuv420p12', + # 'yuv420p16be', + # 'yuv420p16le', + "gbrp", +]) + +if "--all" in ARGS: + FORMAT_LIST |= set([ "yuv420p", "yuv420p10", - # 'yuv420p12', - # 'yuv420p16be', - # 'yuv420p16le', + "yuv420p12", + "yuv420p16be", + "yuv420p16le", + "yuv422p", + "yuv422p10", + "yuv422p12", + "yuv422p16be", + "yuv422p16le", + "yuv444p", + "yuv444p10", + "yuv444p12", + "yuv444p16be", + "yuv444p16le", + "yuv411p", + "yuv410p", + "yuyv422", + "uyvy422", + "rgb24", + "bgr24", + "rgb8", + "bgr8", + "rgb444be", + "rgb444le", + "bgr444be", + "bgr444le", + # 'nv12', # Encoding not different than yuv420p? + # 'nv21', # Encoding not different than yuv420p? "gbrp", - ] -) - -if "--all" in ARGS: - FORMAT_LIST |= set( - [ - "yuv420p", - "yuv420p10", - "yuv420p12", - "yuv420p16be", - "yuv420p16le", - "yuv422p", - "yuv422p10", - "yuv422p12", - "yuv422p16be", - "yuv422p16le", - "yuv444p", - "yuv444p10", - "yuv444p12", - "yuv444p16be", - "yuv444p16le", - "yuv411p", - "yuv410p", - "yuyv422", - "uyvy422", - "rgb24", - "bgr24", - "rgb8", - "bgr8", - "rgb444be", - "rgb444le", - "bgr444be", - "bgr444le", - # 'nv12', # Encoding not different than yuv420p? - # 'nv21', # Encoding not different than yuv420p? - "gbrp", - "gbrp9be", - "gbrp9le", - "gbrp10be", - "gbrp10le", - "gbrp12be", - "gbrp12le", - "gbrp14be", - "gbrp14le", - "gbrp16be", - "gbrp16le", - ] - ) + "gbrp9be", + "gbrp9le", + "gbrp10be", + "gbrp10le", + "gbrp12be", + "gbrp12le", + "gbrp14be", + "gbrp14le", + "gbrp16be", + "gbrp16le", + ]) FORMATS = keyed_combiner("format", list(FORMAT_LIST)) RANGE = keyed_combiner("range", ["tv", "pc"]) -CSPACE_LIST = set( - [ - "bt709", - # 'bt2020', - ] -) +CSPACE_LIST = set([ + "bt709", + # 'bt2020', +]) if "--all" in ARGS: - CSPACE_LIST |= set( - [ - "bt709", - "bt2020", - "bt601-6-525", # aka smpte170m NTSC - "bt601-6-625", # aka bt470bg PAL - ] - ) + CSPACE_LIST |= set([ + "bt709", + "bt2020", + "bt601-6-525", # aka smpte170m NTSC + "bt601-6-625", # aka bt470bg PAL + ]) CSPACE_LIST = list(CSPACE_LIST) # - @@ -164,17 +156,15 @@ def eprint(*args, **kwargs): todo = [] for c in COMBOS: - dst_name = ".".join( - [ - SRC_PATH.name, - c["src_cspace"], - c["dst_cspace"], - c["range"], - c["format"], - c["vcodec"], - c["ext"], - ] - ) + dst_name = ".".join([ + SRC_PATH.name, + c["src_cspace"], + c["dst_cspace"], + c["range"], + c["format"], + c["vcodec"], + c["ext"], + ]) src_cspace = c["src_cspace"] diff --git a/dom/media/tools/checkGmpBalrog.py b/dom/media/tools/checkGmpBalrog.py index c5a8e362046f6..70318f9543597 100644 --- a/dom/media/tools/checkGmpBalrog.py +++ b/dom/media/tools/checkGmpBalrog.py @@ -47,15 +47,13 @@ def fetch_balrog_xml( for channel in channels: results[channel] = {} for target in targets: - balrog_url = url.format_map( - { - "url_base": url_base, - "buildid": buildid, - "channel": channel, - "version": version, - "target": target, - } - ) + balrog_url = url.format_map({ + "url_base": url_base, + "buildid": buildid, + "channel": channel, + "version": version, + "target": target, + }) response = requests.get(balrog_url) response.raise_for_status() diff --git a/dom/media/tools/generateGmpJson.py b/dom/media/tools/generateGmpJson.py index 9bf90bf2b3c53..abdba20001df2 100644 --- a/dom/media/tools/generateGmpJson.py +++ b/dom/media/tools/generateGmpJson.py @@ -32,9 +32,14 @@ def fetch_url_for_cdms(cdms, urlParams): "{} expected https scheme '{}'".format(cdm["target"], redirectUrl) ) - sanitizedUrl = urlunparse( - (parsedUrl.scheme, parsedUrl.netloc, parsedUrl.path, None, None, None) - ) + sanitizedUrl = urlunparse(( + parsedUrl.scheme, + parsedUrl.netloc, + parsedUrl.path, + None, + None, + None, + )) # Note that here we modify the returned URL from the # component update service because it returns a preferred diff --git a/dom/media/webrtc/third_party_build/filter_git_changes.py b/dom/media/webrtc/third_party_build/filter_git_changes.py index c0ec3421f0f2e..fbb9ad285229f 100644 --- a/dom/media/webrtc/third_party_build/filter_git_changes.py +++ b/dom/media/webrtc/third_party_build/filter_git_changes.py @@ -47,9 +47,9 @@ def filter_git_changes(github_path, commit_sha, diff_filter): # Convert the directory exclude list to a regex string and filter # out the excluded directory paths (note the lack of trailing '$' # in the regex). - regex_excludes = "|".join( - [f"^(M|A|D|R\\d\\d\\d)\t{incl}" for incl in exclude_dir_list] - ) + regex_excludes = "|".join([ + f"^(M|A|D|R\\d\\d\\d)\t{incl}" for incl in exclude_dir_list + ]) files_not_excluded = [ path for path in changed_files if not re.findall(regex_excludes, path) ] diff --git a/dom/media/webrtc/third_party_build/write_default_config.py b/dom/media/webrtc/third_party_build/write_default_config.py index 72cc573a039a1..886c4baf028c4 100644 --- a/dom/media/webrtc/third_party_build/write_default_config.py +++ b/dom/media/webrtc/third_party_build/write_default_config.py @@ -220,6 +220,6 @@ def build_default_config_env( run_shell( f"{'git commit -m' if repo_type == RepoType.GIT else 'hg commit --message'} " f'"Bug {args.bug_number} - ' - f'updated default_config_env for v{args.milestone+1}"' + f'updated default_config_env for v{args.milestone + 1}"' f" {args.output_path}" ) diff --git a/dom/origin-trials/gen-keys.py b/dom/origin-trials/gen-keys.py index 6d00d695cf024..aae56d945752d 100644 --- a/dom/origin-trials/gen-keys.py +++ b/dom/origin-trials/gen-keys.py @@ -17,12 +17,12 @@ def public_key_to_string(file, name): ) key = decoder.decode(substrate) ident = key[0][0] - assert ident[0] == univ.ObjectIdentifier( - "1.2.840.10045.2.1" - ), "should be an ECPublicKey" - assert ident[1] == univ.ObjectIdentifier( - "1.2.840.10045.3.1.7" - ), "should be a EcdsaP256 key" + assert ident[0] == univ.ObjectIdentifier("1.2.840.10045.2.1"), ( + "should be an ECPublicKey" + ) + assert ident[1] == univ.ObjectIdentifier("1.2.840.10045.3.1.7"), ( + "should be a EcdsaP256 key" + ) bits = key[0][1] assert isinstance(bits, univ.BitString), "Should be a bit string" assert len(bits) == 520, "Should be 520 bits (65 bytes)" diff --git a/dom/quota/scripts/qm-try-analysis/qm_try_analysis/analyze.py b/dom/quota/scripts/qm-try-analysis/qm_try_analysis/analyze.py index 1173555e08551..67cf92c314061 100755 --- a/dom/quota/scripts/qm-try-analysis/qm_try_analysis/analyze.py +++ b/dom/quota/scripts/qm-try-analysis/qm_try_analysis/analyze.py @@ -63,7 +63,7 @@ def analyze_qm_failures(output_to, workdir): sys.exit(2) if output_to == "qmstacks_until_.txt": - output_to = path.join(workdir, f'qmstacks_until_{run["lasteventtime"]}.txt') + output_to = path.join(workdir, f"qmstacks_until_{run['lasteventtime']}.txt") elif output_to.exists(): error( f'The output file "{output_to}" already exists. This script would override it.' diff --git a/dom/quota/scripts/qm-try-analysis/qm_try_analysis/fn_anchors.py b/dom/quota/scripts/qm-try-analysis/qm_try_analysis/fn_anchors.py index 2280b0b2fce69..cdeb1b2d7f055 100644 --- a/dom/quota/scripts/qm-try-analysis/qm_try_analysis/fn_anchors.py +++ b/dom/quota/scripts/qm-try-analysis/qm_try_analysis/fn_anchors.py @@ -14,12 +14,10 @@ def getMetricsJson(src_url): if src_url.startswith("http"): info(f"Fetching source for function extraction: {src_url}") - metrics = subprocess.check_output( - [ - path.join(path.dirname(path.realpath(__file__)), "fetch_fn_names.sh"), - src_url, - ] - ) + metrics = subprocess.check_output([ + path.join(path.dirname(path.realpath(__file__)), "fetch_fn_names.sh"), + src_url, + ]) else: warning(f"Skip fetching source: {src_url}") metrics = "" @@ -37,13 +35,11 @@ def getSpaceFunctionsRecursive(metrics_space): and metrics_space["name"] and metrics_space["name"] != "" ): - functions.append( - { - "name": metrics_space["name"], - "start_line": int(metrics_space["start_line"]), - "end_line": int(metrics_space["end_line"]), - } - ) + functions.append({ + "name": metrics_space["name"], + "start_line": int(metrics_space["start_line"]), + "end_line": int(metrics_space["end_line"]), + }) for space in metrics_space["spaces"]: functions += getSpaceFunctionsRecursive(space) return functions diff --git a/dom/quota/scripts/qm-try-analysis/qm_try_analysis/report.py b/dom/quota/scripts/qm-try-analysis/qm_try_analysis/report.py index cee3b0542e52b..f99a2ccdaba8c 100644 --- a/dom/quota/scripts/qm-try-analysis/qm_try_analysis/report.py +++ b/dom/quota/scripts/qm-try-analysis/qm_try_analysis/report.py @@ -191,9 +191,9 @@ def reduce(search_results, by: str) -> Union[int, None]: continue search_string = " ".join(filter(None, match.groups())) - search_results = bugzilla_client.search_bugs( - [{"product": "Core", "summary": search_string}] - )["bugs"] + search_results = bugzilla_client.search_bugs([ + {"product": "Core", "summary": search_string} + ])["bugs"] if bug_id := reduce(search_results, by=anchor): info(f'Found bug {BUGZILLA_BUG_URL + str(bug_id)} for anchor "{anchor}".') diff --git a/dom/quota/scripts/qm-try-analysis/qm_try_analysis/stackanalysis.py b/dom/quota/scripts/qm-try-analysis/qm_try_analysis/stackanalysis.py index f0363c5e1fec4..f72fe583de6ff 100644 --- a/dom/quota/scripts/qm-try-analysis/qm_try_analysis/stackanalysis.py +++ b/dom/quota/scripts/qm-try-analysis/qm_try_analysis/stackanalysis.py @@ -227,16 +227,14 @@ def collectRawStacks(rows): "frames": [], } - stack["frames"].append( - { - "location": row["location"], - "source_file": row["source_file"], - "source_line": row["source_line"], - "seq": row["seq"], - "severity": row["severity"], - "result": row["result"], - } - ) + stack["frames"].append({ + "location": row["location"], + "source_file": row["source_file"], + "source_line": row["source_line"], + "seq": row["seq"], + "severity": row["severity"], + "result": row["result"], + }) first = False return raw_stacks diff --git a/dom/security/sanitizer/update-safe-default-configuration.py b/dom/security/sanitizer/update-safe-default-configuration.py index 2255560761c16..8056d42de1549 100644 --- a/dom/security/sanitizer/update-safe-default-configuration.py +++ b/dom/security/sanitizer/update-safe-default-configuration.py @@ -114,44 +114,44 @@ def create_list_body(l): constexpr nsStaticAtom* kDefaultHTMLElements[] = {{ // clang-format off -{ xhtml_elements_body } +{xhtml_elements_body} // clang-format on }}; constexpr nsStaticAtom* kDefaultMathMLElements[] = {{ // clang-format off -{ mathml_elements_body } +{mathml_elements_body} // clang-format on }}; constexpr nsStaticAtom* kDefaultSVGElements[] = {{ // clang-format off -{ svg_elements_body } +{svg_elements_body} // clang-format on }}; constexpr nsStaticAtom* kDefaultAttributes[] = {{ // clang-format off -{ attributes_body } +{attributes_body} // clang-format on }}; // Data is encoded as: element, attributes..., nullptr constexpr nsStaticAtom* kHTMLElementWithAttributes[] = {{ // clang-format off -{ xhtml_element_with_attributes_body } +{xhtml_element_with_attributes_body} // clang-format on }}; constexpr nsStaticAtom* kMathMLElementWithAttributes[] = {{ // clang-format off -{ mathml_element_with_attributes_body } +{mathml_element_with_attributes_body} // clang-format on }}; constexpr nsStaticAtom* kSVGElementWithAttributes[] = {{ // clang-format off -{ svg_element_with_attributes_body } +{svg_element_with_attributes_body} // clang-format on }}; diff --git a/dom/svg/SVGContentUtils.cpp b/dom/svg/SVGContentUtils.cpp index b42b277ef1f8d..314e37614e644 100644 --- a/dom/svg/SVGContentUtils.cpp +++ b/dom/svg/SVGContentUtils.cpp @@ -182,7 +182,7 @@ void SVGContentUtils::GetStrokeOptions(AutoStrokeOptions* aStrokeOptions, const nsStyleSVG* styleSVG = computedStyle->StyleSVG(); bool checkedDashAndStrokeIsDashed = false; - if (aFlags != eIgnoreStrokeDashing) { + if (!aFlags.contains(StrokeOptionFlag::IgnoreStrokeDashing)) { DashState dashState = GetStrokeDashData(aStrokeOptions, aElement, styleSVG, aContextPaint); diff --git a/dom/svg/SVGContentUtils.h b/dom/svg/SVGContentUtils.h index d8fad2a45a613..4fc4e6cd97767 100644 --- a/dom/svg/SVGContentUtils.h +++ b/dom/svg/SVGContentUtils.h @@ -11,6 +11,7 @@ #include #include "gfx2DGlue.h" +#include "mozilla/EnumSet.h" #include "mozilla/gfx/2D.h" // for StrokeOptions #include "mozilla/gfx/Matrix.h" #include "nsDependentSubstring.h" @@ -102,7 +103,9 @@ class SVGContentUtils { Float mSmallArray[16]; }; - enum StrokeOptionFlags { eAllStrokeOptions, eIgnoreStrokeDashing }; + enum class StrokeOptionFlag { IgnoreStrokeDashing }; + using StrokeOptionFlags = EnumSet; + /** * Note: the linecap style returned in aStrokeOptions is not valid when * ShapeTypeHasNoCorners(aElement) == true && aFlags == eIgnoreStrokeDashing, @@ -113,7 +116,7 @@ class SVGContentUtils { dom::SVGElement* aElement, const ComputedStyle* aComputedStyle, const SVGContextPaint* aContextPaint, - StrokeOptionFlags aFlags = eAllStrokeOptions); + StrokeOptionFlags aFlags = {}); /** * Returns the current computed value of the CSS property 'stroke-width' for diff --git a/dom/svg/SVGLineElement.cpp b/dom/svg/SVGLineElement.cpp index 503bbef274e86..72193ba209e79 100644 --- a/dom/svg/SVGLineElement.cpp +++ b/dom/svg/SVGLineElement.cpp @@ -43,8 +43,9 @@ void SVGLineElement::MaybeAdjustForZeroLength(float aX1, float aY1, float& aX2, float aY2) { if (aX1 == aX2 && aY1 == aY2) { SVGContentUtils::AutoStrokeOptions strokeOptions; - SVGContentUtils::GetStrokeOptions(&strokeOptions, this, nullptr, nullptr, - SVGContentUtils::eIgnoreStrokeDashing); + SVGContentUtils::GetStrokeOptions( + &strokeOptions, this, nullptr, nullptr, + SVGContentUtils::StrokeOptionFlag::IgnoreStrokeDashing); if (strokeOptions.mLineCap != CapStyle::BUTT) { float tinyLength = diff --git a/dom/svg/SVGSVGElement.cpp b/dom/svg/SVGSVGElement.cpp index 39dd06c77327b..fc44b8ef0dc1f 100644 --- a/dom/svg/SVGSVGElement.cpp +++ b/dom/svg/SVGSVGElement.cpp @@ -490,7 +490,7 @@ void SVGSVGElement::InvalidateTransformNotifyFrame() { // might fail this check if we've failed conditional processing if (ISVGSVGFrame* svgframe = do_QueryFrame(GetPrimaryFrame())) { svgframe->NotifyViewportOrTransformChanged( - ISVGDisplayableFrame::ChangeFlags::TransformChanged); + ISVGDisplayableFrame::ChangeFlag::TransformChanged); } } diff --git a/dom/svg/SVGViewportElement.cpp b/dom/svg/SVGViewportElement.cpp index ceee3c5e583c2..c63baab80ad42 100644 --- a/dom/svg/SVGViewportElement.cpp +++ b/dom/svg/SVGViewportElement.cpp @@ -115,7 +115,8 @@ void SVGViewportElement::UpdateHasChildrenOnlyTransform() { static_cast(this)->IsScaledOrTranslated()); } -void SVGViewportElement::ChildrenOnlyTransformChanged(uint32_t aFlags) { +void SVGViewportElement::ChildrenOnlyTransformChanged( + ChildrenOnlyTransformChangedFlags aFlags) { // Avoid wasteful calls: MOZ_ASSERT(!GetPrimaryFrame()->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY), "Non-display SVG frames don't maintain overflow rects"); @@ -142,7 +143,7 @@ void SVGViewportElement::ChildrenOnlyTransformChanged(uint32_t aFlags) { // is being reflowed we're going to invalidate and repaint its entire area // anyway (which will include our children). if ((changeHint & nsChangeHint_ReconstructFrame) || - !(aFlags & eDuringReflow)) { + !aFlags.contains(ChildrenOnlyTransformChangedFlag::DuringReflow)) { nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0}, changeHint); } } diff --git a/dom/svg/SVGViewportElement.h b/dom/svg/SVGViewportElement.h index 2c04edde6afc8..853b96e787659 100644 --- a/dom/svg/SVGViewportElement.h +++ b/dom/svg/SVGViewportElement.h @@ -82,7 +82,10 @@ class SVGViewportElement : public SVGGraphicsElement { void UpdateHasChildrenOnlyTransform(); - enum ChildrenOnlyTransformChangedFlags { eDuringReflow = 1 }; + enum class ChildrenOnlyTransformChangedFlag { DuringReflow }; + + using ChildrenOnlyTransformChangedFlags = + EnumSet; /** * This method notifies the style system that the overflow rects of our @@ -94,7 +97,8 @@ class SVGViewportElement : public SVGGraphicsElement { * GetAttributeChangeHint is because we need to act on non-attribute (e.g. * currentScale) changes in addition to attribute (e.g. viewBox) changes. */ - void ChildrenOnlyTransformChanged(uint32_t aFlags = 0); + void ChildrenOnlyTransformChanged( + ChildrenOnlyTransformChangedFlags aFlags = {}); gfx::Matrix GetViewBoxTransform() const; diff --git a/dom/webauthn/AndroidWebAuthnService.cpp b/dom/webauthn/AndroidWebAuthnService.cpp index 74f8d5b6ccf4b..6ce361a4011e3 100644 --- a/dom/webauthn/AndroidWebAuthnService.cpp +++ b/dom/webauthn/AndroidWebAuthnService.cpp @@ -168,9 +168,13 @@ AndroidWebAuthnService::MakeCredential(uint64_t aTransactionId, nsString userVerification; (void)aArgs->GetUserVerification(userVerification); if (userVerification.EqualsLiteral( - MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) { - GECKOBUNDLE_PUT(authSelBundle, "requireUserVerification", - java::sdk::Integer::ValueOf(1)); + MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED) || + userVerification.EqualsLiteral( + MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED) || + userVerification.EqualsLiteral( + MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED)) { + GECKOBUNDLE_PUT(authSelBundle, "userVerification", + jni::StringParam(userVerification)); } nsString authenticatorAttachment; diff --git a/dom/webidl/Event.webidl b/dom/webidl/Event.webidl index 24eb456fa57b5..ecd3757d49eed 100644 --- a/dom/webidl/Event.webidl +++ b/dom/webidl/Event.webidl @@ -69,6 +69,7 @@ partial interface Event { const long META_MASK = 0x00000008; /** The original target of the event, before any retargetings. */ + [NeedsCallerType] readonly attribute EventTarget? originalTarget; /** * The explicit original target of the event. If the event was retargeted diff --git a/dom/workers/test/marionette/test_service_workers_disabled.py b/dom/workers/test/marionette/test_service_workers_disabled.py index 431392e381f1b..65a680823d8cf 100644 --- a/dom/workers/test/marionette/test_service_workers_disabled.py +++ b/dom/workers/test/marionette/test_service_workers_disabled.py @@ -23,11 +23,9 @@ def tearDown(self): def test_service_workers_disabled_at_startup(self): # self.marionette.set_pref sets preferences after startup. Using it # here causes intermittent failures. - self.marionette.instance.profile.set_preferences( - { - "dom.serviceWorkers.enabled": False, - } - ) + self.marionette.instance.profile.set_preferences({ + "dom.serviceWorkers.enabled": False, + }) self.marionette.restart() diff --git a/gfx/angle/checkout/include/GLSLANG/ShaderVars.h b/gfx/angle/checkout/include/GLSLANG/ShaderVars.h index 4b76d33af2fcf..4db1902d1b747 100644 --- a/gfx/angle/checkout/include/GLSLANG/ShaderVars.h +++ b/gfx/angle/checkout/include/GLSLANG/ShaderVars.h @@ -12,6 +12,7 @@ #include #include +#include #include #include diff --git a/gfx/config/gfxVars.h b/gfx/config/gfxVars.h index f38accac3a90e..bf086c41c8a6b 100644 --- a/gfx/config/gfxVars.h +++ b/gfx/config/gfxVars.h @@ -119,7 +119,8 @@ class MOZ_STACK_CLASS gfxVarsCollectUpdates; _(DMABufModifiersNV12, ArrayOfuint64_t, nsTArray()) \ _(AllowGLNorm16Textures, bool, false) \ _(WebRenderLayerCompositorDCompTexture, bool, false) \ - _(WebRenderOverlayHDR, bool, false) + _(WebRenderOverlayHDR, bool, false) \ + _(UseWebRenderDCompositionTextureOverlayWin, bool, false) /* Add new entries above this line. */ diff --git a/gfx/gl/GLConsts.py b/gfx/gl/GLConsts.py index 0c9a1949bbbbe..feee526f34fef 100755 --- a/gfx/gl/GLConsts.py +++ b/gfx/gl/GLConsts.py @@ -50,17 +50,13 @@ * * To generate this file, see tutorial in \'GLConsts.py\'. */ -"""[ - 1: -] +"""[1:] FOOTER = b""" #endif // GLCONSTS_H_ // clang-format on -"""[ - 1: -] +"""[1:] # - diff --git a/gfx/ipc/GPUProcessManager.cpp b/gfx/ipc/GPUProcessManager.cpp index 9e0afe7a948c4..11d3cdf5a38f5 100644 --- a/gfx/ipc/GPUProcessManager.cpp +++ b/gfx/ipc/GPUProcessManager.cpp @@ -796,10 +796,15 @@ void GPUProcessManager::NotifyWebRenderError(wr::WebRenderError aError) { gfxVars::SetUseWebRenderDCompVideoSwOverlayWin(false); return; } + if (aError == wr::WebRenderError::DCOMP_TEXTURE_OVERLAY) { + gfxVars::SetUseWebRenderDCompositionTextureOverlayWin(false); + return; + } #else if (aError == wr::WebRenderError::VIDEO_OVERLAY || aError == wr::WebRenderError::VIDEO_HW_OVERLAY || - aError == wr::WebRenderError::VIDEO_SW_OVERLAY) { + aError == wr::WebRenderError::VIDEO_SW_OVERLAY || + aError == wr::WebRenderError::DCOMP_TEXTURE_OVERLAY) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } diff --git a/gfx/layers/CompositorTypes.h b/gfx/layers/CompositorTypes.h index 9a2adf12bd47d..43226995f1e0d 100644 --- a/gfx/layers/CompositorTypes.h +++ b/gfx/layers/CompositorTypes.h @@ -98,9 +98,11 @@ enum class TextureFlags : uint32_t { SOFTWARE_DECODED_VIDEO = 1 << 23, // Whether the remote texture must wait for its owner to be created. WAIT_FOR_REMOTE_TEXTURE_OWNER = 1 << 24, + // Buffer is allocated by buffer provider like Canvas2D + ALLOC_BY_BUFFER_PROVIDER = 1 << 25, // OR union of all valid bits - ALL_BITS = (1 << 25) - 1, + ALL_BITS = (1 << 26) - 1, // the default flags DEFAULT = NO_FLAGS }; diff --git a/gfx/layers/ShareableCanvasRenderer.cpp b/gfx/layers/ShareableCanvasRenderer.cpp index e9ac137408151..72a31feb90dab 100644 --- a/gfx/layers/ShareableCanvasRenderer.cpp +++ b/gfx/layers/ShareableCanvasRenderer.cpp @@ -116,6 +116,9 @@ void ShareableCanvasRenderer::UpdateCompositableClient() { if (IsOpaque()) { flags |= TextureFlags::IS_OPAQUE; } + if (provider) { + flags |= TextureFlags::ALLOC_BY_BUFFER_PROVIDER; + } // - diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index 8d3de1e3a164e..18e4f1b9de075 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -1312,6 +1312,15 @@ bool DXGITextureHostD3D11::SupportsExternalCompositing( return true; } } + + bool useDcompTextureOverlay = + wr::RenderDXGITextureHost::UseDCompositionTextureOverlay(GetFormat()) && + mFencesHolderId.isSome() && + !(mFlags & TextureFlags::ALLOC_BY_BUFFER_PROVIDER); + if (useDcompTextureOverlay) { + return true; + } + return false; } diff --git a/gfx/layers/wr/AsyncImagePipelineManager.cpp b/gfx/layers/wr/AsyncImagePipelineManager.cpp index 317db72307451..d37e5404a927c 100644 --- a/gfx/layers/wr/AsyncImagePipelineManager.cpp +++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp @@ -59,6 +59,8 @@ AsyncImagePipelineManager::AsyncImagePipelineManager( gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin()), mUseWebRenderDCompVideoSwOverlayWin( gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin()), + mUseWebRenderDCompositionTextureOverlayWin( + gfx::gfxVars::UseWebRenderDCompositionTextureOverlayWin()), #endif mRenderSubmittedUpdatesLock("SubmittedUpdatesLock"), mLastCompletedFrameId(0) { @@ -370,20 +372,24 @@ void AsyncImagePipelineManager::ApplyAsyncImagesOfImageBridge( } #ifdef XP_WIN - // UseWebRenderDCompVideoHwOverlayWin() and - // UseWebRenderDCompVideoSwOverlayWin() could be changed from true to false, - // when DCompVideoOverlay task is failed. In this case, DisplayItems need to - // be re-pushed to WebRender for disabling video overlay. + // UseWebRenderDCompVideoHwOverlayWin(), UseWebRenderDCompVideoSwOverlayWin() + // and gfxVars::UseWebRenderDCompositionTextureOverlayWin() could be changed + // from true to false, when overlay task is failed. In this case, DisplayItems + // need to be re-pushed to WebRender for disabling video overlay. bool isChanged = (mUseWebRenderDCompVideoHwOverlayWin != gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin()) || (mUseWebRenderDCompVideoSwOverlayWin != - gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin()); + gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin()) || + (mUseWebRenderDCompositionTextureOverlayWin != + gfx::gfxVars::UseWebRenderDCompositionTextureOverlayWin()); if (isChanged) { mUseWebRenderDCompVideoHwOverlayWin = gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin(); mUseWebRenderDCompVideoSwOverlayWin = gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin(); + mUseWebRenderDCompositionTextureOverlayWin = + gfx::gfxVars::UseWebRenderDCompositionTextureOverlayWin(); } #endif diff --git a/gfx/layers/wr/AsyncImagePipelineManager.h b/gfx/layers/wr/AsyncImagePipelineManager.h index b3a188b432b14..7c2b56beaad87 100644 --- a/gfx/layers/wr/AsyncImagePipelineManager.h +++ b/gfx/layers/wr/AsyncImagePipelineManager.h @@ -260,6 +260,7 @@ class AsyncImagePipelineManager final { #ifdef XP_WIN bool mUseWebRenderDCompVideoHwOverlayWin; bool mUseWebRenderDCompVideoSwOverlayWin; + bool mUseWebRenderDCompositionTextureOverlayWin; #endif // Render time for the current composition. diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index a843d42fa4282..0db9d1dd428f9 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -2875,13 +2875,16 @@ void gfxPlatform::InitWebRenderConfig() { } } -# ifdef XP_WIN if (StaticPrefs:: gfx_webrender_layer_compositor_use_dcomp_texture_AtStartup() && IsWin1122H2OrLater() && gfxVars::UseWebRenderDCompWin()) { gfxVars::SetWebRenderLayerCompositorDCompTexture(true); } -# endif + + if (StaticPrefs::gfx_webrender_dcomp_texture_overlay_win_AtStartup() && + IsWin1122H2OrLater() && gfxVars::UseWebRenderDCompWin()) { + gfxVars::SetUseWebRenderDCompositionTextureOverlayWin(true); + } #endif bool allowOverlayVpAutoHDR = false; diff --git a/gfx/webrender_bindings/DCLayerTree.cpp b/gfx/webrender_bindings/DCLayerTree.cpp index e9fbe59c93290..d771992b03f59 100644 --- a/gfx/webrender_bindings/DCLayerTree.cpp +++ b/gfx/webrender_bindings/DCLayerTree.cpp @@ -849,7 +849,7 @@ void DCLayerTree::CreateSwapChainSurface(wr::NativeSurfaceId aId, MOZ_RELEASE_ASSERT(it == mDCSurfaces.end()); UniquePtr surface; - if (SupportsDCompositionTexture()) { + if (UseDCLayerDCompositionTexture()) { surface = MakeUnique(aSize, aIsOpaque, this); if (!surface->Initialize()) { gfxCriticalNote << "Failed to initialize DCLayerDCompositionTexture: " @@ -962,11 +962,25 @@ DCSurface* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage( RenderTextureHost* texture = RenderThread::Get()->GetRenderTexture(aExternalImage); if (texture && texture->AsRenderDXGITextureHost()) { - mSurface.reset(new DCSurfaceVideo(mIsOpaque, mDCLayerTree)); - if (!mSurface->Initialize()) { - gfxCriticalNote << "Failed to initialize DCSurfaceVideo: " - << wr::AsUint64(aExternalImage); - mSurface = nullptr; + auto format = texture->GetFormat(); + if (format == gfx::SurfaceFormat::B8G8R8A8 || + format == gfx::SurfaceFormat::B8G8R8X8) { + MOZ_ASSERT(RenderDXGITextureHost::UseDCompositionTextureOverlay(format)); + mSurface.reset( + new DCSurfaceDCompositionTextureOverlay(mIsOpaque, mDCLayerTree)); + if (!mSurface->Initialize()) { + gfxCriticalNote + << "Failed to initialize DCSurfaceDCompositionTextureOverlay: " + << wr::AsUint64(aExternalImage); + mSurface = nullptr; + } + } else { + mSurface.reset(new DCSurfaceVideo(mIsOpaque, mDCLayerTree)); + if (!mSurface->Initialize()) { + gfxCriticalNote << "Failed to initialize DCSurfaceVideo: " + << wr::AsUint64(aExternalImage); + mSurface = nullptr; + } } } else if (texture && texture->AsRenderDcompSurfaceTextureHost()) { mSurface.reset(new DCSurfaceHandle(mIsOpaque, mDCLayerTree)); @@ -1098,6 +1112,9 @@ void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix& aTransform) { } } else if (auto* surface = mSurface->AsDCSurfaceHandle()) { surface->PresentSurfaceHandle(); + } else if (auto* surface = + mSurface->AsDCSurfaceDCompositionTextureOverlay()) { + surface->Present(); } } @@ -1321,7 +1338,7 @@ bool DCLayerTree::SupportsSwapChainTearing() { return supported; } -bool DCLayerTree::SupportsDCompositionTexture() { +bool DCLayerTree::UseDCLayerDCompositionTexture() { if (!gfx::gfxVars::WebRenderLayerCompositorDCompTexture()) { return false; } @@ -2160,6 +2177,53 @@ void DCLayerCompositionSurface::Present(const wr::DeviceIntRect* aDirtyRects, mEGLSurface = EGL_NO_SURFACE; } +DCSurfaceDCompositionTextureOverlay::DCSurfaceDCompositionTextureOverlay( + bool aIsOpaque, DCLayerTree* aDCLayerTree) + : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque, + aDCLayerTree) {} + +DCSurfaceDCompositionTextureOverlay::~DCSurfaceDCompositionTextureOverlay() {} + +void DCSurfaceDCompositionTextureOverlay::AttachExternalImage( + wr::ExternalImageId aExternalImage) { + auto* texture = RenderThread::Get()->GetRenderTexture(aExternalImage); + if (!texture) { + return; + } + mRenderTextureHost = texture; +} + +void DCSurfaceDCompositionTextureOverlay::Present() { + if (!mRenderTextureHost) { + return; + } + + // Content is not updated + if (mPrevRenderTextureHost == mRenderTextureHost) { + return; + } + + const auto textureHost = mRenderTextureHost->AsRenderDXGITextureHost(); + RefPtr dcompTexture = + textureHost->GetDCompositionTexture(); + if (!dcompTexture) { + gfxCriticalNote << "Failed to get DCompTexture"; + RenderThread::Get()->NotifyWebRenderError( + WebRenderError::DCOMP_TEXTURE_OVERLAY); + return; + } + + const auto alphaMode = + mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; + dcompTexture->SetAlphaMode(alphaMode); + // XXX + // dcompTexture->SetColorSpace(); + + mContentVisual->SetContent(dcompTexture); + mPrevRenderTextureHost = mRenderTextureHost; + mDCLayerTree->SetPendingCommet(); +} + DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree) : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque, aDCLayerTree), diff --git a/gfx/webrender_bindings/DCLayerTree.h b/gfx/webrender_bindings/DCLayerTree.h index 88e1dc214afa3..1f847e9faa3c0 100644 --- a/gfx/webrender_bindings/DCLayerTree.h +++ b/gfx/webrender_bindings/DCLayerTree.h @@ -67,6 +67,7 @@ class DCTile; class DCLayerDCompositionTexture; class DCSurface; class DCSwapChain; +class DCSurfaceDCompositionTextureOverlay; class DCSurfaceVideo; class DCSurfaceHandle; class RenderTextureHost; @@ -201,12 +202,14 @@ class DCLayerTree { DXGI_FORMAT GetOverlayFormatForSDR(); bool SupportsSwapChainTearing(); - bool SupportsDCompositionTexture(); + bool UseDCLayerDCompositionTexture(); void SetUsedOverlayTypeInFrame(DCompOverlayTypes aTypes); int GetFrameId() { return mCurrentFrame; } + void SetPendingCommet() { mPendingCommit = true; } + protected: bool Initialize(HWND aHwnd, nsACString& aError); bool InitializeVideoOverlaySupport(); @@ -362,6 +365,10 @@ class DCSurface { virtual DCLayerDCompositionTexture* AsDCLayerDCompositionTexture() { return nullptr; } + virtual DCSurfaceDCompositionTextureOverlay* + AsDCSurfaceDCompositionTextureOverlay() { + return nullptr; + } bool IsUpdated(const wr::CompositorSurfaceTransform& aTransform, const wr::DeviceIntRect& aClipRect, @@ -553,6 +560,12 @@ class DCExternalSurfaceWrapper : public DCSurface { return mSurface ? mSurface->AsDCSurfaceHandle() : nullptr; } + DCSurfaceDCompositionTextureOverlay* AsDCSurfaceDCompositionTextureOverlay() + override { + return mSurface ? mSurface->AsDCSurfaceDCompositionTextureOverlay() + : nullptr; + } + private: DCSurface* EnsureSurfaceForExternalImage(wr::ExternalImageId aExternalImage); @@ -561,6 +574,26 @@ class DCExternalSurfaceWrapper : public DCSurface { Maybe mCManageChain; }; +class DCSurfaceDCompositionTextureOverlay : public DCSurface { + public: + DCSurfaceDCompositionTextureOverlay(bool aIsOpaque, + DCLayerTree* aDCLayerTree); + + void AttachExternalImage(wr::ExternalImageId aExternalImage) override; + void Present(); + + DCSurfaceDCompositionTextureOverlay* AsDCSurfaceDCompositionTextureOverlay() + override { + return this; + } + + protected: + virtual ~DCSurfaceDCompositionTextureOverlay(); + + RefPtr mRenderTextureHost; + RefPtr mPrevRenderTextureHost; +}; + class DCSurfaceVideo : public DCSurface { public: DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree); diff --git a/gfx/webrender_bindings/RenderCompositorANGLE.cpp b/gfx/webrender_bindings/RenderCompositorANGLE.cpp index 7cc225a3de84e..e5c3f4dd159e0 100644 --- a/gfx/webrender_bindings/RenderCompositorANGLE.cpp +++ b/gfx/webrender_bindings/RenderCompositorANGLE.cpp @@ -1008,7 +1008,7 @@ bool RenderCompositorANGLE::UsePartialPresent() { return mUsePartialPresent; } bool RenderCompositorANGLE::RequestFullRender() { // XXX Remove when partial update is supported. - if (UseLayerCompositor() && mDCLayerTree->SupportsDCompositionTexture()) { + if (UseLayerCompositor() && mDCLayerTree->UseDCLayerDCompositionTexture()) { return true; } return mFullRender; diff --git a/gfx/webrender_bindings/RenderD3D11TextureHost.cpp b/gfx/webrender_bindings/RenderD3D11TextureHost.cpp index 8de82eb77c7be..3d8a6995c6c56 100644 --- a/gfx/webrender_bindings/RenderD3D11TextureHost.cpp +++ b/gfx/webrender_bindings/RenderD3D11TextureHost.cpp @@ -6,6 +6,8 @@ #include "RenderD3D11TextureHost.h" +#include + #include "GLContextEGL.h" #include "GLLibraryEGL.h" #include "RenderThread.h" @@ -14,6 +16,7 @@ #include "ScopedGLHelpers.h" #include "mozilla/gfx/CanvasManagerParent.h" #include "mozilla/gfx/DeviceManagerDx.h" +#include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/Logging.h" #include "mozilla/layers/FenceD3D11.h" #include "mozilla/layers/GpuProcessD3D11TextureMap.h" @@ -56,6 +59,76 @@ RenderDXGITextureHost::~RenderDXGITextureHost() { DeleteTextureHandle(); } +/* static */ +bool RenderDXGITextureHost::UseDCompositionTextureOverlay( + gfx::SurfaceFormat aFormat) { + if (!gfx::gfxVars::UseWebRenderDCompositionTextureOverlayWin()) { + return false; + } + + if (!gfx::DeviceManagerDx::Get()->CanUseDCompositionTexture()) { + return false; + } + + if (aFormat != gfx::SurfaceFormat::B8G8R8A8 && + aFormat != gfx::SurfaceFormat::B8G8R8X8) { + return false; + } + + return true; +} + +IDCompositionTexture* RenderDXGITextureHost::GetDCompositionTexture() { + if (mDCompositionTexture) { + return mDCompositionTexture; + } + + if (!UseDCompositionTextureOverlay(mFormat)) { + return nullptr; + } + + if (mFencesHolderId.isNothing()) { + MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); + return nullptr; + } + + HRESULT hr; + RefPtr dcomp = + gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice(); + if (!dcomp) { + MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); + gfxCriticalNoteOnce << "Failed to get DirectCompositionDevice"; + return nullptr; + } + + RefPtr dcomp4; + hr = dcomp->QueryInterface((IDCompositionDevice4**)getter_AddRefs(dcomp4)); + if (FAILED(hr)) { + MOZ_ASSERT_UNREACHABLE("Unexpected to be called!"); + gfxCriticalNoteOnce << "Failed to get DCompositionDevice4"; + return nullptr; + } + + RefPtr texture2D = GetD3D11Texture2DWithGL(); + if (!texture2D) { + gfxCriticalNoteOnce << "Failed to get D3D11Texture"; + return nullptr; + } + + RefPtr dcompTexture; + hr = + dcomp4->CreateCompositionTexture(texture2D, getter_AddRefs(dcompTexture)); + if (FAILED(hr)) { + gfxCriticalNoteOnce << "CreateCompositionTexture failed: " + << gfx::hexa(hr); + return nullptr; + } + + mDCompositionTexture = dcompTexture; + + return mDCompositionTexture; +} + ID3D11Texture2D* RenderDXGITextureHost::GetD3D11Texture2DWithGL() { if (mTexture) { return mTexture; diff --git a/gfx/webrender_bindings/RenderD3D11TextureHost.h b/gfx/webrender_bindings/RenderD3D11TextureHost.h index a5839788a3883..08c9b073c3946 100644 --- a/gfx/webrender_bindings/RenderD3D11TextureHost.h +++ b/gfx/webrender_bindings/RenderD3D11TextureHost.h @@ -14,6 +14,7 @@ #include "RenderTextureHostSWGL.h" struct ID3D11Texture2D; +struct IDCompositionTexture; struct IDXGIKeyedMutex; namespace mozilla { @@ -34,6 +35,8 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL { const gfx::IntSize aSize, const bool aHasKeyedMutex, const Maybe& aFencesHolderId); + static bool UseDCompositionTextureOverlay(gfx::SurfaceFormat aFormat); + wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override; void Unlock() override; void ClearCachedResources() override; @@ -50,6 +53,8 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL { ID3D11Texture2D* GetD3D11Texture2DWithGL(); ID3D11Texture2D* GetD3D11Texture2D() { return mTexture; } + IDCompositionTexture* GetDCompositionTexture(); + // RenderTextureHostSWGL gfx::SurfaceFormat GetFormat() const override { return mFormat; } gfx::ColorDepth GetColorDepth() const override { @@ -106,6 +111,7 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL { RefPtr mTexture; const uint32_t mArrayIndex; RefPtr mKeyedMutex; + RefPtr mDCompositionTexture; // Temporary state between MapPlane and UnmapPlanes. RefPtr mDeviceContext; diff --git a/gfx/webrender_bindings/WebRenderTypes.h b/gfx/webrender_bindings/WebRenderTypes.h index cff7c12759b94..0432e56a4cba1 100644 --- a/gfx/webrender_bindings/WebRenderTypes.h +++ b/gfx/webrender_bindings/WebRenderTypes.h @@ -805,6 +805,7 @@ enum class WebRenderError : int8_t { VIDEO_OVERLAY, VIDEO_HW_OVERLAY, VIDEO_SW_OVERLAY, + DCOMP_TEXTURE_OVERLAY, EXCESSIVE_RESETS, Sentinel /* this must be last for serialization purposes. */ diff --git a/intl/icu_sources_data.py b/intl/icu_sources_data.py index 5dcc69526fec0..4ef7638400f53 100644 --- a/intl/icu_sources_data.py +++ b/intl/icu_sources_data.py @@ -211,9 +211,7 @@ def try_run(name, command, cwd=None, **kwargs): except subprocess.CalledProcessError: print( """Error running "{}" in directory {} - See output in {}""".format( - " ".join(command), cwd, f.name - ), + See output in {}""".format(" ".join(command), cwd, f.name), file=sys.stderr, ) return False @@ -232,21 +230,19 @@ def update_data_file(topsrcdir): configure = mozpath.join(topsrcdir, "intl/icu/source/configure") env = dict(os.environ) # bug 1262101 - these should be shared with the moz.build files - env.update( - { - "CPPFLAGS": ( - "-DU_NO_DEFAULT_INCLUDE_UTF_HEADERS=1 " - + "-DU_HIDE_OBSOLETE_UTF_OLD_H=1 " - + "-DUCONFIG_NO_LEGACY_CONVERSION " - + "-DUCONFIG_NO_TRANSLITERATION " - + "-DUCONFIG_NO_REGULAR_EXPRESSIONS " - + "-DUCONFIG_NO_BREAK_ITERATION " - + "-DUCONFIG_NO_IDNA " - + "-DUCONFIG_NO_MF2 " - + "-DU_CHARSET_IS_UTF8 " - ) - } - ) + env.update({ + "CPPFLAGS": ( + "-DU_NO_DEFAULT_INCLUDE_UTF_HEADERS=1 " + + "-DU_HIDE_OBSOLETE_UTF_OLD_H=1 " + + "-DUCONFIG_NO_LEGACY_CONVERSION " + + "-DUCONFIG_NO_TRANSLITERATION " + + "-DUCONFIG_NO_REGULAR_EXPRESSIONS " + + "-DUCONFIG_NO_BREAK_ITERATION " + + "-DUCONFIG_NO_IDNA " + + "-DUCONFIG_NO_MF2 " + + "-DU_CHARSET_IS_UTF8 " + ) + }) # Exclude data that we currently don't need. # diff --git a/ipc/ipdl/ipdl.py b/ipc/ipdl/ipdl.py index 15112b34041df..2e02d0a6022d7 100644 --- a/ipc/ipdl/ipdl.py +++ b/ipc/ipdl/ipdl.py @@ -31,7 +31,7 @@ def __init__( allsyncmessages, alljsonobjs, *, - processes=None + processes=None, ): if processes is None: processes = os.cpu_count() or 1 diff --git a/ipc/pull-chromium.py b/ipc/pull-chromium.py index 688098b511bc5..17a205df18fc1 100644 --- a/ipc/pull-chromium.py +++ b/ipc/pull-chromium.py @@ -36,16 +36,14 @@ def doexport(svnpath): localpath = os.path.join(chromiumsrc, svnpath) os.makedirs(os.path.dirname(localpath)) - check_call( - [ - "svn", - "export", - "-r", - "BASE", - os.path.join(chromiumtree, "src", svnpath), - localpath, - ] - ) + check_call([ + "svn", + "export", + "-r", + "BASE", + os.path.join(chromiumtree, "src", svnpath), + localpath, + ]) doexport("base") diff --git a/js/moz.configure b/js/moz.configure index a69ae114f59b0..3be771b9fcd80 100644 --- a/js/moz.configure +++ b/js/moz.configure @@ -1083,24 +1083,6 @@ set_config("ENABLE_WASM_CUSTOM_PAGE_SIZES", wasm_custom_page_sizes) set_define("ENABLE_WASM_CUSTOM_PAGE_SIZES", wasm_custom_page_sizes) -# Enable static checking using sixgill -# ==================================== - -option("--with-sixgill", nargs=1, help="Enable static checking of code using sixgill") - - -@depends_if("--with-sixgill") -@imports("os") -def sixgill(value): - for f in ("bin/xdbfind", "gcc/xgill.so", "scripts/wrap_gcc/g++"): - if not os.path.exists(os.path.join(value[0], f)): - die("The sixgill plugin and binaries are not at the specified path") - return value[0] - - -set_config("SIXGILL_PATH", sixgill) - - # Support for readline # ===================================================== diff --git a/js/src/builtin/embedjs.py b/js/src/builtin/embedjs.py index 989b6cfbff45e..517028dc7ec1b 100644 --- a/js/src/builtin/embedjs.py +++ b/js/src/builtin/embedjs.py @@ -101,13 +101,11 @@ def embed( ) args = cppflags + ["-D%(k)s=%(v)s" % {"k": k, "v": env[k]} for k in env] preprocessed = preprocess(cxx, preprocessorOption, combinedSources, args) - processed = "\n".join( - [ - line - for line in preprocessed.splitlines() - if (line.strip() and not line.startswith("#")) - ] - ) + processed = "\n".join([ + line + for line in preprocessed.splitlines() + if (line.strip() and not line.startswith("#")) + ]) js_out.write(processed) import zlib diff --git a/js/src/builtin/intl/Locale.cpp b/js/src/builtin/intl/Locale.cpp index 2428289b228f7..eefb1c5bfd044 100644 --- a/js/src/builtin/intl/Locale.cpp +++ b/js/src/builtin/intl/Locale.cpp @@ -141,9 +141,9 @@ static LocaleObject* CreateLocaleObject(JSContext* cx, HandleObject prototype, return nullptr; } - locale->setFixedSlot(LocaleObject::LANGUAGE_TAG_SLOT, StringValue(tagStr)); - locale->setFixedSlot(LocaleObject::BASENAME_SLOT, StringValue(baseName)); - locale->setFixedSlot(LocaleObject::UNICODE_EXTENSION_SLOT, unicodeExtension); + locale->initFixedSlot(LocaleObject::LANGUAGE_TAG_SLOT, StringValue(tagStr)); + locale->initFixedSlot(LocaleObject::BASENAME_SLOT, StringValue(baseName)); + locale->initFixedSlot(LocaleObject::UNICODE_EXTENSION_SLOT, unicodeExtension); return locale; } diff --git a/js/src/builtin/intl/make_intl_data.py b/js/src/builtin/intl/make_intl_data.py index e4401bc0ab330..511d0a2c4f6c8 100755 --- a/js/src/builtin/intl/make_intl_data.py +++ b/js/src/builtin/intl/make_intl_data.py @@ -4,40 +4,40 @@ # 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/. -""" Usage: - make_intl_data.py langtags [cldr_common.zip] - make_intl_data.py tzdata - make_intl_data.py currency - make_intl_data.py units - make_intl_data.py numbering +"""Usage: +make_intl_data.py langtags [cldr_common.zip] +make_intl_data.py tzdata +make_intl_data.py currency +make_intl_data.py units +make_intl_data.py numbering - Target "langtags": - This script extracts information about 1) mappings between deprecated and - current Unicode BCP 47 locale identifiers, and 2) deprecated and current - BCP 47 Unicode extension value from CLDR, and converts it to C++ mapping - code in intl/components/LocaleGenerated.cpp. The code is used in - intl/components/Locale.cpp. +Target "langtags": +This script extracts information about 1) mappings between deprecated and +current Unicode BCP 47 locale identifiers, and 2) deprecated and current +BCP 47 Unicode extension value from CLDR, and converts it to C++ mapping +code in intl/components/LocaleGenerated.cpp. The code is used in +intl/components/Locale.cpp. - Target "tzdata": - This script computes which time zone informations are not up-to-date in ICU - and provides the necessary mappings to workaround this problem. - https://ssl.icu-project.org/trac/ticket/12044 +Target "tzdata": +This script computes which time zone informations are not up-to-date in ICU +and provides the necessary mappings to workaround this problem. +https://ssl.icu-project.org/trac/ticket/12044 - Target "currency": - Generates the mapping from currency codes to decimal digits used for them. +Target "currency": +Generates the mapping from currency codes to decimal digits used for them. - Target "units": - Generate source and test files using the list of so-called "sanctioned unit - identifiers" and verifies that the ICU data filter includes these units. +Target "units": +Generate source and test files using the list of so-called "sanctioned unit +identifiers" and verifies that the ICU data filter includes these units. - Target "numbering": - Generate source and test files using the list of numbering systems with - simple digit mappings and verifies that it's in sync with ICU/CLDR. +Target "numbering": +Generate source and test files using the list of numbering systems with +simple digit mappings and verifies that it's in sync with ICU/CLDR. """ import io @@ -121,9 +121,7 @@ def writeMappingsBinarySearch( println( """ -}""".lstrip( - "\n" - ) +}""".lstrip("\n") ) @@ -157,18 +155,14 @@ def write_array(subtags, name, length, fixed): println( f""" if ({source_name}.Length() == {length}) {{ -""".rstrip( - "\n" - ) +""".rstrip("\n") ) else: trailing_return = False println( """ { -""".rstrip( - "\n" - ) +""".rstrip("\n") ) # The subtags need to be sorted for binary search to work. @@ -187,17 +181,13 @@ def equals(subtag): return true; }} return false; -""".strip( - "\n" - ) +""".strip("\n") ) else: println( f""" return {equals(subtags[0])}; -""".strip( - "\n" - ) +""".strip("\n") ) elif len(subtags) <= 4: if type(mappings) is dict: @@ -208,17 +198,13 @@ def equals(subtag): {target_name}.Set("{mappings[subtag]}"); return true; }} -""".strip( - "\n" - ) +""".strip("\n") ) println( """ return false; -""".strip( - "\n" - ) +""".strip("\n") ) else: cond = (equals(subtag) for subtag in subtags) @@ -226,9 +212,7 @@ def equals(subtag): println( f""" return {cond}; -""".strip( - "\n" - ) +""".strip("\n") ) else: write_array(subtags, source_name + "s", length, True) @@ -255,9 +239,7 @@ def equals(subtag): println( """ } -""".strip( - "\n" - ) +""".strip("\n") ) if trailing_return: @@ -310,16 +292,12 @@ def writeComplexLanguageTagMappings( println( f""" - {if_kind} ({cond}) {{""".strip( - "\n" - ) + {if_kind} ({cond}) {{""".strip("\n") ) println( f""" - SetLanguage("{language}");""".strip( - "\n" - ) + SetLanguage("{language}");""".strip("\n") ) if script is not None: @@ -327,32 +305,24 @@ def writeComplexLanguageTagMappings( f""" if (Script().Missing()) {{ SetScript("{script}"); - }}""".strip( - "\n" - ) + }}""".strip("\n") ) if region is not None: println( f""" if (Region().Missing()) {{ SetRegion("{region}"); - }}""".strip( - "\n" - ) + }}""".strip("\n") ) println( """ - }""".strip( - "\n" - ) + }""".strip("\n") ) println( """ } -""".strip( - "\n" - ) +""".strip("\n") ) @@ -406,14 +376,12 @@ def hash_key(default, non_default_replacements): println( f""" - {if_kind} ({cond}) {{""".strip( - "\n" - ) + {if_kind} ({cond}) {{""".strip("\n") ) - replacement_regions = sorted( - {region for (_, _, region) in non_default_replacements} - ) + replacement_regions = sorted({ + region for (_, _, region) in non_default_replacements + }) first_case = True for replacement_region in replacement_regions: @@ -441,9 +409,7 @@ def compare_tags(language, script): f""" {if_kind} ({cond}) {{ SetRegion("{replacement_region}"); - }}""".rstrip().strip( - "\n" - ) + }}""".rstrip().strip("\n") ) println( @@ -451,17 +417,13 @@ def compare_tags(language, script): else {{ SetRegion("{default}"); }} - }}""".rstrip().strip( - "\n" - ) + }}""".rstrip().strip("\n") ) println( """ } -""".strip( - "\n" - ) +""".strip("\n") ) @@ -531,9 +493,7 @@ def writeVariantTagMappings(println, variant_mappings, description, source, url) if ({no_replacements}) {{ removeVariantAt(i); }} -""".strip( - "\n" - ) +""".strip("\n") ) for deprecated_variant, (type, replacement) in sorted( @@ -543,26 +503,20 @@ def writeVariantTagMappings(println, variant_mappings, description, source, url) f""" else if (variant.Span() == mozilla::MakeStringSpan("{deprecated_variant}")) {{ removeVariantAt(i); -""".strip( - "\n" - ) +""".strip("\n") ) if type == "language": println( f""" SetLanguage("{replacement}"); -""".strip( - "\n" - ) +""".strip("\n") ) elif type == "region": println( f""" SetRegion("{replacement}"); -""".strip( - "\n" - ) +""".strip("\n") ) else: assert type == "variant" @@ -571,17 +525,13 @@ def writeVariantTagMappings(println, variant_mappings, description, source, url) if (!insertVariantSortedIfNotPresent(mozilla::MakeStringSpan("{replacement}"))) {{ return false; }} -""".strip( - "\n" - ) +""".strip("\n") ) println( """ } -""".strip( - "\n" - ) +""".strip("\n") ) println( @@ -592,9 +542,7 @@ def writeVariantTagMappings(println, variant_mappings, description, source, url) } return true; } -""".strip( - "\n" - ) +""".strip("\n") ) @@ -732,9 +680,7 @@ def __eq__(self, obj): mRegion.Set(mozilla::MakeStringSpan("")); } } -""".rstrip().lstrip( - "\n" - ) +""".rstrip().lstrip("\n") ) # Finally handle all remaining cases. @@ -784,9 +730,7 @@ def hash_key(mappings): println( f""" else if ({cond}) {{ -""".rstrip().lstrip( - "\n" - ) +""".rstrip().lstrip("\n") ) mappings = legacy_mappings_by_language[langs[0]] @@ -820,9 +764,7 @@ def variant_size(m): println( f""" {" " * i}{maybe_else}if (auto* {variant} = findVariant(mozilla::MakeStringSpan("{variant}"))) {{ -""".rstrip().lstrip( - "\n" - ) +""".rstrip().lstrip("\n") ) indent = " " * len_variants @@ -832,26 +774,20 @@ def variant_size(m): {indent}removeVariant{"s" if len_variants > 1 else ""}({", ".join(sorted_variants)}); {indent}SetLanguage("{r_language}"); {indent}{"return true;" if not chain_if else ""} -""".rstrip().lstrip( - "\n" - ) +""".rstrip().lstrip("\n") ) for i in range(len_variants, 0, -1): println( f""" {" " * (i - 1)}}} -""".rstrip().lstrip( - "\n" - ) +""".rstrip().lstrip("\n") ) println( """ } -""".rstrip().lstrip( - "\n" - ) +""".rstrip().lstrip("\n") ) println( @@ -962,9 +898,9 @@ def bcp47_canonical(language, script, region, variants): # See UTS35, §Annex C, Definitions - 1. Multimap interpretation. def language_id_to_multimap(language_id): match = re_unicode_language_id.match(language_id) - assert ( - match is not None - ), f"{language_id} invalid Unicode BCP 47 locale identifier" + assert match is not None, ( + f"{language_id} invalid Unicode BCP 47 locale identifier" + ) canonical_language_id = bcp47_canonical( *match.group("language", "script", "region", "variants") @@ -1011,9 +947,9 @@ def language_id_to_multimap(language_id): language_id_to_multimap("und-" + r) for r in replacements ] - assert ( - type not in territory_exception_rules - ), f"Duplicate alias rule: {type}" + assert type not in territory_exception_rules, ( + f"Duplicate alias rule: {type}" + ) territory_exception_rules[type] = replacement_list @@ -1213,9 +1149,9 @@ def multi_map_size(locale_id): assert r_language is not None, "Can't remove a language subtag" # We don't yet support this case. - assert ( - r_variants is None - ), f"Unhandled variant replacement in language alias: {replacement}" + assert r_variants is None, ( + f"Unhandled variant replacement in language alias: {replacement}" + ) if replacement == (Any, None, None, None): language_mappings[language] = r_language @@ -1223,9 +1159,9 @@ def multi_map_size(locale_id): complex_language_mappings[language] = replacement[:-1] elif script is not None: # We don't support removing script subtags. - assert ( - r_script is not None - ), f"Can't remove a script subtag: {replacement}" + assert r_script is not None, ( + f"Can't remove a script subtag: {replacement}" + ) # We only support one-to-one script mappings for now. assert replacement == ( @@ -1238,9 +1174,9 @@ def multi_map_size(locale_id): script_mappings[script] = r_script elif region is not None: # We don't support removing region subtags. - assert ( - r_region is not None - ), f"Can't remove a region subtag: {replacement}" + assert r_region is not None, ( + f"Can't remove a region subtag: {replacement}" + ) # We only support one-to-one region mappings for now. assert replacement == ( @@ -1262,9 +1198,9 @@ def multi_map_size(locale_id): assert len(variants.split("-")) == 1 # We only support one-to-one variant mappings for now. - assert ( - multi_map_size(replacement) <= 1 - ), f"Unhandled replacement in variant alias: {replacement}" + assert multi_map_size(replacement) <= 1, ( + f"Unhandled replacement in variant alias: {replacement}" + ) if r_language is not None: variant_mappings[variants] = ("language", r_language) @@ -1311,21 +1247,21 @@ def multi_map_size(locale_id): for likely_subtag in tree.iterfind(".//likelySubtag"): from_tag = bcp47_id(likely_subtag.get("from")) from_match = re_unicode_language_id.match(from_tag) - assert ( - from_match is not None - ), f"{from_tag} invalid Unicode BCP 47 locale identifier" - assert ( - from_match.group("variants") is None - ), f"unexpected variant subtags in {from_tag}" + assert from_match is not None, ( + f"{from_tag} invalid Unicode BCP 47 locale identifier" + ) + assert from_match.group("variants") is None, ( + f"unexpected variant subtags in {from_tag}" + ) to_tag = bcp47_id(likely_subtag.get("to")) to_match = re_unicode_language_id.match(to_tag) - assert ( - to_match is not None - ), f"{to_tag} invalid Unicode BCP 47 locale identifier" - assert ( - to_match.group("variants") is None - ), f"unexpected variant subtags in {to_tag}" + assert to_match is not None, ( + f"{to_tag} invalid Unicode BCP 47 locale identifier" + ) + assert to_match.group("variants") is None, ( + f"unexpected variant subtags in {to_tag}" + ) from_canonical = bcp47_canonical( *from_match.group("language", "script", "region", "variants") @@ -1472,9 +1408,9 @@ def readBCP47File(file): continue # All other names should match the 'type' production. - assert ( - typeRE.match(name) is not None - ), f"{name} matches the 'type' production" + assert typeRE.match(name) is not None, ( + f"{name} matches the 'type' production" + ) # : # @@ -1548,9 +1484,9 @@ def readSupplementalMetadata(file): tree = ET.parse(file) for alias in tree.iterfind(".//subdivisionAlias"): type = alias.get("type") - assert ( - typeRE.match(type) is not None - ), f"{type} matches the 'type' production" + assert typeRE.match(type) is not None, ( + f"{type} matches the 'type' production" + ) # Take the first replacement when multiple ones are present. replacement = alias.get("replacement").split(" ")[0].lower() @@ -1560,9 +1496,9 @@ def readSupplementalMetadata(file): replacement += "zzzz" # Assert the replacement is syntactically correct. - assert ( - typeRE.match(replacement) is not None - ), f"replacement {replacement} matches the 'type' production" + assert typeRE.match(replacement) is not None, ( + f"replacement {replacement} matches the 'type' production" + ) # 'subdivisionAlias' applies to 'rg' and 'sd' keys. mapping["u"].setdefault("rg", {})[type] = replacement @@ -1851,9 +1787,9 @@ def canonical(tag): region = region_mappings[region] else: # Assume no complex region mappings are needed for now. - assert ( - region not in complex_region_mappings - ), f"unexpected region with complex mappings: {region}" + assert region not in complex_region_mappings, ( + f"unexpected region with complex mappings: {region}" + ) return (language, script, region) @@ -3228,9 +3164,7 @@ def writeUnicodeExtensionsMappings(println, mapping, extension): return type.size() == (Length - 1) && memcmp(type.data(), str, Length - 1) == 0; }} -""".rstrip( - "\n" - ) +""".rstrip("\n") ) linear_search_max_length = 4 @@ -3276,9 +3210,7 @@ def writeUnicodeExtensionsMappings(println, mapping, extension): }} return nullptr; }} -""".rstrip( - "\n" - ) +""".rstrip("\n") ) println( @@ -3337,9 +3269,7 @@ def write_array(subtags, name, length): cond = (" ||\n" + " " * (2 + len(if_kind) + 2)).join(cond) println( f""" - {if_kind} ({cond}) {{""".strip( - "\n" - ) + {if_kind} ({cond}) {{""".strip("\n") ) first_key = False @@ -3355,9 +3285,7 @@ def write_array(subtags, name, length): println( f""" return Search{extension}Replacement(types, aliases, type); -""".strip( - "\n" - ) +""".strip("\n") ) else: for type, replacement in replacements: @@ -3365,25 +3293,19 @@ def write_array(subtags, name, length): f""" if (Is{extension}Type(type, "{type}")) {{ return "{replacement}"; - }}""".strip( - "\n" - ) + }}""".strip("\n") ) println( """ - }""".lstrip( - "\n" - ) + }""".lstrip("\n") ) println( """ return nullptr; } -""".strip( - "\n" - ) +""".strip("\n") ) @@ -3626,9 +3548,7 @@ def find_unit_type(unit): } // namespace mozilla::intl #endif -""".strip( - "\n" - ) +""".strip("\n") ) writeUnitTestFiles(all_units, sanctioned_units) @@ -3667,9 +3587,7 @@ def write_test(file_name, test_content, indent=4): println( """ if (typeof reportCompare === "function") -{}reportCompare(true, true);""".format( - " " * indent - ) +{}reportCompare(true, true);""".format(" " * indent) ) write_test( @@ -3999,16 +3917,16 @@ def updateNumberingSystems(topsrcdir, args): # Assert ICU includes support for all required numbering systems. If this assertion fails, # something is broken in ICU. - assert all_numbering_systems_simple_digits.issuperset( - numbering_systems - ), f"{numbering_systems.difference(all_numbering_systems_simple_digits)}" + assert all_numbering_systems_simple_digits.issuperset(numbering_systems), ( + f"{numbering_systems.difference(all_numbering_systems_simple_digits)}" + ) # Assert the spec requires support for all numbering systems with simple digit mappings. If # this assertion fails, file a PR at to include any new # numbering systems. - assert all_numbering_systems_simple_digits.issubset( - numbering_systems - ), f"{all_numbering_systems_simple_digits.difference(numbering_systems)}" + assert all_numbering_systems_simple_digits.issubset(numbering_systems), ( + f"{all_numbering_systems_simple_digits.difference(numbering_systems)}" + ) writeNumberingSystemFiles(all_numbering_systems) @@ -4077,8 +3995,7 @@ def EnsureHttps(v): metavar="URL", default="https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/lists/list-one.xml", # NOQA: E501 type=EnsureHttps, - help="Download url for the currency & funds code list (default: " - "%(default)s)", + help="Download url for the currency & funds code list (default: %(default)s)", ) parser_currency.add_argument( "--out", diff --git a/js/src/devtools/automation/autospider.py b/js/src/devtools/automation/autospider.py index c4fd674aea1a5..38de07bf778e6 100755 --- a/js/src/devtools/automation/autospider.py +++ b/js/src/devtools/automation/autospider.py @@ -30,8 +30,7 @@ def directories(pathmodule, cwd, fixup=lambda s: s): source = pathmodule.abspath(pathmodule.join(js_src, "..", "..")) mozbuild = pathmodule.abspath( # os.path.expanduser does not work on Windows. - env.get("MOZBUILD_STATE_PATH") - or pathmodule.join(Path.home(), ".mozbuild") + env.get("MOZBUILD_STATE_PATH") or pathmodule.join(Path.home(), ".mozbuild") ) fetches = pathmodule.abspath(env.get("MOZ_FETCHES_DIR", mozbuild)) return Dirs(scripts, js_src, source, fetches) @@ -148,8 +147,7 @@ def quote(s): type=str, metavar="TESTSUITE", default="", - help="comma-separated set of test suites to remove from the variant's default " - "set", + help="comma-separated set of test suites to remove from the variant's default set", ) parser.add_argument( "--build-only", @@ -591,12 +589,10 @@ def normalize_tests(tests): "--timeout=300", "--jitflags=all", ] - results.append( - ( - "mach jit-test", - run_mach_command(["jit-test", "--", *auto_args, *extra_args["jit-test"]]), - ) - ) + results.append(( + "mach jit-test", + run_mach_command(["jit-test", "--", *auto_args, *extra_args["jit-test"]]), + )) if "jsapitests" in test_suites: st = run_jsapitests([]) if st == 0: @@ -606,23 +602,17 @@ def normalize_tests(tests): auto_args = [] if AUTOMATION: auto_args = ["--no-progress", "--format=automation", "--timeout=300"] - results.append( - ( - "mach jstests", - run_mach_command(["jstests", "--", *auto_args, *extra_args["jstests"]]), - ) - ) + results.append(( + "mach jstests", + run_mach_command(["jstests", "--", *auto_args, *extra_args["jstests"]]), + )) if "gdb" in test_suites: test_script = os.path.join(DIR.js_src, "gdb", "run-tests.py") auto_args = ["-s", "-o", "--no-progress"] if AUTOMATION else [] - results.append( - ( - "gdb", - run_test_command( - [PYTHON, test_script, *auto_args, *extra_args["gdb"], OBJDIR] - ), - ) - ) + results.append(( + "gdb", + run_test_command([PYTHON, test_script, *auto_args, *extra_args["gdb"], OBJDIR]), + )) # FIXME bug 1291449: This would be unnecessary if we could run msan with -mllvm # -msan-keep-going, but in clang 3.8 it causes a hang during compilation. @@ -691,15 +681,13 @@ def normalize_tests(tests): # Generate stacks from minidumps. if use_minidump: - run_mach_command( - [ - "python", - "--virtualenv=build", - os.path.join(DIR.source, "testing/mozbase/mozcrash/mozcrash/mozcrash.py"), - os.getenv("TMPDIR", "/tmp"), - os.path.join(OBJDIR, "dist/crashreporter-symbols"), - ] - ) + run_mach_command([ + "python", + "--virtualenv=build", + os.path.join(DIR.source, "testing/mozbase/mozcrash/mozcrash/mozcrash.py"), + os.getenv("TMPDIR", "/tmp"), + os.path.join(OBJDIR, "dist/crashreporter-symbols"), + ]) for name, st in results: print("exit status %d for '%s'" % (st, name)) diff --git a/js/src/devtools/gc/gc-test.py b/js/src/devtools/gc/gc-test.py index 23807fc432983..661171b3ef5fb 100644 --- a/js/src/devtools/gc/gc-test.py +++ b/js/src/devtools/gc/gc-test.py @@ -150,7 +150,7 @@ def compare(current, baseline): "--baseline", metavar="JSON_PATH", dest="baseline_path", - help="json file with baseline values to " "compare against", + help="json file with baseline values to compare against", ) (OPTIONS, args) = op.parse_args() diff --git a/js/src/devtools/rootAnalysis/analyze.py b/js/src/devtools/rootAnalysis/analyze.py index 961d2d36ad36f..d5c45aacddfd6 100755 --- a/js/src/devtools/rootAnalysis/analyze.py +++ b/js/src/devtools/rootAnalysis/analyze.py @@ -358,7 +358,7 @@ def max_parallel_jobs(job_size=4 * 2**30): "--expect-file", type=str, nargs="?", - help="deprecated option, temporarily still present for backwards " "compatibility", + help="deprecated option, temporarily still present for backwards compatibility", ) parser.add_argument( "--verbose", @@ -434,12 +434,13 @@ def max_parallel_jobs(job_size=4 * 2**30): # Trim the {curly brackets} off of the output keys. data[name[1:-1]] = outfiles[i] num_outputs += 1 - assert ( - len(outfiles) == num_outputs - ), 'step "%s": mismatched number of output files (%d) and params (%d)' % ( - step, - num_outputs, - len(outfiles), + assert len(outfiles) == num_outputs, ( + 'step "%s": mismatched number of output files (%d) and params (%d)' + % ( + step, + num_outputs, + len(outfiles), + ) ) # NOQA: E501 if args.step: diff --git a/js/src/devtools/rootAnalysis/mach_commands.py b/js/src/devtools/rootAnalysis/mach_commands.py index bd8426f29f340..649eff7b3b92c 100644 --- a/js/src/devtools/rootAnalysis/mach_commands.py +++ b/js/src/devtools/rootAnalysis/mach_commands.py @@ -159,8 +159,9 @@ def bootstrap(command_context, **kwargs): "what", default=["objdir", "work"], nargs="*", - help="Target to clobber, must be one of {{{}}} (default " - "objdir and work).".format(", ".join(CLOBBER_CHOICES)), + help="Target to clobber, must be one of {{{}}} (default objdir and work).".format( + ", ".join(CLOBBER_CHOICES) + ), ) def clobber(command_context, what, **kwargs): from mozbuild.controller.clobber import Clobberer @@ -344,15 +345,13 @@ def gather_hazard_data(command_context, **kwargs): ) fh.write(data) - buildscript = " ".join( - [ - command_context.topsrcdir + "/mach hazards compile", - *kwargs.get("what", []), - "--job-size=3.0", # Conservatively estimate 3GB/process - "--project=" + project, - "--haz-objdir=" + objdir, - ] - ) + buildscript = " ".join([ + command_context.topsrcdir + "/mach hazards compile", + *kwargs.get("what", []), + "--job-size=3.0", # Conservatively estimate 3GB/process + "--project=" + project, + "--haz-objdir=" + objdir, + ]) args = [ os.path.join(script_dir(command_context), "run_complete"), "--foreground", diff --git a/js/src/devtools/rootAnalysis/run-test.py b/js/src/devtools/rootAnalysis/run-test.py index 9301fd8fbcc60..b985aa67a4d36 100755 --- a/js/src/devtools/rootAnalysis/run-test.py +++ b/js/src/devtools/rootAnalysis/run-test.py @@ -84,9 +84,11 @@ cfg.sixgill, "usr", "libexec", "sixgill", "gcc", "xgill.so" ) -subprocess.check_call( - [cfg.js, "-e", 'if (!getBuildConfiguration("has-ctypes")) quit(1)'] -) +subprocess.check_call([ + cfg.js, + "-e", + 'if (!getBuildConfiguration("has-ctypes")) quit(1)', +]) def binpath(prog): diff --git a/js/src/devtools/rootAnalysis/t/graph/test.py b/js/src/devtools/rootAnalysis/t/graph/test.py index 5589b784bfdf0..bc7d84f44f597 100644 --- a/js/src/devtools/rootAnalysis/t/graph/test.py +++ b/js/src/devtools/rootAnalysis/t/graph/test.py @@ -42,13 +42,11 @@ assert callgraph.calleeGraph[node[src]][node[dst]] funcInfo = test.load_funcInfo() -rroots = set( - [ - callgraph.mangledToUnmangled[f] - for f in funcInfo - if funcInfo[f].get("recursive_root") - ] -) +rroots = set([ + callgraph.mangledToUnmangled[f] + for f in funcInfo + if funcInfo[f].get("recursive_root") +]) assert len(set([node[1], node[2]]) & rroots) == 1 assert len(set([node[4], node[5]]) & rroots) == 1 assert len(rroots) == 4, f"rroots = {rroots}" # n1, n4, f, self_recursive diff --git a/js/src/frontend/align_stack_comment.py b/js/src/frontend/align_stack_comment.py index 60895f9630ff4..5350b7fc57439 100755 --- a/js/src/frontend/align_stack_comment.py +++ b/js/src/frontend/align_stack_comment.py @@ -4,13 +4,13 @@ # You can obtain one at http://mozilla.org/MPL/2.0/. -""" Usage: align_stack_comment.py FILE +"""Usage: align_stack_comment.py FILE - This script aligns the stack transition comment in BytecodeEmitter and - its helper classes. +This script aligns the stack transition comment in BytecodeEmitter and +its helper classes. - The stack transition comment looks like the following: - // [stack] VAL1 VAL2 VAL3 +The stack transition comment looks like the following: + // [stack] VAL1 VAL2 VAL3 """ import re diff --git a/js/src/gdb/run-tests.py b/js/src/gdb/run-tests.py index 33efa7d83017c..7474271e50a42 100644 --- a/js/src/gdb/run-tests.py +++ b/js/src/gdb/run-tests.py @@ -252,14 +252,12 @@ def run_tests(tests, summary): # python 3.3 fixed a bug with concurrently writing .pyc files. # https://bugs.python.org/issue13146 embedded_version = ( - subprocess.check_output( - [ - OPTIONS.gdb_executable, - "--batch", - "--ex", - "python import sys; print(sys.hexversion)", - ] - ) + subprocess.check_output([ + OPTIONS.gdb_executable, + "--batch", + "--ex", + "python import sys; print(sys.hexversion)", + ]) .decode("ascii") .strip() ) diff --git a/js/src/jit-test/jit_test.py b/js/src/jit-test/jit_test.py index 6b49fa0412714..2381ec8672988 100755 --- a/js/src/jit-test/jit_test.py +++ b/js/src/jit-test/jit_test.py @@ -107,13 +107,13 @@ def main(argv): "--failed-only", dest="failed_only", action="store_true", - help="if --show-output is given, only print output for" " failed tests", + help="if --show-output is given, only print output for failed tests", ) op.add_argument( "--no-show-failed", dest="no_show_failed", action="store_true", - help="don't print output for failed tests" " (no-op with --show-output)", + help="don't print output for failed tests (no-op with --show-output)", ) op.add_argument( "-x", @@ -327,14 +327,14 @@ def main(argv): action="store", type=str, default="/data/local/tmp/test_root", - help="The remote directory to use as test root" " (e.g. %(default)s)", + help="The remote directory to use as test root (e.g. %(default)s)", ) op.add_argument( "--localLib", dest="local_lib", action="store", type=str, - help="The location of libraries to push -- preferably" " stripped", + help="The location of libraries to push -- preferably stripped", ) op.add_argument( "--repeat", type=int, default=1, help="Repeat tests the given number of times." @@ -536,8 +536,7 @@ def repeat_copy(job_list_generator, repeat): elif options.debugger: if job_count > 1: print( - "Multiple tests match command line" - " arguments, debugger can only run one" + "Multiple tests match command line arguments, debugger can only run one" ) jobs = list(job_list) @@ -589,7 +588,7 @@ def repeat_copy(job_list_generator, repeat): except OSError: if not os.path.exists(prefix[0]): print( - "JS shell argument: file does not exist:" f" '{prefix[0]}'", + f"JS shell argument: file does not exist: '{prefix[0]}'", file=sys.stderr, ) sys.exit(1) diff --git a/js/src/jit/GenerateABIFunctionType.py b/js/src/jit/GenerateABIFunctionType.py index bfa38b2d673bd..04be10d1de2a7 100644 --- a/js/src/jit/GenerateABIFunctionType.py +++ b/js/src/jit/GenerateABIFunctionType.py @@ -106,7 +106,7 @@ def arm32_soft_fp_args(func_type): contents += f"MakeInt64(stack_pointer[{stackOffset}], stack_pointer[{stackOffset + 1}])" stackOffset += 2 else: - contents += f"MakeInt64(a{intRegIndex}, a{intRegIndex+1})" + contents += f"MakeInt64(a{intRegIndex}, a{intRegIndex + 1})" intRegIndex += 2 elif arg == "Float32": if intRegIndex == numIntArgRegs: @@ -162,7 +162,7 @@ def arm32_hard_fp_args(func_type): contents += f"MakeInt64(stack_pointer[{stackOffset}], stack_pointer[{stackOffset + 1}])" stackOffset += 2 else: - contents += f"MakeInt64(a{intRegIndex}, a{intRegIndex+1})" + contents += f"MakeInt64(a{intRegIndex}, a{intRegIndex + 1})" intRegIndex += 2 elif arg == "Float32": if floatRegIndex == numFloatArgRegs: diff --git a/js/src/shell/js-gdb.py b/js/src/shell/js-gdb.py index dd4ae7b0cd1f1..dbe83fdb93f38 100644 --- a/js/src/shell/js-gdb.py +++ b/js/src/shell/js-gdb.py @@ -2,7 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -""" GDB Python customization auto-loader for JS shell binary """ +"""GDB Python customization auto-loader for JS shell binary""" # This script will be installed into $objdir/dist/bin. Add $objdir to gdb's # source search path and load in the Gecko+JS init file. diff --git a/js/src/tests/compare_bench.py b/js/src/tests/compare_bench.py index ec741682bbae1..bfb8e6c680d1f 100644 --- a/js/src/tests/compare_bench.py +++ b/js/src/tests/compare_bench.py @@ -1,6 +1,5 @@ #!/usr/bin/env python2.4 -"""usage: %progname candidate_path baseline_path -""" +"""usage: %progname candidate_path baseline_path""" import json import optparse diff --git a/js/src/tests/jstests.py b/js/src/tests/jstests.py index 91ab01ad57bc1..9d970f0d518e1 100755 --- a/js/src/tests/jstests.py +++ b/js/src/tests/jstests.py @@ -112,7 +112,7 @@ def parse_args(): op.add_argument( "--xul-info", dest="xul_info_src", - help="config data for xulRuntime" " (avoids search for config/autoconf.mk)", + help="config data for xulRuntime (avoids search for config/autoconf.mk)", ) harness_og = op.add_argument_group("Harness Controls", "Control how tests are run.") @@ -121,19 +121,19 @@ def parse_args(): "--worker-count", type=int, default=max(1, get_cpu_count()), - help="Number of tests to run in parallel" " (default %(default)s)", + help="Number of tests to run in parallel (default %(default)s)", ) harness_og.add_argument( "-t", "--timeout", type=float, default=150.0, - help="Set maximum time a test is allows to run" " (in seconds).", + help="Set maximum time a test is allows to run (in seconds).", ) harness_og.add_argument( "--show-slow", action="store_true", - help="Show tests taking longer than a minimum time" " (in seconds).", + help="Show tests taking longer than a minimum time (in seconds).", ) harness_og.add_argument( "--slow-test-threshold", @@ -168,12 +168,12 @@ def parse_args(): harness_og.add_argument( "--tbpl", action="store_true", - help="Runs each test in all configurations tbpl" " tests.", + help="Runs each test in all configurations tbpl tests.", ) harness_og.add_argument( "--tbpl-debug", action="store_true", - help="Runs each test in some faster configurations" " tbpl tests.", + help="Runs each test in some faster configurations tbpl tests.", ) harness_og.add_argument( "-g", "--debug", action="store_true", help="Run a test in debugger." @@ -187,7 +187,7 @@ def parse_args(): harness_og.add_argument( "--passthrough", action="store_true", - help="Run tests with stdin/stdout attached to" " caller.", + help="Run tests with stdin/stdout attached to caller.", ) harness_og.add_argument( "--test-reflect-stringify", @@ -244,14 +244,14 @@ def parse_args(): action="store", type=str, default="/data/local/tmp/test_root", - help="The remote directory to use as test root" " (e.g. %(default)s)", + help="The remote directory to use as test root (e.g. %(default)s)", ) harness_og.add_argument( "--localLib", dest="local_lib", action="store", type=str, - help="The location of libraries to push -- preferably" " stripped", + help="The location of libraries to push -- preferably stripped", ) harness_og.add_argument( "--no-xdr", @@ -319,7 +319,7 @@ def parse_args(): input_og.add_argument( "--no-extensions", action="store_true", - help="Run only tests conforming to the ECMAScript 5" " standard.", + help="Run only tests conforming to the ECMAScript 5 standard.", ) input_og.add_argument( "--repeat", type=int, default=1, help="Repeat tests the given number of times." @@ -336,23 +336,23 @@ def parse_args(): "-o", "--show-output", action="store_true", - help="Print each test's output to the file given by" " --output-file.", + help="Print each test's output to the file given by --output-file.", ) output_og.add_argument( "-F", "--failed-only", action="store_true", - help="If a --show-* option is given, only print" " output for failed tests.", + help="If a --show-* option is given, only print output for failed tests.", ) output_og.add_argument( "--no-show-failed", action="store_true", - help="Don't print output for failed tests" " (no-op with --show-output).", + help="Don't print output for failed tests (no-op with --show-output).", ) output_og.add_argument( "-O", "--output-file", - help="Write all output to the given file" " (default: stdout).", + help="Write all output to the given file (default: stdout).", ) output_og.add_argument( "--failure-file", help="Write all not-passed tests to the given file." @@ -375,7 +375,7 @@ def parse_args(): dest="format", default="none", choices=["automation", "none"], - help="Output format. Either automation or none" " (default %(default)s).", + help="Output format. Either automation or none (default %(default)s).", ) output_og.add_argument( "--log-wptreport", @@ -458,9 +458,9 @@ def parse_args(): # requested tests set. if options.test_file: for test_file in options.test_file: - requested_paths |= set( - [line.strip() for line in open(test_file).readlines()] - ) + requested_paths |= set([ + line.strip() for line in open(test_file).readlines() + ]) excluded_paths = set(options.excluded_paths) @@ -556,17 +556,15 @@ def load_wpt_tests(xul_tester, requested_paths, excluded_paths, update_manifest= ) kwargs = vars(wptcommandline.create_parser().parse_args([])) - kwargs.update( - { - "config": os.path.join( - manifest_root, "_tests", "web-platform", "wptrunner.local.ini" - ), - "gecko_e10s": False, - "product": "firefox", - "verify": False, - "wasm": xul_tester.test("wasmIsSupported()"), - } - ) + kwargs.update({ + "config": os.path.join( + manifest_root, "_tests", "web-platform", "wptrunner.local.ini" + ), + "gecko_e10s": False, + "product": "firefox", + "verify": False, + "wasm": xul_tester.test("wasmIsSupported()"), + }) wptcommandline.set_from_config(kwargs) def filter_jsshell_tests(it): @@ -793,8 +791,7 @@ def main(): if options.debug: if test_count > 1: print( - "Multiple tests match command line arguments," - " debugger can only run one" + "Multiple tests match command line arguments, debugger can only run one" ) for tc in test_gen: print(f" {tc.path}") diff --git a/js/src/tests/lib/jittests.py b/js/src/tests/lib/jittests.py index 209f167bcd0ff..e2ebbd3895a1c 100755 --- a/js/src/tests/lib/jittests.py +++ b/js/src/tests/lib/jittests.py @@ -288,12 +288,12 @@ def from_file(cls, path, options): else: test.expect_status = status except ValueError: - print("warning: couldn't parse exit status" f" {value}") + print(f"warning: couldn't parse exit status {value}") elif name == "thread-count": try: test.jitflags.append(f"--thread-count={int(value, 0)}") except ValueError: - print("warning: couldn't parse thread-count" f" {value}") + print(f"warning: couldn't parse thread-count {value}") elif name == "include": test.other_lib_includes.append(value) elif name == "local-include": @@ -310,8 +310,7 @@ def from_file(cls, path, options): print("warning: couldn't parse skip-variant-if") else: print( - f"{path}: warning: unrecognized |jit-test| attribute" - f" {part}" + f"{path}: warning: unrecognized |jit-test| attribute {part}" ) elif name == "slow": test.slow = True @@ -339,9 +338,9 @@ def from_file(cls, path, options): # works, and not meant as a way to accept temporary # failing tests. These tests should either be fixed or # skipped. - assert ( - "self-test" in path - ), f"{path}: has an unexpected crash annotation." + assert "self-test" in path, ( + f"{path}: has an unexpected crash annotation." + ) test.expect_crash = True elif name.startswith("--"): # // |jit-test| --ion-gvn=off; --no-sse4 @@ -352,9 +351,7 @@ def from_file(cls, path, options): # // |jit-test| -P pref(=value)? test.jitflags.append("--setpref=" + prefAndValue[1]) else: - print( - f"{path}: warning: unrecognized |jit-test| attribute" f" {part}" - ) + print(f"{path}: warning: unrecognized |jit-test| attribute {part}") if options.valgrind_all: test.valgrind = True diff --git a/js/src/tests/lib/manifest.py b/js/src/tests/lib/manifest.py index 20a704fd6aea4..2cfd3e40fae99 100644 --- a/js/src/tests/lib/manifest.py +++ b/js/src/tests/lib/manifest.py @@ -42,9 +42,7 @@ def as_js(self): var release_or_beta = getBuildConfiguration('release_or_beta'); var isDebugBuild={str(self.isdebug).lower()}; var Android={str(self.os == "Android").lower()}; -""".replace( - "\n", "" - ) +""".replace("\n", "") @classmethod def create(cls, jsdir): @@ -283,18 +281,16 @@ def _build_manifest_script_entry(script_name, test): properties = [] if test.terms: # Remove jsreftest internal terms. - terms = " ".join( - [ - term - for term in test.terms.split() - if not ( - term in {"module", "async", "test262-raw"} - or term.startswith("error:") - or term.startswith("ignore-flag(") - or term.startswith("shell-option(") - ) - ] - ) + terms = " ".join([ + term + for term in test.terms.split() + if not ( + term in {"module", "async", "test262-raw"} + or term.startswith("error:") + or term.startswith("ignore-flag(") + or term.startswith("shell-option(") + ) + ]) if terms: line.append(terms) if test.error: @@ -512,7 +508,7 @@ def _parse_external_manifest(filename, relpath): if not matches: matches = include_re.match(line) if not matches: - print("warning: unrecognized line in jstests.list:" f" {line}") + print(f"warning: unrecognized line in jstests.list: {line}") continue include_file = matches.group("path") @@ -533,13 +529,11 @@ def _parse_external_manifest(filename, relpath): assert path.endswith("jstests.list") path = path[: -len("jstests.list")] - entries.append( - { - "path": path, - "terms": matches.group("terms"), - "comment": comment.strip(), - } - ) + entries.append({ + "path": path, + "terms": matches.group("terms"), + "comment": comment.strip(), + }) # if one directory name is a prefix of another, we want the shorter one # first @@ -566,16 +560,14 @@ def _apply_external_manifests(filename, testcase, entries, xul_tester): def _is_test_file(path_from_root, basename, filename, path_options): # Any file whose basename matches something in this set is ignored. - EXCLUDED = set( - ( - "browser.js", - "shell.js", - "template.js", - "user.js", - "js-test-driver-begin.js", - "js-test-driver-end.js", - ) - ) + EXCLUDED = set(( + "browser.js", + "shell.js", + "template.js", + "user.js", + "js-test-driver-begin.js", + "js-test-driver-end.js", + )) # Skip js files in the root test directory. if not path_from_root: diff --git a/js/src/tests/lib/results.py b/js/src/tests/lib/results.py index f567c6390b3b8..4907e7945c0bd 100644 --- a/js/src/tests/lib/results.py +++ b/js/src/tests/lib/results.py @@ -121,14 +121,12 @@ def from_wpt_output(cls, output): test_output += "expected %s, found %s" % (expected, test.status) if test.message: test_output += ' (with message: "%s")' % (test.message,) - subtests.append( - { - "test": output.test.wpt.id, - "subtest": test.name, - "status": test.status, - "expected": expected, - } - ) + subtests.append({ + "test": output.test.wpt.id, + "subtest": test.name, + "status": test.status, + "expected": expected, + }) results.append(test_result) stdout.append(test_output) @@ -172,8 +170,7 @@ def from_output(cls, output): results.append((cls.PASS, msg)) else: m = re.match( - "--- NOTE: IN THIS TESTCASE, WE EXPECT EXIT CODE" - " ((?:-|\\d)+) ---", + "--- NOTE: IN THIS TESTCASE, WE EXPECT EXIT CODE ((?:-|\\d)+) ---", line, ) if m: diff --git a/js/src/tests/lib/wptreport.py b/js/src/tests/lib/wptreport.py index 39aeea212f8f3..2f4e7c6df6a37 100644 --- a/js/src/tests/lib/wptreport.py +++ b/js/src/tests/lib/wptreport.py @@ -26,23 +26,19 @@ def suite_start(self): """ Produce the "suite_start" message at the present time. """ - self.formatter.suite_start( - { - "time": time(), - "run_info": {}, - } - ) + self.formatter.suite_start({ + "time": time(), + "run_info": {}, + }) def suite_end(self): """ Produce the "suite_end" message at the present time and write the results to the file path given in the constructor. """ - result = self.formatter.suite_end( - { - "time": time(), - } - ) + result = self.formatter.suite_end({ + "time": time(), + }) with open(self.out, "w") as fp: fp.write(result) @@ -65,21 +61,17 @@ def test(self, result, duration): end_time = time() start_time = end_time - duration - self.formatter.test_start( - { - "test": testname, - "time": start_time, - } - ) + self.formatter.test_start({ + "test": testname, + "time": start_time, + }) for subtest in result["subtests"]: self.formatter.test_status(subtest) - self.formatter.test_end( - { - "test": testname, - "time": end_time, - "status": result["status"], - "expected": result["expected"], - } - ) + self.formatter.test_end({ + "test": testname, + "time": end_time, + "status": result["status"], + "expected": result["expected"], + }) diff --git a/js/src/tests/non262/String/make-normalize-generateddata-input.py b/js/src/tests/non262/String/make-normalize-generateddata-input.py index 3fe19b55216ef..554d1ddebff53 100644 --- a/js/src/tests/non262/String/make-normalize-generateddata-input.py +++ b/js/src/tests/non262/String/make-normalize-generateddata-input.py @@ -1,10 +1,10 @@ #!/usr/bin/python -B -""" Usage: make-normalize-generateddata-input.py PATH_TO_MOZILLA_CENTRAL +"""Usage: make-normalize-generateddata-input.py PATH_TO_MOZILLA_CENTRAL - This script generates test input data for String.prototype.normalize - from intl/icu/source/data/unidata/NormalizationTest.txt - to js/src/tests/non262/String/normalize-generateddata-input.js +This script generates test input data for String.prototype.normalize +from intl/icu/source/data/unidata/NormalizationTest.txt +to js/src/tests/non262/String/normalize-generateddata-input.js """ import re diff --git a/js/src/tests/parsemark.py b/js/src/tests/parsemark.py index 4723814f8c912..00574ce3c60e5 100644 --- a/js/src/tests/parsemark.py +++ b/js/src/tests/parsemark.py @@ -134,20 +134,20 @@ def main(): metavar="COUNT", type=int, default=50, - help="timed data runs that count towards the average" " [%default]", + help="timed data runs that count towards the average [%default]", ) parser.add_option( "-s", "--shell", metavar="PATH", - help="explicit shell location; when omitted, will look" " in likely places", + help="explicit shell location; when omitted, will look in likely places", ) parser.add_option( "-b", "--baseline", metavar="JSON_PATH", dest="baseline_path", - help="json file with baseline values to " "compare against", + help="json file with baseline values to compare against", ) parser.add_option( "--mode", diff --git a/js/src/tests/test/run.py b/js/src/tests/test/run.py index 7bf2021002376..9a92a9d38dff2 100755 --- a/js/src/tests/test/run.py +++ b/js/src/tests/test/run.py @@ -51,28 +51,35 @@ def importLocal(self): subprocess.check_call(["git", "-C", cloneDir, "checkout", "-b", branch]) # Make changes on the new branch # Remove test/language/export/escaped-from.js - subprocess.check_call( - ["git", "-C", cloneDir, "rm", "test/language/export/escaped-from.js"] - ) + subprocess.check_call([ + "git", + "-C", + cloneDir, + "rm", + "test/language/export/escaped-from.js", + ]) # Rename test/language/export/escaped-default.js - subprocess.check_call( - [ - "git", - "-C", - cloneDir, - "mv", - "test/language/export/escaped-default.js", - "test/language/export/escaped-foobarbaz.js", - ] - ) + subprocess.check_call([ + "git", + "-C", + cloneDir, + "mv", + "test/language/export/escaped-default.js", + "test/language/export/escaped-foobarbaz.js", + ]) # Copy fixtures files fixturesDir = os.path.join(testDir, "fixtures", "import", "files") shutil.copytree(fixturesDir, os.path.join(cloneDir, "test", "temp42")) # Stage and Commit changes subprocess.check_call(["git", "-C", cloneDir, "add", "."]) - subprocess.check_call( - ["git", "-C", cloneDir, "commit", "-m", '"local foo"'] - ) + subprocess.check_call([ + "git", + "-C", + cloneDir, + "commit", + "-m", + '"local foo"', + ]) # Run import script print( diff --git a/js/src/tests/test262-export.py b/js/src/tests/test262-export.py index e5095a09e1c50..b4dd0a8e257e3 100755 --- a/js/src/tests/test262-export.py +++ b/js/src/tests/test262-export.py @@ -17,16 +17,14 @@ # Skip all common files used to support tests for jstests # These files are listed in the README.txt -SUPPORT_FILES = set( - [ - "browser.js", - "shell.js", - "template.js", - "user.js", - "js-test-driver-begin.js", - "js-test-driver-end.js", - ] -) +SUPPORT_FILES = set([ + "browser.js", + "shell.js", + "template.js", + "user.js", + "js-test-driver-begin.js", + "js-test-driver-end.js", +]) # Run once per subdirectory diff --git a/js/src/tests/test262-update.py b/js/src/tests/test262-update.py index 634f59c02147f..d528e9a9910f2 100755 --- a/js/src/tests/test262-update.py +++ b/js/src/tests/test262-update.py @@ -14,16 +14,14 @@ from operator import itemgetter # Skip all tests which use features not supported in SpiderMonkey. -UNSUPPORTED_FEATURES = set( - [ - "tail-call-optimization", - "Intl.Locale-info", # Bug 1693576 - "source-phase-imports", - "source-phase-imports-module-source", - "import-defer", - "nonextensible-applies-to-private", # Bug 1991478 - ] -) +UNSUPPORTED_FEATURES = set([ + "tail-call-optimization", + "Intl.Locale-info", # Bug 1693576 + "source-phase-imports", + "source-phase-imports-module-source", + "import-defer", + "nonextensible-applies-to-private", # Bug 1991478 +]) FEATURE_CHECK_NEEDED = { "Atomics": "!this.hasOwnProperty('Atomics')", "SharedArrayBuffer": "!this.hasOwnProperty('SharedArrayBuffer')", @@ -318,7 +316,7 @@ def convertTestFile(test262parser, testSource, testName, includeSet, strictTests or isAsync or isNegative or testName.split(os.path.sep)[0] == "harness" - ), ("Missing async attribute in: %s" % testName) + ), "Missing async attribute in: %s" % testName # When the "module" attribute is set, the source code is module code. isModule = "module" in testRec @@ -341,38 +339,29 @@ def convertTestFile(test262parser, testSource, testName, includeSet, strictTests else: releaseOrBeta = [f for f in testRec["features"] if f in RELEASE_OR_BETA] if releaseOrBeta: - refTestSkipIf.append( - ( - "release_or_beta", - "%s is not released yet" % ",".join(releaseOrBeta), - ) - ) + refTestSkipIf.append(( + "release_or_beta", + "%s is not released yet" % ",".join(releaseOrBeta), + )) featureCheckNeeded = [ f for f in testRec["features"] if f in FEATURE_CHECK_NEEDED ] if featureCheckNeeded: - refTestSkipIf.append( - ( - "||".join( - [FEATURE_CHECK_NEEDED[f] for f in featureCheckNeeded] - ), - "%s is not enabled unconditionally" - % ",".join(featureCheckNeeded), - ) - ) + refTestSkipIf.append(( + "||".join([FEATURE_CHECK_NEEDED[f] for f in featureCheckNeeded]), + "%s is not enabled unconditionally" % ",".join(featureCheckNeeded), + )) if ( "Atomics" in testRec["features"] and "SharedArrayBuffer" in testRec["features"] ): - refTestSkipIf.append( - ( - "(this.hasOwnProperty('getBuildConfiguration')" - "&&getBuildConfiguration('arm64-simulator'))", - "ARM64 Simulator cannot emulate atomics", - ) - ) + refTestSkipIf.append(( + "(this.hasOwnProperty('getBuildConfiguration')" + "&&getBuildConfiguration('arm64-simulator'))", + "ARM64 Simulator cannot emulate atomics", + )) shellOptions = { SHELL_OPTIONS[f] for f in testRec["features"] if f in SHELL_OPTIONS @@ -577,12 +566,10 @@ def process_test262(test262Dir, test262OutDir, strictTests, externManifests): writeTestFile(test262OutDir, newFileName, newSource) if externRefTest is not None: - externManifests.append( - { - "name": newFileName, - "reftest": externRefTest, - } - ) + externManifests.append({ + "name": newFileName, + "reftest": externRefTest, + }) # Remove "sm/non262.js" because it overwrites our test harness with stub # functions. @@ -873,13 +860,23 @@ def update_test262(args): return fetch_local_changes(inDir, outDir, srcDir, strictTests) if revision == "HEAD": - subprocess.check_call( - ["git", "clone", "--depth=1", "--branch=%s" % branch, url, inDir] - ) + subprocess.check_call([ + "git", + "clone", + "--depth=1", + "--branch=%s" % branch, + url, + inDir, + ]) else: - subprocess.check_call( - ["git", "clone", "--single-branch", "--branch=%s" % branch, url, inDir] - ) + subprocess.check_call([ + "git", + "clone", + "--single-branch", + "--branch=%s" % branch, + url, + inDir, + ]) subprocess.check_call(["git", "-C", inDir, "reset", "--hard", revision]) # If a PR number is provided, fetches only the new and modified files diff --git a/js/src/util/make_unicode.py b/js/src/util/make_unicode.py index 4145433fb9e34..0e30703f4665f 100755 --- a/js/src/util/make_unicode.py +++ b/js/src/util/make_unicode.py @@ -571,15 +571,13 @@ def is_equals(iter1, iter2): ) # Ensure all case mapping contexts are known (see Unicode 9.0, §3.13 Default Case Algorithms). - assert set( - [ - "After_I", - "After_Soft_Dotted", - "Final_Sigma", - "More_Above", - "Not_Before_Dot", - ] - ).issuperset( + assert set([ + "After_I", + "After_Soft_Dotted", + "Final_Sigma", + "More_Above", + "Not_Before_Dot", + ]).issuperset( set( filter( partial(is_not, None), diff --git a/js/src/vm/jsopcode.py b/js/src/vm/jsopcode.py index e64ec246dfe77..6c5dbf2cdfe1d 100644 --- a/js/src/vm/jsopcode.py +++ b/js/src/vm/jsopcode.py @@ -149,12 +149,12 @@ def find_by_name(list, name): def add_to_index(index, opcode): types = find_by_name(index, opcode.category_name) if types is None: - raise Exception("Category is not listed in index: " f"{opcode.category_name}") + raise Exception(f"Category is not listed in index: {opcode.category_name}") opcodes = find_by_name(types, opcode.type_name) if opcodes is None: if opcode.type_name: raise Exception( - f"Type is not listed in {opcode.category_name}: " f"{opcode.type_name}" + f"Type is not listed in {opcode.category_name}: {opcode.type_name}" ) types.append((opcode.type_name, [opcode])) return @@ -302,7 +302,7 @@ def get_opcodes(dir): opcode.sort_key = opcode.op if opcode.category_name == "": - raise Exception("Category is not specified for " f"{opcode.op}") + raise Exception(f"Category is not specified for {opcode.op}") add_to_index(index, opcode) else: if group_head.length != opcode.length: diff --git a/js/src/vm/make_opcode_doc.py b/js/src/vm/make_opcode_doc.py index 80547a18f09ba..7eefabb2ee4ce 100755 --- a/js/src/vm/make_opcode_doc.py +++ b/js/src/vm/make_opcode_doc.py @@ -4,14 +4,14 @@ # You can obtain one at http://mozilla.org/MPL/2.0/. -""" Usage: python make_opcode_doc.py +"""Usage: python make_opcode_doc.py - This script generates SpiderMonkey bytecode documentation - from js/src/vm/Opcodes.h. +This script generates SpiderMonkey bytecode documentation +from js/src/vm/Opcodes.h. - Output is written to stdout and should be pasted into the following - MDN page: - https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode +Output is written to stdout and should be pasted into the following +MDN page: +https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode """ import os diff --git a/js/src/wasm/GenerateBuiltinModules.py b/js/src/wasm/GenerateBuiltinModules.py index f8a137049486c..e263313770892 100644 --- a/js/src/wasm/GenerateBuiltinModules.py +++ b/js/src/wasm/GenerateBuiltinModules.py @@ -122,7 +122,7 @@ def main(c_out, yaml_path): if "inline_op" in op: inlineOp = f"BuiltinInlineOp::{op['inline_op']}" contents += ( - f" M({op['op']}, \"{op['export']}\", " + f' M({op["op"]}, "{op["export"]}", ' f"{sa['name']}, {sa['type']}, {cppBool(sa['needs_thunk'])}, {op['entry']}, {cppBool(op['uses_memory'])}, {inlineOp}, {i})\\\n" ) contents += "\n" diff --git a/layout/generic/AbsoluteContainingBlock.cpp b/layout/generic/AbsoluteContainingBlock.cpp index 2d969c79bb477..137ee067849e2 100644 --- a/layout/generic/AbsoluteContainingBlock.cpp +++ b/layout/generic/AbsoluteContainingBlock.cpp @@ -1136,6 +1136,9 @@ struct ContainingBlockRect { explicit ContainingBlockRect(const nsRect& aRect) : mMaybeScrollableRect{aRect}, mFinalRect{aRect} {} + ContainingBlockRect(const nsRect& aMaybeScrollableRect, + const nsRect& aFinalRect) + : mMaybeScrollableRect{aMaybeScrollableRect}, mFinalRect{aFinalRect} {} ContainingBlockRect(const nsPoint& aOffset, const StylePositionArea& aResolvedArea, const nsRect& aMaybeScrollableRect, @@ -1385,7 +1388,8 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( // scroll offset (See above), the offset will be applied later. scrolledAnchorCb + offset}; } - return ContainingBlockRect{containingBlock}; + return ContainingBlockRect{aOriginalScrollableContainingBlockRect, + containingBlock}; } if (ViewportFrame* viewport = do_QueryFrame(aDelegatingFrame)) { diff --git a/layout/painting/nsImageRenderer.cpp b/layout/painting/nsImageRenderer.cpp index 479c5b535e8b0..019210c89f001 100644 --- a/layout/painting/nsImageRenderer.cpp +++ b/layout/painting/nsImageRenderer.cpp @@ -783,7 +783,7 @@ already_AddRefed nsImageRenderer::DrawableForElement( drawable = SVGIntegrationUtils::DrawableFromPaintServer( mPaintServerFrame, mForFrame, mSize, imageSize, aContext.GetDrawTarget(), aContext.CurrentMatrixDouble(), - SVGIntegrationUtils::DecodeFlags::SyncDecodeImages); + SVGIntegrationUtils::DecodeFlag::SyncDecodeImages); } return drawable.forget(); diff --git a/layout/svg/ISVGDisplayableFrame.h b/layout/svg/ISVGDisplayableFrame.h index 6415c4ba7cdc1..7a657406e2c8c 100644 --- a/layout/svg/ISVGDisplayableFrame.h +++ b/layout/svg/ISVGDisplayableFrame.h @@ -103,11 +103,12 @@ class ISVGDisplayableFrame : public nsQueryFrame { // lengths must be reevaluated) // FullZoomChanged: // the page's zoom level has changed - enum class ChangeFlags { + enum class ChangeFlag { TransformChanged, CoordContextChanged, FullZoomChanged }; + using ChangeFlags = EnumSet; /** * This is called on a frame when there has been a change to one of its @@ -118,7 +119,7 @@ class ISVGDisplayableFrame : public nsQueryFrame { * invalidate the entire area of the ancestor that changed. However, they * may need to update their bounds. */ - virtual void NotifySVGChanged(EnumSet aFlags) = 0; + virtual void NotifySVGChanged(ChangeFlags aFlags) = 0; /** * Get this frame's contribution to the rect returned by a GetBBox() call diff --git a/layout/svg/ISVGSVGFrame.h b/layout/svg/ISVGSVGFrame.h index 3364fb363fe05..bf48c53cc80e1 100644 --- a/layout/svg/ISVGSVGFrame.h +++ b/layout/svg/ISVGSVGFrame.h @@ -20,11 +20,11 @@ class ISVGSVGFrame { * Called when non-attribute changes have caused the element's width/height * or its for-children transform to change, and to get the element to notify * its children appropriately. aFlags must be set to - * ISVGDisplayableFrame::ChangeFlags::CoordContextChanged and/or - * ISVGDisplayableFrame::ChangeFlags::TransformChanged. + * ISVGDisplayableFrame::ChangeFlag::CoordContextChanged and/or + * ISVGDisplayableFrame::ChangeFlag::TransformChanged. */ virtual void NotifyViewportOrTransformChanged( - EnumSet aFlags) = 0; + ISVGDisplayableFrame::ChangeFlags aFlags) = 0; }; } // namespace mozilla diff --git a/layout/svg/SVGClipPathFrame.cpp b/layout/svg/SVGClipPathFrame.cpp index 0534a08ab7fbf..e1b45bd3cf2e3 100644 --- a/layout/svg/SVGClipPathFrame.cpp +++ b/layout/svg/SVGClipPathFrame.cpp @@ -172,7 +172,7 @@ void SVGClipPathFrame::PaintFrameIntoMask(nsIFrame* aFrame, } // The CTM of each frame referencing us can be different. - frame->NotifySVGChanged(ISVGDisplayableFrame::ChangeFlags::TransformChanged); + frame->NotifySVGChanged(ISVGDisplayableFrame::ChangeFlag::TransformChanged); // Children of this clipPath may themselves be clipped. SVGClipPathFrame* clipPathThatClipsChild; diff --git a/layout/svg/SVGContainerFrame.cpp b/layout/svg/SVGContainerFrame.cpp index 63ef168e8973e..273200f381f01 100644 --- a/layout/svg/SVGContainerFrame.cpp +++ b/layout/svg/SVGContainerFrame.cpp @@ -381,16 +381,16 @@ void SVGDisplayContainerFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) { } if (StyleDisplay()->CalcTransformPropertyDifference( *aOldStyle->StyleDisplay())) { - NotifySVGChanged(ChangeFlags::TransformChanged); + NotifySVGChanged(ChangeFlag::TransformChanged); } } -void SVGDisplayContainerFrame::NotifySVGChanged(EnumSet aFlags) { - MOZ_ASSERT(aFlags.contains(ChangeFlags::TransformChanged) || - aFlags.contains(ChangeFlags::CoordContextChanged), +void SVGDisplayContainerFrame::NotifySVGChanged(ChangeFlags aFlags) { + MOZ_ASSERT(aFlags.contains(ChangeFlag::TransformChanged) || + aFlags.contains(ChangeFlag::CoordContextChanged), "Invalidation logic may need adjusting"); - if (aFlags.contains(ChangeFlags::TransformChanged)) { + if (aFlags.contains(ChangeFlag::TransformChanged)) { // make sure our cached transform matrix gets (lazily) updated mCanvasTM = nullptr; } diff --git a/layout/svg/SVGContainerFrame.h b/layout/svg/SVGContainerFrame.h index 714e4ae2b0b81..15d5a4cd20b2d 100644 --- a/layout/svg/SVGContainerFrame.h +++ b/layout/svg/SVGContainerFrame.h @@ -133,7 +133,7 @@ class SVGDisplayContainerFrame : public SVGContainerFrame, imgDrawingParams& aImgParams) override; nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override; void ReflowSVG() override; - void NotifySVGChanged(EnumSet aFlags) override; + void NotifySVGChanged(ChangeFlags aFlags) override; SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace, uint32_t aFlags) override; bool IsDisplayContainer() override { return true; } diff --git a/layout/svg/SVGForeignObjectFrame.cpp b/layout/svg/SVGForeignObjectFrame.cpp index 0b72148e9acae..3a557ca469af8 100644 --- a/layout/svg/SVGForeignObjectFrame.cpp +++ b/layout/svg/SVGForeignObjectFrame.cpp @@ -280,16 +280,16 @@ void SVGForeignObjectFrame::ReflowSVG() { NS_FRAME_HAS_DIRTY_CHILDREN); } -void SVGForeignObjectFrame::NotifySVGChanged(EnumSet aFlags) { - MOZ_ASSERT(aFlags.contains(ChangeFlags::TransformChanged) || - aFlags.contains(ChangeFlags::CoordContextChanged), +void SVGForeignObjectFrame::NotifySVGChanged(ChangeFlags aFlags) { + MOZ_ASSERT(aFlags.contains(ChangeFlag::TransformChanged) || + aFlags.contains(ChangeFlag::CoordContextChanged), "Invalidation logic may need adjusting"); bool needNewBounds = false; // i.e. mRect or ink overflow rect bool needReflow = false; bool needNewCanvasTM = false; - if (aFlags.contains(ChangeFlags::CoordContextChanged)) { + if (aFlags.contains(ChangeFlag::CoordContextChanged)) { // Coordinate context changes affect mCanvasTM if we have a // percentage 'x' or 'y' if (StyleSVGReset()->mX.HasPercent() || StyleSVGReset()->mY.HasPercent()) { @@ -307,7 +307,7 @@ void SVGForeignObjectFrame::NotifySVGChanged(EnumSet aFlags) { } } - if (aFlags.contains(ChangeFlags::TransformChanged)) { + if (aFlags.contains(ChangeFlag::TransformChanged)) { if (mCanvasTM && mCanvasTM->IsSingular()) { needNewBounds = true; // old bounds are bogus } diff --git a/layout/svg/SVGForeignObjectFrame.h b/layout/svg/SVGForeignObjectFrame.h index 1d23e51927f50..d563165c4f1cf 100644 --- a/layout/svg/SVGForeignObjectFrame.h +++ b/layout/svg/SVGForeignObjectFrame.h @@ -62,7 +62,7 @@ class SVGForeignObjectFrame final : public nsContainerFrame, imgDrawingParams& aImgParams) override; nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override; void ReflowSVG() override; - void NotifySVGChanged(EnumSet aFlags) override; + void NotifySVGChanged(ChangeFlags aFlags) override; SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace, uint32_t aFlags) override; bool IsDisplayContainer() override { return true; } diff --git a/layout/svg/SVGGFrame.cpp b/layout/svg/SVGGFrame.cpp index 7cfe1497cb36a..d4b099dede744 100644 --- a/layout/svg/SVGGFrame.cpp +++ b/layout/svg/SVGGFrame.cpp @@ -49,7 +49,7 @@ nsresult SVGGFrame::AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, // Also note that SVGTransformableElement::GetAttributeChangeHint will // return nsChangeHint_UpdateOverflow for "transform" attribute changes // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call. - NotifySVGChanged(ChangeFlags::TransformChanged); + NotifySVGChanged(ChangeFlag::TransformChanged); } return NS_OK; diff --git a/layout/svg/SVGGeometryFrame.cpp b/layout/svg/SVGGeometryFrame.cpp index ed6b3b186b017..9cf41813d802b 100644 --- a/layout/svg/SVGGeometryFrame.cpp +++ b/layout/svg/SVGGeometryFrame.cpp @@ -116,7 +116,7 @@ void SVGGeometryFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) { if (StyleDisplay()->CalcTransformPropertyDifference( *aOldComputedStyle->StyleDisplay())) { - NotifySVGChanged(ChangeFlags::TransformChanged); + NotifySVGChanged(ChangeFlag::TransformChanged); } if (element->IsGeometryChangedViaCSS(*Style(), *aOldComputedStyle) || @@ -177,17 +177,18 @@ void SVGGeometryFrame::PaintSVG(gfxContext& aContext, uint32_t paintOrder = StyleSVG()->mPaintOrder; if (!paintOrder) { - Render(&aContext, eRenderFill | eRenderStroke, newMatrix, aImgParams); + Render(&aContext, RenderFlags(RenderFlag::Fill, RenderFlag::Stroke), + newMatrix, aImgParams); PaintMarkers(aContext, aTransform, aImgParams); } else { while (paintOrder) { auto component = StylePaintOrder(paintOrder & kPaintOrderMask); switch (component) { case StylePaintOrder::Fill: - Render(&aContext, eRenderFill, newMatrix, aImgParams); + Render(&aContext, RenderFlag::Fill, newMatrix, aImgParams); break; case StylePaintOrder::Stroke: - Render(&aContext, eRenderStroke, newMatrix, aImgParams); + Render(&aContext, RenderFlag::Stroke, newMatrix, aImgParams); break; case StylePaintOrder::Markers: PaintMarkers(aContext, aTransform, aImgParams); @@ -309,9 +310,9 @@ void SVGGeometryFrame::ReflowSVG() { } } -void SVGGeometryFrame::NotifySVGChanged(EnumSet aFlags) { - MOZ_ASSERT(aFlags.contains(ChangeFlags::TransformChanged) || - aFlags.contains(ChangeFlags::CoordContextChanged), +void SVGGeometryFrame::NotifySVGChanged(ChangeFlags aFlags) { + MOZ_ASSERT(aFlags.contains(ChangeFlag::TransformChanged) || + aFlags.contains(ChangeFlag::CoordContextChanged), "Invalidation logic may need adjusting"); // Changes to our ancestors may affect how we render when we are rendered as @@ -327,7 +328,7 @@ void SVGGeometryFrame::NotifySVGChanged(EnumSet aFlags) { // overflow rects or not, and we sometimes deliberately include stroke // when it's not visible. See the complexities of GetBBoxContribution. - if (aFlags.contains(ChangeFlags::CoordContextChanged)) { + if (aFlags.contains(ChangeFlag::CoordContextChanged)) { auto* geom = static_cast(GetContent()); // Stroke currently contributes to our mRect, which is why we have to take // account of stroke-width here. Note that we do not need to take account @@ -343,7 +344,7 @@ void SVGGeometryFrame::NotifySVGChanged(EnumSet aFlags) { } } - if (aFlags.contains(ChangeFlags::TransformChanged) && + if (aFlags.contains(ChangeFlag::TransformChanged) && StyleSVGReset()->HasNonScalingStroke()) { // Stroke currently contributes to our mRect, and our stroke depends on // the transform to our outer- if |vector-effect:non-scaling-stroke|. @@ -399,8 +400,9 @@ SVGBBox SVGGeometryFrame::GetBBoxContribution(const Matrix& aToBBoxUserspace, SVGContentUtils::AutoStrokeOptions strokeOptions; if (getStroke) { - SVGContentUtils::GetStrokeOptions(&strokeOptions, element, Style(), nullptr, - SVGContentUtils::eIgnoreStrokeDashing); + SVGContentUtils::GetStrokeOptions( + &strokeOptions, element, Style(), nullptr, + SVGContentUtils::StrokeOptionFlag::IgnoreStrokeDashing); } else { // Override the default line width of 1.f so that when we call // GetGeometryBounds below the result doesn't include stroke bounds. @@ -549,7 +551,8 @@ gfxMatrix SVGGeometryFrame::GetCanvasTM() { return content->ChildToUserSpaceTransform() * parent->GetCanvasTM(); } -void SVGGeometryFrame::Render(gfxContext* aContext, uint32_t aRenderComponents, +void SVGGeometryFrame::Render(gfxContext* aContext, + RenderFlags aRenderComponents, const gfxMatrix& aTransform, imgDrawingParams& aImgParams) { MOZ_ASSERT(!aTransform.IsSingular()); @@ -601,7 +604,7 @@ void SVGGeometryFrame::Render(gfxContext* aContext, uint32_t aRenderComponents, SVGContextPaint* contextPaint = SVGContextPaint::GetContextPaint(GetContent()); - if (aRenderComponents & eRenderFill) { + if (aRenderComponents.contains(RenderFlag::Fill)) { GeneralPattern fillPattern; SVGUtils::MakeFillPatternFor(this, aContext, &fillPattern, aImgParams, contextPaint); @@ -616,7 +619,7 @@ void SVGGeometryFrame::Render(gfxContext* aContext, uint32_t aRenderComponents, } } - if ((aRenderComponents & eRenderStroke) && + if (aRenderComponents.contains(RenderFlag::Stroke) && SVGUtils::HasStroke(this, contextPaint)) { // Account for vector-effect:non-scaling-stroke: gfxMatrix userToOuterSVG; diff --git a/layout/svg/SVGGeometryFrame.h b/layout/svg/SVGGeometryFrame.h index 1e039d0734ab0..162cca275020c 100644 --- a/layout/svg/SVGGeometryFrame.h +++ b/layout/svg/SVGGeometryFrame.h @@ -10,6 +10,7 @@ #include "gfxMatrix.h" #include "gfxRect.h" #include "mozilla/DisplaySVGItem.h" +#include "mozilla/EnumSet.h" #include "mozilla/ISVGDisplayableFrame.h" #include "nsIFrame.h" @@ -91,13 +92,14 @@ class SVGGeometryFrame final : public nsIFrame, public ISVGDisplayableFrame { imgDrawingParams& aImgParams) override; nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override; void ReflowSVG() override; - void NotifySVGChanged(EnumSet aFlags) override; + void NotifySVGChanged(ChangeFlags aFlags) override; SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace, uint32_t aFlags) override; bool IsDisplayContainer() override { return false; } - enum { eRenderFill = 1, eRenderStroke = 2 }; - void Render(gfxContext* aContext, uint32_t aRenderComponents, + enum class RenderFlag { Fill, Stroke }; + using RenderFlags = EnumSet; + void Render(gfxContext* aContext, RenderFlags aRenderComponents, const gfxMatrix& aTransform, imgDrawingParams& aImgParams); bool CreateWebRenderCommands( diff --git a/layout/svg/SVGImageFrame.cpp b/layout/svg/SVGImageFrame.cpp index aa5aa9b0b6bcc..9a13458b732fb 100644 --- a/layout/svg/SVGImageFrame.cpp +++ b/layout/svg/SVGImageFrame.cpp @@ -826,9 +826,9 @@ bool SVGImageFrame::IgnoreHitTest() const { return true; } -void SVGImageFrame::NotifySVGChanged(EnumSet aFlags) { - MOZ_ASSERT(aFlags.contains(ChangeFlags::TransformChanged) || - aFlags.contains(ChangeFlags::CoordContextChanged), +void SVGImageFrame::NotifySVGChanged(ChangeFlags aFlags) { + MOZ_ASSERT(aFlags.contains(ChangeFlag::TransformChanged) || + aFlags.contains(ChangeFlag::CoordContextChanged), "Invalidation logic may need adjusting"); } diff --git a/layout/svg/SVGImageFrame.h b/layout/svg/SVGImageFrame.h index 6c8e967be09b0..eac4619dca34f 100644 --- a/layout/svg/SVGImageFrame.h +++ b/layout/svg/SVGImageFrame.h @@ -63,7 +63,7 @@ class SVGImageFrame final : public nsIFrame, imgDrawingParams& aImgParams) override; nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override; void ReflowSVG() override; - void NotifySVGChanged(EnumSet aFlags) override; + void NotifySVGChanged(ChangeFlags aFlags) override; SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace, uint32_t aFlags) override; bool IsDisplayContainer() override { return false; } diff --git a/layout/svg/SVGIntegrationUtils.cpp b/layout/svg/SVGIntegrationUtils.cpp index d293524dac5c6..fd2464cd5e66f 100644 --- a/layout/svg/SVGIntegrationUtils.cpp +++ b/layout/svg/SVGIntegrationUtils.cpp @@ -1080,7 +1080,7 @@ class PaintFrameCallback : public gfxDrawingCallback { public: PaintFrameCallback(nsIFrame* aFrame, const nsSize aPaintServerSize, const IntSize aRenderSize, - EnumSet aFlags) + SVGIntegrationUtils::DecodeFlags aFlags) : mFrame(aFrame), mPaintServerSize(aPaintServerSize), mRenderSize(aRenderSize), @@ -1093,7 +1093,7 @@ class PaintFrameCallback : public gfxDrawingCallback { nsIFrame* mFrame; nsSize mPaintServerSize; IntSize mRenderSize; - EnumSet mFlags; + SVGIntegrationUtils::DecodeFlags mFlags; }; bool PaintFrameCallback::operator()(gfxContext* aContext, @@ -1141,7 +1141,7 @@ bool PaintFrameCallback::operator()(gfxContext* aContext, using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags; PaintFrameFlags flags = PaintFrameFlags::InTransform; - if (mFlags.contains(SVGIntegrationUtils::DecodeFlags::SyncDecodeImages)) { + if (mFlags.contains(SVGIntegrationUtils::DecodeFlag::SyncDecodeImages)) { flags |= PaintFrameFlags::SyncDecodeImages; } nsLayoutUtils::PaintFrame(aContext, mFrame, dirty, NS_RGBA(0, 0, 0, 0), @@ -1173,7 +1173,7 @@ bool PaintFrameCallback::operator()(gfxContext* aContext, already_AddRefed SVGIntegrationUtils::DrawableFromPaintServer( nsIFrame* aFrame, nsIFrame* aTarget, const nsSize& aPaintServerSize, const IntSize& aRenderSize, const DrawTarget* aDrawTarget, - const gfxMatrix& aContextMatrix, EnumSet aFlags) { + const gfxMatrix& aContextMatrix, DecodeFlags aFlags) { // aPaintServerSize is the size that would be filled when using // background-repeat:no-repeat and background-size:auto. For normal background // images, this would be the intrinsic size of the image; for gradients and @@ -1188,7 +1188,7 @@ already_AddRefed SVGIntegrationUtils::DrawableFromPaintServer( aPaintServerSize.height); overrideBounds.Scale(1.0 / aFrame->PresContext()->AppUnitsPerDevPixel()); uint32_t imgFlags = imgIContainer::FLAG_ASYNC_NOTIFY; - if (aFlags.contains(DecodeFlags::SyncDecodeImages)) { + if (aFlags.contains(DecodeFlag::SyncDecodeImages)) { imgFlags |= imgIContainer::FLAG_SYNC_DECODE; } imgDrawingParams imgParams(imgFlags); diff --git a/layout/svg/SVGIntegrationUtils.h b/layout/svg/SVGIntegrationUtils.h index acce583f2654a..954a23f0965cb 100644 --- a/layout/svg/SVGIntegrationUtils.h +++ b/layout/svg/SVGIntegrationUtils.h @@ -258,14 +258,15 @@ class SVGIntegrationUtils final { * @param aFlags pass SyncDecodeImages and any images in the paint * server will be decoding synchronously if they are not decoded already. */ - enum class DecodeFlags { + enum class DecodeFlag { SyncDecodeImages, }; + using DecodeFlags = EnumSet; static already_AddRefed DrawableFromPaintServer( nsIFrame* aFrame, nsIFrame* aTarget, const nsSize& aPaintServerSize, const gfx::IntSize& aRenderSize, const DrawTarget* aDrawTarget, - const gfxMatrix& aContextMatrix, EnumSet aFlags); + const gfxMatrix& aContextMatrix, DecodeFlags aFlags); /** * For non-SVG frames, this gives the offset to the frame's "user space". diff --git a/layout/svg/SVGMarkerFrame.cpp b/layout/svg/SVGMarkerFrame.cpp index 3415c50278596..7d0b22aff1410 100644 --- a/layout/svg/SVGMarkerFrame.cpp +++ b/layout/svg/SVGMarkerFrame.cpp @@ -134,7 +134,7 @@ void SVGMarkerFrame::PaintMark(gfxContext& aContext, ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid); // The CTM of each frame referencing us may be different. SVGFrame->NotifySVGChanged( - ISVGDisplayableFrame::ChangeFlags::TransformChanged); + ISVGDisplayableFrame::ChangeFlag::TransformChanged); auto contextPaint = MakeRefPtr(); contextPaint->Init(aContext.GetDrawTarget(), aToMarkedFrameUserSpace * aContext.CurrentMatrixDouble(), diff --git a/layout/svg/SVGMaskFrame.cpp b/layout/svg/SVGMaskFrame.cpp index 6408aa9c1b1e3..87791c3d360a7 100644 --- a/layout/svg/SVGMaskFrame.cpp +++ b/layout/svg/SVGMaskFrame.cpp @@ -90,7 +90,7 @@ already_AddRefed SVGMaskFrame::GetMaskForMaskedFrame( ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid); if (SVGFrame) { SVGFrame->NotifySVGChanged( - ISVGDisplayableFrame::ChangeFlags::TransformChanged); + ISVGDisplayableFrame::ChangeFlag::TransformChanged); m = SVGUtils::GetTransformMatrixInUserSpace(kid) * m; } diff --git a/layout/svg/SVGOuterSVGFrame.cpp b/layout/svg/SVGOuterSVGFrame.cpp index 74dc9d375bd2c..babab22284f46 100644 --- a/layout/svg/SVGOuterSVGFrame.cpp +++ b/layout/svg/SVGOuterSVGFrame.cpp @@ -374,7 +374,7 @@ void SVGOuterSVGFrame::Reflow(nsPresContext* aPresContext, nsPresContext::AppUnitsToFloatCSSPixels(aReflowInput.ComputedWidth()), nsPresContext::AppUnitsToFloatCSSPixels(aReflowInput.ComputedHeight())); - EnumSet changeBits; + ChangeFlags changeBits; if (newViewportSize != svgElem->GetViewportSize()) { // When our viewport size changes, we may need to update the overflow rects // of our child frames. This is the case if: @@ -403,14 +403,14 @@ void SVGOuterSVGFrame::Reflow(nsPresContext* aPresContext, child->MarkSubtreeDirty(); } } - changeBits += ChangeFlags::CoordContextChanged; + changeBits += ChangeFlag::CoordContextChanged; svgElem->SetViewportSize(newViewportSize); } if (mIsRootContent && !mIsInIframe) { const auto oldZoom = mFullZoom; mFullZoom = ComputeFullZoom(); if (oldZoom != mFullZoom) { - changeBits += ChangeFlags::FullZoomChanged; + changeBits += ChangeFlag::FullZoomChanged; } } if (!changeBits.isEmpty() && !HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { @@ -513,9 +513,9 @@ nsresult SVGOuterSVGFrame::AttributeChanged(int32_t aNameSpaceID, SVGUtils::NotifyChildrenOfSVGChange( PrincipalChildList().FirstChild(), aAttribute == nsGkAtoms::viewBox - ? EnumSet(ChangeFlags::TransformChanged, - ChangeFlags::CoordContextChanged) - : ChangeFlags::TransformChanged); + ? ChangeFlags(ChangeFlag::TransformChanged, + ChangeFlag::CoordContextChanged) + : ChangeFlag::TransformChanged); if (aAttribute != nsGkAtoms::transform) { static_cast(GetContent()) @@ -578,47 +578,48 @@ void SVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, //---------------------------------------------------------------------- // ISVGSVGFrame methods: -void SVGOuterSVGFrame::NotifyViewportOrTransformChanged( - EnumSet aFlags) { +void SVGOuterSVGFrame::NotifyViewportOrTransformChanged(ChangeFlags aFlags) { auto* content = static_cast(GetContent()); - if (aFlags.contains(ChangeFlags::CoordContextChanged)) { + if (aFlags.contains(ChangeFlag::CoordContextChanged)) { if (content->HasViewBox()) { // Percentage lengths on children resolve against the viewBox rect so we // don't need to notify them of the viewport change, but the viewBox // transform will have changed, so we need to notify them of that instead. - aFlags = ChangeFlags::TransformChanged; + aFlags = ChangeFlag::TransformChanged; } else if (content->ShouldSynthesizeViewBox()) { // In the case of a synthesized viewBox, the synthetic viewBox's rect // changes as the viewport changes. As a result we need to maintain the // COORD_CONTEXT_CHANGED flag. - aFlags += ChangeFlags::TransformChanged; + aFlags += ChangeFlag::TransformChanged; } else if (mCanvasTM && mCanvasTM->IsSingular()) { // A width/height of zero will result in us having a singular mCanvasTM // even when we don't have a viewBox. So we also want to recompute our // mCanvasTM for this width/height change even though we don't have a // viewBox. - aFlags += ChangeFlags::TransformChanged; + aFlags += ChangeFlag::TransformChanged; } } bool haveNonFullZoomTransformChange = - aFlags.contains(ChangeFlags::TransformChanged); + aFlags.contains(ChangeFlag::TransformChanged); - if (aFlags.contains(ChangeFlags::FullZoomChanged)) { + if (aFlags.contains(ChangeFlag::FullZoomChanged)) { // Convert FullZoomChanged to TransformChanged. - aFlags -= ChangeFlags::FullZoomChanged; - aFlags += ChangeFlags::TransformChanged; + aFlags -= ChangeFlag::FullZoomChanged; + aFlags += ChangeFlag::TransformChanged; } - if (aFlags.contains(ChangeFlags::TransformChanged)) { + if (aFlags.contains(ChangeFlag::TransformChanged)) { // Make sure our canvas transform matrix gets (lazily) recalculated: mCanvasTM = nullptr; if (haveNonFullZoomTransformChange && !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) { - uint32_t flags = HasAnyStateBits(NS_FRAME_IN_REFLOW) - ? SVGSVGElement::eDuringReflow - : 0; + SVGViewportElement::ChildrenOnlyTransformChangedFlags flags; + if (HasAnyStateBits(NS_FRAME_IN_REFLOW)) { + flags += + SVGViewportElement::ChildrenOnlyTransformChangedFlag::DuringReflow; + } content->ChildrenOnlyTransformChanged(flags); } } diff --git a/layout/svg/SVGOuterSVGFrame.h b/layout/svg/SVGOuterSVGFrame.h index ebeaf00ccad81..cc8debda72f23 100644 --- a/layout/svg/SVGOuterSVGFrame.h +++ b/layout/svg/SVGOuterSVGFrame.h @@ -99,7 +99,7 @@ class SVGOuterSVGFrame final : public SVGDisplayContainerFrame, void AppendDirectlyOwnedAnonBoxes(nsTArray& aResult) override; // ISVGSVGFrame interface: - void NotifyViewportOrTransformChanged(EnumSet aFlags) override; + void NotifyViewportOrTransformChanged(ChangeFlags aFlags) override; // ISVGDisplayableFrame methods: void PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform, diff --git a/layout/svg/SVGPatternFrame.cpp b/layout/svg/SVGPatternFrame.cpp index 9445b1a714509..14225463d6d8b 100644 --- a/layout/svg/SVGPatternFrame.cpp +++ b/layout/svg/SVGPatternFrame.cpp @@ -240,7 +240,7 @@ void SVGPatternFrame::PaintChildren(DrawTarget* aDrawTarget, ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid); if (SVGFrame) { SVGFrame->NotifySVGChanged( - ISVGDisplayableFrame::ChangeFlags::TransformChanged); + ISVGDisplayableFrame::ChangeFlag::TransformChanged); tm = SVGUtils::GetTransformMatrixInUserSpace(kid) * tm; } diff --git a/layout/svg/SVGTextFrame.cpp b/layout/svg/SVGTextFrame.cpp index 85a3f55f6c245..3757646936e23 100644 --- a/layout/svg/SVGTextFrame.cpp +++ b/layout/svg/SVGTextFrame.cpp @@ -3053,19 +3053,19 @@ void SVGTextFrame::FindCloserFrameForSelection( //---------------------------------------------------------------------- // ISVGDisplayableFrame methods -void SVGTextFrame::NotifySVGChanged(EnumSet aFlags) { - MOZ_ASSERT(aFlags.contains(ChangeFlags::TransformChanged) || - aFlags.contains(ChangeFlags::CoordContextChanged), +void SVGTextFrame::NotifySVGChanged(ChangeFlags aFlags) { + MOZ_ASSERT(aFlags.contains(ChangeFlag::TransformChanged) || + aFlags.contains(ChangeFlag::CoordContextChanged), "Invalidation logic may need adjusting"); bool needNewBounds = false; bool needGlyphMetricsUpdate = false; - if (aFlags.contains(ChangeFlags::CoordContextChanged) && + if (aFlags.contains(ChangeFlag::CoordContextChanged) && HasAnyStateBits(NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES)) { needGlyphMetricsUpdate = true; } - if (aFlags.contains(ChangeFlags::TransformChanged)) { + if (aFlags.contains(ChangeFlag::TransformChanged)) { if (mCanvasTM && mCanvasTM->IsSingular()) { // We won't have calculated the glyph positions correctly. needNewBounds = true; diff --git a/layout/svg/SVGTextFrame.h b/layout/svg/SVGTextFrame.h index e4af3bea6dbea..aa5d54c55fbf0 100644 --- a/layout/svg/SVGTextFrame.h +++ b/layout/svg/SVGTextFrame.h @@ -226,7 +226,7 @@ class SVGTextFrame final : public SVGDisplayContainerFrame { const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) override; // ISVGDisplayableFrame interface: - void NotifySVGChanged(EnumSet aFlags) override; + void NotifySVGChanged(ChangeFlags aFlags) override; void PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform, imgDrawingParams& aImgParams) override; nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override; diff --git a/layout/svg/SVGUseFrame.cpp b/layout/svg/SVGUseFrame.cpp index 799b738a0db5d..6ce7e7a2e5c8b 100644 --- a/layout/svg/SVGUseFrame.cpp +++ b/layout/svg/SVGUseFrame.cpp @@ -55,7 +55,7 @@ void SVGUseFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) { // make sure our cached transform matrix gets (lazily) updated mCanvasTM = nullptr; SVGUtils::ScheduleReflowSVG(this); - SVGUtils::NotifyChildrenOfSVGChange(this, ChangeFlags::TransformChanged); + SVGUtils::NotifyChildrenOfSVGChange(this, ChangeFlag::TransformChanged); } } @@ -105,13 +105,13 @@ void SVGUseFrame::ReflowSVG() { SVGGFrame::ReflowSVG(); } -void SVGUseFrame::NotifySVGChanged(EnumSet aFlags) { - if (aFlags.contains(ChangeFlags::CoordContextChanged) && - !aFlags.contains(ChangeFlags::TransformChanged)) { +void SVGUseFrame::NotifySVGChanged(ChangeFlags aFlags) { + if (aFlags.contains(ChangeFlag::CoordContextChanged) && + !aFlags.contains(ChangeFlag::TransformChanged)) { // Coordinate context changes affect mCanvasTM if we have a // percentage 'x' or 'y' if (StyleSVGReset()->mX.HasPercent() || StyleSVGReset()->mY.HasPercent()) { - aFlags += ChangeFlags::TransformChanged; + aFlags += ChangeFlag::TransformChanged; // Ancestor changes can't affect how we render from the perspective of // any rendering observers that we may have, so we don't need to // invalidate them. We also don't need to invalidate ourself, since our diff --git a/layout/svg/SVGUseFrame.h b/layout/svg/SVGUseFrame.h index d961634e1f777..919b29ff33440 100644 --- a/layout/svg/SVGUseFrame.h +++ b/layout/svg/SVGUseFrame.h @@ -51,7 +51,7 @@ class SVGUseFrame final : public SVGGFrame { // ISVGDisplayableFrame interface: void ReflowSVG() override; - void NotifySVGChanged(EnumSet aFlags) override; + void NotifySVGChanged(ChangeFlags aFlags) override; private: bool mHasValidDimensions; diff --git a/layout/svg/SVGUtils.cpp b/layout/svg/SVGUtils.cpp index 87761628c1ece..45e0f4a3779a2 100644 --- a/layout/svg/SVGUtils.cpp +++ b/layout/svg/SVGUtils.cpp @@ -361,7 +361,7 @@ bool SVGUtils::GetParentSVGTransforms(const nsIFrame* aFrame, } void SVGUtils::NotifyChildrenOfSVGChange( - nsIFrame* aFrame, EnumSet aFlags) { + nsIFrame* aFrame, ISVGDisplayableFrame::ChangeFlags aFlags) { for (nsIFrame* kid : aFrame->PrincipalChildList()) { ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid); if (SVGFrame) { diff --git a/layout/svg/SVGUtils.h b/layout/svg/SVGUtils.h index ec9c7dbd96da4..0af16c9f7530a 100644 --- a/layout/svg/SVGUtils.h +++ b/layout/svg/SVGUtils.h @@ -262,7 +262,7 @@ class SVGUtils final { * that might affect them. */ static void NotifyChildrenOfSVGChange( - nsIFrame* aFrame, EnumSet aFlags); + nsIFrame* aFrame, ISVGDisplayableFrame::ChangeFlags aFlags); /* * Convert a surface size to an integer for use by thebes diff --git a/layout/svg/SVGViewportFrame.cpp b/layout/svg/SVGViewportFrame.cpp index e9ef0f4cc5c88..fb9ab02c57058 100644 --- a/layout/svg/SVGViewportFrame.cpp +++ b/layout/svg/SVGViewportFrame.cpp @@ -73,12 +73,12 @@ void SVGViewportFrame::ReflowSVG() { SVGDisplayContainerFrame::ReflowSVG(); } -void SVGViewportFrame::NotifySVGChanged(EnumSet aFlags) { - MOZ_ASSERT(aFlags.contains(ChangeFlags::TransformChanged) || - aFlags.contains(ChangeFlags::CoordContextChanged), +void SVGViewportFrame::NotifySVGChanged(ChangeFlags aFlags) { + MOZ_ASSERT(aFlags.contains(ChangeFlag::TransformChanged) || + aFlags.contains(ChangeFlag::CoordContextChanged), "Invalidation logic may need adjusting"); - if (aFlags.contains(ChangeFlags::CoordContextChanged)) { + if (aFlags.contains(ChangeFlag::CoordContextChanged)) { SVGViewportElement* svg = static_cast(GetContent()); bool xOrYIsPercentage = @@ -102,17 +102,17 @@ void SVGViewportFrame::NotifySVGChanged(EnumSet aFlags) { // percentage 'x' or 'y', or if we have a percentage 'width' or 'height' AND // a 'viewBox'. - if (!aFlags.contains(ChangeFlags::TransformChanged) && + if (!aFlags.contains(ChangeFlag::TransformChanged) && (xOrYIsPercentage || (widthOrHeightIsPercentage && svg->HasViewBox()))) { - aFlags += ChangeFlags::TransformChanged; + aFlags += ChangeFlag::TransformChanged; } if (svg->HasViewBox() || !widthOrHeightIsPercentage) { // Remove COORD_CONTEXT_CHANGED, since we establish the coordinate // context for our descendants and this notification won't change its // dimensions: - aFlags -= ChangeFlags::CoordContextChanged; + aFlags -= ChangeFlag::CoordContextChanged; if (aFlags.isEmpty()) { return; // No notification flags left @@ -181,13 +181,12 @@ nsresult SVGViewportFrame::AttributeChanged(int32_t aNameSpaceID, // make sure our cached transform matrix gets (lazily) updated mCanvasTM = nullptr; content->ChildrenOnlyTransformChanged(); - SVGUtils::NotifyChildrenOfSVGChange(this, - ChangeFlags::TransformChanged); + SVGUtils::NotifyChildrenOfSVGChange(this, ChangeFlag::TransformChanged); } else { - EnumSet flags(ChangeFlags::CoordContextChanged); + ChangeFlags flags(ChangeFlag::CoordContextChanged); if (mCanvasTM && mCanvasTM->IsSingular()) { mCanvasTM = nullptr; - flags += ChangeFlags::TransformChanged; + flags += ChangeFlag::TransformChanged; } SVGUtils::NotifyChildrenOfSVGChange(this, flags); } @@ -200,9 +199,9 @@ nsresult SVGViewportFrame::AttributeChanged(int32_t aNameSpaceID, SVGUtils::NotifyChildrenOfSVGChange( this, aAttribute == nsGkAtoms::viewBox - ? EnumSet(ChangeFlags::TransformChanged, - ChangeFlags::CoordContextChanged) - : ChangeFlags::TransformChanged); + ? ChangeFlags(ChangeFlag::TransformChanged, + ChangeFlag::CoordContextChanged) + : ChangeFlag::TransformChanged); if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y) { nsLayoutUtils::PostRestyleEvent( @@ -232,8 +231,7 @@ nsIFrame* SVGViewportFrame::GetFrameForPoint(const gfxPoint& aPoint) { //---------------------------------------------------------------------- // ISVGSVGFrame methods: -void SVGViewportFrame::NotifyViewportOrTransformChanged( - EnumSet aFlags) { +void SVGViewportFrame::NotifyViewportOrTransformChanged(ChangeFlags aFlags) { // The dimensions of inner- frames are purely defined by their "width" // and "height" attributes, and transform changes can only occur as a result // of changes to their "width", "height", "viewBox" or "preserveAspectRatio" diff --git a/layout/svg/SVGViewportFrame.h b/layout/svg/SVGViewportFrame.h index 90c7f36fb6a42..4e1c359fe4176 100644 --- a/layout/svg/SVGViewportFrame.h +++ b/layout/svg/SVGViewportFrame.h @@ -33,7 +33,7 @@ class SVGViewportFrame : public SVGDisplayContainerFrame, public ISVGSVGFrame { void PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform, imgDrawingParams& aImgParams) override; void ReflowSVG() override; - void NotifySVGChanged(EnumSet aFlags) override; + void NotifySVGChanged(ChangeFlags aFlags) override; SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace, uint32_t aFlags) override; nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override; @@ -42,7 +42,7 @@ class SVGViewportFrame : public SVGDisplayContainerFrame, public ISVGSVGFrame { bool HasChildrenOnlyTransform(Matrix* aTransform) const override; // ISVGSVGFrame interface: - void NotifyViewportOrTransformChanged(EnumSet aFlags) override; + void NotifyViewportOrTransformChanged(ChangeFlags aFlags) override; }; } // namespace mozilla diff --git a/layout/tools/reftest/reftest/__init__.py b/layout/tools/reftest/reftest/__init__.py index 8fbac7c3d3986..02b828ac2f9a6 100644 --- a/layout/tools/reftest/reftest/__init__.py +++ b/layout/tools/reftest/reftest/__init__.py @@ -125,9 +125,9 @@ def add_test(file, annotations, referenced_test=None, skip_if=""): for annotation in annotations: key, condition = self.translate_condition_for_mozinfo(annotation) - test_dict[key] = "\n".join( - [t for t in [test_dict.get(key, ""), condition] if t] - ) + test_dict[key] = "\n".join([ + t for t in [test_dict.get(key, ""), condition] if t + ]) self.tests.append(test_dict) diff --git a/layout/tools/reftest/reftestcommandline.py b/layout/tools/reftest/reftestcommandline.py index 72c9316e31a80..40e4f1efc2e68 100644 --- a/layout/tools/reftest/reftestcommandline.py +++ b/layout/tools/reftest/reftestcommandline.py @@ -152,8 +152,7 @@ def __init__(self, **kwargs): dest="ignoreWindowSize", action="store_true", default=False, - help="ignore the window size, which may cause spurious " - "failures and passes", + help="ignore the window size, which may cause spurious failures and passes", ) self.add_argument( @@ -188,7 +187,7 @@ def __init__(self, **kwargs): default=[], dest="environment", metavar="NAME=VALUE", - help="sets the given variable in the application's " "environment", + help="sets the given variable in the application's environment", ) self.add_argument( @@ -368,13 +367,11 @@ def get_ip(self): return moznetwork.get_ip() def set_default_suite(self, options): - manifests = OrderedDict( - [ - ("reftest.list", "reftest"), - ("crashtests.list", "crashtest"), - ("jstests.list", "jstestbrowser"), - ] - ) + manifests = OrderedDict([ + ("reftest.list", "reftest"), + ("crashtests.list", "crashtest"), + ("jstests.list", "jstestbrowser"), + ]) for test_path in options.tests: file_name = os.path.basename(test_path) diff --git a/layout/tools/reftest/remotereftest.py b/layout/tools/reftest/remotereftest.py index d653e1f57776a..6d08eb7c9361c 100644 --- a/layout/tools/reftest/remotereftest.py +++ b/layout/tools/reftest/remotereftest.py @@ -329,7 +329,7 @@ def createReftestProfile(self, options, **kwargs): options, server=options.remoteWebServer, port=options.httpPort, - **kwargs + **kwargs, ) profileDir = profile.profile prefs = {} @@ -407,7 +407,7 @@ def runApp( valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None, - **profileArgs + **profileArgs, ): if cmdargs is None: cmdargs = [] diff --git a/layout/tools/reftest/runreftest.py b/layout/tools/reftest/runreftest.py index d25bfc35d0629..7f94282cbf08d 100644 --- a/layout/tools/reftest/runreftest.py +++ b/layout/tools/reftest/runreftest.py @@ -5,6 +5,7 @@ """ Runs the reftest test harness. """ + import json import os import platform @@ -763,12 +764,10 @@ def runTests(self, tests, options, cmdargs=None): print("REFTEST INFO | Result summary:") for summaryObj, (text, categories) in zip(summaryObjects, summaryLines): - details = ", ".join( - [ - "%d %s" % (summaryObj[attribute], description) - for (attribute, description) in categories - ] - ) + details = ", ".join([ + "%d %s" % (summaryObj[attribute], description) + for (attribute, description) in categories + ]) print( "REFTEST INFO | " + text diff --git a/layout/tools/reftest/selftest/conftest.py b/layout/tools/reftest/selftest/conftest.py index f29fdbb003e1b..48fe0447560ef 100644 --- a/layout/tools/reftest/selftest/conftest.py +++ b/layout/tools/reftest/selftest/conftest.py @@ -51,40 +51,32 @@ def get_reftest(setup_test_harness, binary, parser): build = parser.build_obj options = vars(parser.parse_args([])) - options.update( - { - "app": binary, - "focusFilterMode": "non-needs-focus", - "suite": "reftest", - } - ) + options.update({ + "app": binary, + "focusFilterMode": "non-needs-focus", + "suite": "reftest", + }) if not os.path.isdir(build.bindir): package_root = os.path.dirname(harness_root) - options.update( - { - "extraProfileFiles": [os.path.join(package_root, "bin", "plugins")], - "reftestExtensionPath": os.path.join(harness_root, "reftest"), - "sandboxReadWhitelist": [here, os.environ["PYTHON_TEST_TMP"]], - "utilityPath": os.path.join(package_root, "bin"), - "specialPowersExtensionPath": os.path.join( - harness_root, "specialpowers" - ), - } - ) + options.update({ + "extraProfileFiles": [os.path.join(package_root, "bin", "plugins")], + "reftestExtensionPath": os.path.join(harness_root, "reftest"), + "sandboxReadWhitelist": [here, os.environ["PYTHON_TEST_TMP"]], + "utilityPath": os.path.join(package_root, "bin"), + "specialPowersExtensionPath": os.path.join(harness_root, "specialpowers"), + }) if "MOZ_FETCHES_DIR" in os.environ: options["sandboxReadWhitelist"].append(os.environ["MOZ_FETCHES_DIR"]) else: - options.update( - { - "extraProfileFiles": [os.path.join(build.topobjdir, "dist", "plugins")], - "sandboxReadWhitelist": [build.topobjdir, build.topsrcdir], - "specialPowersExtensionPath": os.path.join( - build.distdir, "xpi-stage", "specialpowers" - ), - } - ) + options.update({ + "extraProfileFiles": [os.path.join(build.topobjdir, "dist", "plugins")], + "sandboxReadWhitelist": [build.topobjdir, build.topsrcdir], + "specialPowersExtensionPath": os.path.join( + build.distdir, "xpi-stage", "specialpowers" + ), + }) def inner(**opts): options.update(opts) diff --git a/memory/replace/dmd/block_analyzer.py b/memory/replace/dmd/block_analyzer.py index 25b42054d6dd8..b4ddb09c925e0 100644 --- a/memory/replace/dmd/block_analyzer.py +++ b/memory/replace/dmd/block_analyzer.py @@ -117,8 +117,7 @@ def range_1_24(string): "-c", "--chain-reports", action="store_true", - help="if only one block is found to hold onto the object, report " - "the next one, too", + help="if only one block is found to hold onto the object, report the next one, too", ) diff --git a/mobile/android/android-components/automation/publish_to_maven_local_if_modified.py b/mobile/android/android-components/automation/publish_to_maven_local_if_modified.py index 23dde50635c9c..cd296b4e26702 100755 --- a/mobile/android/android-components/automation/publish_to_maven_local_if_modified.py +++ b/mobile/android/android-components/automation/publish_to_maven_local_if_modified.py @@ -124,9 +124,11 @@ def find_project_root(): shell=True, ) else: - run_cmd_checked( - ["./gradlew", "publishToMavenLocal", f"-Plocal={time.time_ns()}"] - ) + run_cmd_checked([ + "./gradlew", + "publishToMavenLocal", + f"-Plocal={time.time_ns()}", + ]) with open(LAST_CONTENTS_HASH_FILE, "w") as f: f.write(contents_hash) f.write("\n") diff --git a/mobile/android/android-components/components/browser/errorpages/src/main/res/values-sl/strings.xml b/mobile/android/android-components/components/browser/errorpages/src/main/res/values-sl/strings.xml index 1efdea03cc42a..f77fc9f338b0e 100644 --- a/mobile/android/android-components/components/browser/errorpages/src/main/res/values-sl/strings.xml +++ b/mobile/android/android-components/components/browser/errorpages/src/main/res/values-sl/strings.xml @@ -137,6 +137,8 @@ <p>Spletna stran na naslovu %1$s je bila prijavljena kot zavajajoča in je bila zavrnjena na podlagi vaših varnostnih nastavitev.</p> Stran je zaradi vaše varnosti blokirana + + <p>Ta spletna stran na %1$s je bila blokirana, ker jo je poskušal odpreti eden izmed vaših dodatkov. Na tem spletnem mestu bi lahko ukradli vaše podatke, kot so gesla ali podatki o bančnem računu.</p> Varno spletno mesto ni na voljo diff --git a/mobile/android/android-components/components/browser/toolbar/src/main/res/values-bqi/strings.xml b/mobile/android/android-components/components/browser/toolbar/src/main/res/values-bqi/strings.xml index eddbb55de5208..f1e9915c729c6 100644 --- a/mobile/android/android-components/components/browser/toolbar/src/main/res/values-bqi/strings.xml +++ b/mobile/android/android-components/components/browser/toolbar/src/main/res/values-bqi/strings.xml @@ -20,7 +20,7 @@ دووسمندیا سایت - هونی بار اکونه + هونی بار اونه ی قرد ز موئتوا و دست سامووا پشک خوتکار مسدۊد وابین diff --git a/mobile/android/android-components/components/compose/base/src/main/java/mozilla/components/compose/base/annotation/FlexibleWindowLightDarkPreview.kt b/mobile/android/android-components/components/compose/base/src/main/java/mozilla/components/compose/base/annotation/FlexibleWindowLightDarkPreview.kt index 53cb981d6905f..9546d11280014 100644 --- a/mobile/android/android-components/components/compose/base/src/main/java/mozilla/components/compose/base/annotation/FlexibleWindowLightDarkPreview.kt +++ b/mobile/android/android-components/components/compose/base/src/main/java/mozilla/components/compose/base/annotation/FlexibleWindowLightDarkPreview.kt @@ -9,9 +9,6 @@ import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import mozilla.components.compose.base.theme.layout.AcornWindowSize -private const val SMALL_WINDOW_WIDTH = 400 -private const val MEDIUM_WINDOW_WIDTH = 700 - /** * A wrapper annotation for creating a preview that renders a preview for each * combination of [AcornWindowSize] and Light/Dark theme. @@ -54,11 +51,13 @@ private const val MEDIUM_WINDOW_WIDTH = 700 ) @Preview( name = "Large Window Light Landscape", + widthDp = LARGE_WINDOW_WIDTH, device = Devices.PIXEL_TABLET, uiMode = Configuration.UI_MODE_NIGHT_NO, ) @Preview( name = "Large Window Dark Landscape", + widthDp = LARGE_WINDOW_WIDTH, device = Devices.PIXEL_TABLET, uiMode = Configuration.UI_MODE_NIGHT_YES, ) diff --git a/mobile/android/android-components/components/compose/base/src/main/java/mozilla/components/compose/base/annotation/FlexibleWindowPreview.kt b/mobile/android/android-components/components/compose/base/src/main/java/mozilla/components/compose/base/annotation/FlexibleWindowPreview.kt index d918e8c5840bf..07495565f11f7 100644 --- a/mobile/android/android-components/components/compose/base/src/main/java/mozilla/components/compose/base/annotation/FlexibleWindowPreview.kt +++ b/mobile/android/android-components/components/compose/base/src/main/java/mozilla/components/compose/base/annotation/FlexibleWindowPreview.kt @@ -15,16 +15,21 @@ import mozilla.components.compose.base.theme.layout.AcornWindowSize // to work properly. See: https://issuetracker.google.com/issues/300116108#comment1 @Preview( name = "Small Window", - widthDp = 400, + widthDp = SMALL_WINDOW_WIDTH, +) +@Preview( + name = "Small Window Landscape", + heightDp = SMALL_WINDOW_WIDTH, + widthDp = SMALL_WINDOW_WIDTH * 2, ) @Preview( name = "Medium Window", - widthDp = 700, + widthDp = MEDIUM_WINDOW_WIDTH, device = Devices.NEXUS_7, ) @Preview( name = "Large Window", - widthDp = 1000, - device = Devices.AUTOMOTIVE_1024p, + widthDp = LARGE_WINDOW_WIDTH, + device = Devices.PIXEL_TABLET, ) annotation class FlexibleWindowPreview diff --git a/mobile/android/android-components/components/compose/base/src/main/java/mozilla/components/compose/base/annotation/FlexibleWindowPreviewSizes.kt b/mobile/android/android-components/components/compose/base/src/main/java/mozilla/components/compose/base/annotation/FlexibleWindowPreviewSizes.kt new file mode 100644 index 0000000000000..1cf41aee5f3ff --- /dev/null +++ b/mobile/android/android-components/components/compose/base/src/main/java/mozilla/components/compose/base/annotation/FlexibleWindowPreviewSizes.kt @@ -0,0 +1,9 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.compose.base.annotation + +internal const val SMALL_WINDOW_WIDTH = 400 +internal const val MEDIUM_WINDOW_WIDTH = 700 +internal const val LARGE_WINDOW_WIDTH = 1000 diff --git a/mobile/android/android-components/components/compose/base/src/main/res/values-bqi/strings.xml b/mobile/android/android-components/components/compose/base/src/main/res/values-bqi/strings.xml index ebd638e55737e..98952f09977c5 100644 --- a/mobile/android/android-components/components/compose/base/src/main/res/values-bqi/strings.xml +++ b/mobile/android/android-components/components/compose/base/src/main/res/values-bqi/strings.xml @@ -16,7 +16,7 @@ پاک کردن هؽل - هونی بار اکونه + هونی بار اونه رڌ کردن پیوم diff --git a/mobile/android/android-components/components/compose/browser-toolbar/src/main/res/values-en-rCA/strings.xml b/mobile/android/android-components/components/compose/browser-toolbar/src/main/res/values-en-rCA/strings.xml index c1c7ff35ad5d2..e2769a91610d0 100644 --- a/mobile/android/android-components/components/compose/browser-toolbar/src/main/res/values-en-rCA/strings.xml +++ b/mobile/android/android-components/components/compose/browser-toolbar/src/main/res/values-en-rCA/strings.xml @@ -21,4 +21,6 @@ Designed for Android. Refined for You We’ve refreshed the design with a sleek, modern feel + + Entering text in %1$s. Double tap to stop diff --git a/mobile/android/android-components/components/feature/contextmenu/src/main/res/values-en-rCA/strings.xml b/mobile/android/android-components/components/feature/contextmenu/src/main/res/values-en-rCA/strings.xml index fe530ad3c5bd9..333638254365a 100644 --- a/mobile/android/android-components/components/feature/contextmenu/src/main/res/values-en-rCA/strings.xml +++ b/mobile/android/android-components/components/feature/contextmenu/src/main/res/values-en-rCA/strings.xml @@ -21,6 +21,8 @@ Share image Copy link + + Copy link text Copy image location @@ -35,6 +37,8 @@ New private tab opened Link copied to clipboard + + Link text copied to clipboard Switch diff --git a/mobile/android/android-components/components/feature/sitepermissions/src/main/res/values-sl/strings.xml b/mobile/android/android-components/components/feature/sitepermissions/src/main/res/values-sl/strings.xml index 151f8849d320f..906dafd42f67d 100644 --- a/mobile/android/android-components/components/feature/sitepermissions/src/main/res/values-sl/strings.xml +++ b/mobile/android/android-components/components/feature/sitepermissions/src/main/res/values-sl/strings.xml @@ -51,6 +51,8 @@ this button will not give access to this permission --> Zavrni + + Za prejemanje obvestil tega spletnega mesta jih boste morali dovoliti v %1$su. Prekliči diff --git a/mobile/android/android-components/plugins/dependencies/src/main/java/ApplicationServices.kt b/mobile/android/android-components/plugins/dependencies/src/main/java/ApplicationServices.kt index 5615ba54d945f..0f6424ec64b73 100644 --- a/mobile/android/android-components/plugins/dependencies/src/main/java/ApplicationServices.kt +++ b/mobile/android/android-components/plugins/dependencies/src/main/java/ApplicationServices.kt @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // These lines are generated by android-components/automation/application-services-nightly-bump.py -val VERSION = "148.20260108050205" +val VERSION = "148.20260110050246" val CHANNEL = ApplicationServicesChannel.NIGHTLY object ApplicationServicesConfig { diff --git a/mobile/android/android-components/plugins/dependencies/src/main/java/appservices_version_bump.py b/mobile/android/android-components/plugins/dependencies/src/main/java/appservices_version_bump.py index eaf0a523cebe5..d3b3f9276bf31 100755 --- a/mobile/android/android-components/plugins/dependencies/src/main/java/appservices_version_bump.py +++ b/mobile/android/android-components/plugins/dependencies/src/main/java/appservices_version_bump.py @@ -167,8 +167,7 @@ def update_application_services(revision): json = get_as_nightly_json(f"revision.{revision}") target_as_version = json["version"] log.info( - f"Target app-services {as_channel.capitalize()} version " - f"is {target_as_version}" + f"Target app-services {as_channel.capitalize()} version is {target_as_version}" ) if compare_as_versions(current_as_version, target_as_version) >= 0: diff --git a/mobile/android/android-components/plugins/dependencies/src/main/java/moz.yaml b/mobile/android/android-components/plugins/dependencies/src/main/java/moz.yaml index bdda8c54424a2..5a8166a4925e4 100644 --- a/mobile/android/android-components/plugins/dependencies/src/main/java/moz.yaml +++ b/mobile/android/android-components/plugins/dependencies/src/main/java/moz.yaml @@ -31,11 +31,11 @@ origin: # Human-readable identifier for this version/release # Generally "version NNN", "tag SSS", "bookmark SSS" - release: aba6c68a54b6670ac3f6794a183d28cb67b16a3a (2026-01-08T05:02:05). + release: 32693fef638471a3667869ba0c0b7a8bb52c15e3 (2026-01-10T05:02:46). # Revision to pull in # Must be a long or short commit SHA (long preferred) - revision: aba6c68a54b6670ac3f6794a183d28cb67b16a3a + revision: 32693fef638471a3667869ba0c0b7a8bb52c15e3 # The package's license, where possible using the mnemonic from # https://spdx.org/licenses/ diff --git a/mobile/android/fenix/app/build.gradle b/mobile/android/fenix/app/build.gradle index 4594b757b6282..1b27020a20ee7 100644 --- a/mobile/android/fenix/app/build.gradle +++ b/mobile/android/fenix/app/build.gradle @@ -542,15 +542,6 @@ nimbus { } dependencies { - // We pick up JNA transitively by way of Glean, which is currently on version 5.14.0. - // However, we need to force version 5.17.0 due to Google Play's 16KB page size requirement. - // JNA 5.15.0+ crashes on Android <7, however, so it can't be bumped in Glean at this time. - // Therefore, manually force the use of version 5.17.0 at the app level since we only support - // running on Android 8+ now anyway. This can be removed once Glean updates. - constraints { - implementation(libs.jna) - } - implementation project(':components:browser-engine-gecko') implementation project(':components:compose-awesomebar') implementation project(':components:compose-base') diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/generate_smoke_tests.py b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/generate_smoke_tests.py index 60c9b7920177a..a598ef0f7bec7 100644 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/generate_smoke_tests.py +++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/experimentintegration/generate_smoke_tests.py @@ -42,7 +42,7 @@ def search_for_smoke_tests(tests_name): locations.append(count) for location in locations: - test_names.append(f"{class_name}#{code[location+3].strip('()')}") + test_names.append(f"{class_name}#{code[location + 3].strip('()')}") return test_names diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/syncintegration/conftest.py b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/syncintegration/conftest.py index 63ea600f0aa16..c1eedf7b86138 100644 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/syncintegration/conftest.py +++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/syncintegration/conftest.py @@ -156,11 +156,11 @@ def gradlewbuild(fxa_account, monkeypatch, gradlewbuild_log): def pytest_addoption(parser): parser.addoption( "--firefox", - help="path to firefox binary (defaults to " "downloading latest nightly build)", + help="path to firefox binary (defaults to downloading latest nightly build)", ) parser.addoption( "--tps", - help="path to tps add-on (defaults to " "downloading latest nightly build)", + help="path to tps add-on (defaults to downloading latest nightly build)", ) diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/syncintegration/tps.py b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/syncintegration/tps.py index 1ea224dae1398..93dd8880f1a2f 100644 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/syncintegration/tps.py +++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/syncintegration/tps.py @@ -21,13 +21,11 @@ def _log(self, line): self.firefox_log.write(line + "\n") def run(self, test, phase="phase1", ignore_unused_engines=True): - self.profile.set_preferences( - { - "testing.tps.testFile": os.path.abspath(test), - "testing.tps.testPhase": phase, - "testing.tps.ignoreUnusedEngines": ignore_unused_engines, - } - ) + self.profile.set_preferences({ + "testing.tps.testFile": os.path.abspath(test), + "testing.tps.testPhase": phase, + "testing.tps.ignoreUnusedEngines": ignore_unused_engines, + }) args = ["-marionette"] process_args = {"processOutputLine": [self._log]} self.logger.info("Running: {} {}".format(self.firefox, " ".join(args))) diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/MainMenuTest.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/MainMenuTest.kt index 716a6f0b567ff..04ea41cda88a9 100644 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/MainMenuTest.kt +++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/MainMenuTest.kt @@ -771,7 +771,6 @@ class MainMenuTest : TestSetup() { } // TestRail link: https://mozilla.testrail.io/index.php?/cases/view/3080162 - @Ignore("Failing, see: https://bugzilla.mozilla.org/show_bug.cgi?id=2002573") @SmokeTest @Test fun verifyTheExtensionMenuListWhileExtensionsAreDisabledTest() { diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt index 74e9e2b9daaab..df15c6c2259d8 100644 --- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt +++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt @@ -46,6 +46,7 @@ import org.mozilla.fenix.helpers.MatcherHelper.assertUIObjectExists import org.mozilla.fenix.helpers.MatcherHelper.itemContainingText import org.mozilla.fenix.helpers.MatcherHelper.itemWithDescription import org.mozilla.fenix.helpers.MatcherHelper.itemWithResId +import org.mozilla.fenix.helpers.MatcherHelper.itemWithResIdAndDescription import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime import org.mozilla.fenix.helpers.TestAssetHelper.waitingTimeLong import org.mozilla.fenix.helpers.TestAssetHelper.waitingTimeShort @@ -376,16 +377,10 @@ class ThreeDotMenuMainRobot(private val composeTestRule: ComposeTestRule) { @OptIn(ExperimentalTestApi::class) fun verifyExtensionsButtonWithInstalledExtension(extensionTitle: String) { - Log.i(TAG, "verifyExtensionsButtonWithInstalledExtension: Waiting for $waitingTime for the collapsed \"Extensions\" button with installed $extensionTitle to exist.") - composeTestRule.waitUntilAtLeastOneExists(hasContentDescription(extensionTitle, substring = true, ignoreCase = true), waitingTime) - Log.i(TAG, "verifyExtensionsButtonWithInstalledExtension: Waited for $waitingTime for the collapsed \"Extensions\" button with installed $extensionTitle to exist.") - Log.i(TAG, "verifyExtensionsButtonWithInstalledExtension: Trying to verify that the collapsed \"Extensions\" button with installed $extensionTitle exists.") - composeTestRule.onNode( - hasTestTag("mainMenu.extensions"), - ).assert( - hasContentDescription(extensionTitle, substring = true, ignoreCase = true), - ).assertIsDisplayed() - Log.i(TAG, "verifyExtensionsButtonWithInstalledExtension: Verified that the collapsed \"Extensions\" button with installed $extensionTitle exists.") + Log.i(TAG, "verifyExtensionsButtonWithInstalledExtension: Waiting for the compose test rule to be idle.") + composeTestRule.waitForIdle() + Log.i(TAG, "verifyExtensionsButtonWithInstalledExtension: Waited for the compose test rule to be idle.") + assertUIObjectExists(itemWithResIdAndDescription("mainMenu.extensions", extensionTitle)) } @OptIn(ExperimentalTestApi::class) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/android/FenixDialogFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/android/FenixDialogFragment.kt index 23d9f037445fd..dc04818853fd3 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/android/FenixDialogFragment.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/android/FenixDialogFragment.kt @@ -19,7 +19,6 @@ import androidx.core.graphics.drawable.toDrawable import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import mozilla.components.concept.base.crash.Breadcrumb -import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.ext.components import com.google.android.material.R as materialR @@ -74,8 +73,8 @@ abstract class FenixDialogFragment : AppCompatDialogFragment() { fun inflateRootView(container: ViewGroup? = null): View { val contextThemeWrapper = ContextThemeWrapper( - activity, - (activity as HomeActivity).themeManager.currentThemeResource, + requireContext(), + requireActivity().theme, ) return LayoutInflater.from(contextThemeWrapper).inflate( layoutId, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/CustomTabMenu.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/CustomTabMenu.kt index 75ed53f838304..16453881d2ae5 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/CustomTabMenu.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/CustomTabMenu.kt @@ -30,7 +30,7 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTag import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.browser.state.state.CustomTabMenuItem import mozilla.components.feature.addons.Addon @@ -41,6 +41,7 @@ import org.mozilla.fenix.components.menu.MenuDialogTestTag.DESKTOP_SITE_ON import org.mozilla.fenix.components.menu.store.WebExtensionMenuItem import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -306,10 +307,12 @@ private fun PoweredByFirefoxItem(modifier: Modifier = Modifier) { } } -@PreviewLightDark +@Preview @Composable -private fun CustomTabMenuPreview() { - FirefoxTheme { +private fun CustomTabMenuPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Column( modifier = Modifier .background(color = MaterialTheme.colorScheme.surface) @@ -350,8 +353,10 @@ private fun CustomTabMenuPreview() { @Preview @Composable -private fun CustomTabMenuPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun CustomTabMenuDisabledButtonsPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Column( modifier = Modifier .background(color = MaterialTheme.colorScheme.surface) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/MenuBanner.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/MenuBanner.kt index 9ae826289bd2e..7f605d9871902 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/MenuBanner.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/MenuBanner.kt @@ -31,13 +31,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import mozilla.components.compose.base.theme.surfaceDimVariant import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR private val ROUNDED_CORNER_SHAPE = RoundedCornerShape(28.dp) @@ -127,24 +128,12 @@ fun MenuBanner( } } -@PreviewLightDark -@Composable -private fun MenuBannerPreview() { - FirefoxTheme { - MenuBanner( - onDismiss = {}, - onClick = {}, - modifier = Modifier - .background(color = MaterialTheme.colorScheme.surface) - .padding(all = FirefoxTheme.layout.space.static200), - ) - } -} - @Preview @Composable -private fun MenuBannerPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun MenuBannerPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { MenuBanner( onDismiss = {}, onClick = {}, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/MenuNavigation.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/MenuNavigation.kt index 7e9eb8d9e0160..a5e5381af75b4 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/MenuNavigation.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/MenuNavigation.kt @@ -33,12 +33,12 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.Hyphens import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR @Suppress("LongParameterList") @@ -186,10 +186,12 @@ private fun getIconTint(state: MenuItemState): Color { } } -@PreviewLightDark +@Preview @Composable -private fun MenuNavigationPreview() { - FirefoxTheme { +private fun MenuNavigationPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { MenuNavigation( isSiteLoading = false, isExtensionsExpanded = false, @@ -203,10 +205,12 @@ private fun MenuNavigationPreview() { } } -@PreviewLightDark +@Preview @Composable -private fun MenuNavigationExpandedPreview() { - FirefoxTheme { +private fun MenuNavigationExpandedPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { MenuNavigation( isSiteLoading = false, isExtensionsExpanded = true, @@ -222,12 +226,12 @@ private fun MenuNavigationExpandedPreview() { @Preview @Composable -private fun MenuNavigationPrivatePreview( - @PreviewParameter(SiteLoadingPreviewParameterProvider::class) isSiteLoading: Boolean, +private fun MenuNavigationSiteLoadingPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, ) { - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { MenuNavigation( - isSiteLoading = isSiteLoading, + isSiteLoading = true, isExtensionsExpanded = false, isMoreMenuExpanded = false, onBackButtonClick = {}, @@ -241,12 +245,12 @@ private fun MenuNavigationPrivatePreview( @Preview @Composable -private fun MenuNavigationExpandedPrivatePreview( - @PreviewParameter(SiteLoadingPreviewParameterProvider::class) isSiteLoading: Boolean, +private fun MenuNavigationExpandedSiteLoadingPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, ) { - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { MenuNavigation( - isSiteLoading = isSiteLoading, + isSiteLoading = true, isExtensionsExpanded = true, isMoreMenuExpanded = false, onBackButtonClick = {}, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/MoreSettingsSubmenu.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/MoreSettingsSubmenu.kt index 8a0736344f9ad..37ea96876bdf2 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/MoreSettingsSubmenu.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/MoreSettingsSubmenu.kt @@ -14,12 +14,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import org.mozilla.fenix.R import org.mozilla.fenix.components.menu.store.TranslationInfo import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR @Suppress("LongParameterList", "CognitiveComplexMethod") @@ -178,10 +179,12 @@ private fun ShortcutsMenuItem( ) } -@PreviewLightDark +@Preview @Composable -private fun MoreSettingsSubmenuPreview() { - FirefoxTheme { +private fun MoreSettingsSubmenuPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Column( modifier = Modifier .background(color = MaterialTheme.colorScheme.surface) @@ -222,8 +225,10 @@ private fun MoreSettingsSubmenuPreview() { @Preview @Composable -private fun MoreSettingsSubmenuPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun MoreSettingsSubmenuDisabledOpenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Column( modifier = Modifier .background(color = MaterialTheme.colorScheme.surface) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/header/MozillaAccountMenuItem.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/header/MozillaAccountMenuItem.kt index 288150bc63683..b817866e34768 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/header/MozillaAccountMenuItem.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/header/MozillaAccountMenuItem.kt @@ -37,7 +37,7 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.role import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.compose.base.theme.surfaceDimVariant import mozilla.components.service.fxa.manager.AccountState @@ -50,6 +50,7 @@ import org.mozilla.fenix.R import org.mozilla.fenix.compose.Image import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR private val BUTTON_HEIGHT = 56.dp @@ -271,18 +272,12 @@ private fun MozillaAccountMenuItemPreviewContent() { } } -@PreviewLightDark -@Composable -private fun MozillaAccountMenuItemPreview() { - FirefoxTheme { - MozillaAccountMenuItemPreviewContent() - } -} - @Preview @Composable -private fun MozillaAccountMenuItemPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun MozillaAccountMenuItemPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { MozillaAccountMenuItemPreviewContent() } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/header/SubmenuHeader.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/header/SubmenuHeader.kt index 22a22e9f0a3ed..74cf8e61fc085 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/header/SubmenuHeader.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/compose/header/SubmenuHeader.kt @@ -27,10 +27,11 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR @Composable @@ -72,26 +73,12 @@ internal fun SubmenuHeader( } } -@PreviewLightDark -@Composable -private fun SubmenuHeaderPreview() { - FirefoxTheme { - Column( - modifier = Modifier - .background(color = MaterialTheme.colorScheme.surface), - ) { - SubmenuHeader( - header = "sub-menu header", - onClick = {}, - ) - } - } -} - @Preview @Composable -private fun SubmenuMenuHeaderPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun SubmenuHeaderPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Column( modifier = Modifier .background(color = MaterialTheme.colorScheme.surface), diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/DismissibleItemBackground.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/DismissibleItemBackground.kt index 7cef8b8fa703e..3a7304b489307 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/DismissibleItemBackground.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/DismissibleItemBackground.kt @@ -22,10 +22,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -103,18 +104,12 @@ private fun DismissibleItemBackgroundPreviewContent() { } } -@Composable -@PreviewLightDark -private fun DismissedTabBackgroundPreview() { - FirefoxTheme { - DismissibleItemBackgroundPreviewContent() - } -} - -@Composable @Preview -private fun DismissedTabBackgroundPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun DismissedTabBackgroundPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { DismissibleItemBackgroundPreviewContent() } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/Favicon.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/Favicon.kt index 4fedb32e60000..b1f087f4c87d8 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/Favicon.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/Favicon.kt @@ -20,7 +20,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import mozilla.components.browser.icons.IconRequest @@ -30,6 +30,7 @@ import mozilla.components.compose.base.utils.inComposePreview import org.mozilla.fenix.components.components import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider internal val FAVICON_ROUNDED_CORNER_SHAPE = RoundedCornerShape(2.dp) @@ -176,24 +177,12 @@ private fun FaviconPlaceholder( ) } -@Composable -@PreviewLightDark -private fun FaviconPreview() { - FirefoxTheme { - Favicon( - url = "www.mozilla.com", - size = 64.dp, - modifier = Modifier - .background(MaterialTheme.colorScheme.surfaceContainerLowest) - .padding(all = FirefoxTheme.layout.space.static200), - ) - } -} - -@Composable @Preview -private fun FaviconPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun FaviconPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Favicon( url = "www.mozilla.com", size = 64.dp, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/InfoCardContainer.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/InfoCardContainer.kt index 499fbf3e7ac9a..273b1952a9b66 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/InfoCardContainer.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/InfoCardContainer.kt @@ -37,13 +37,14 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import org.mozilla.fenix.R import org.mozilla.fenix.shopping.ui.ext.headingResource import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR private val cardShape = RoundedCornerShape(8.dp) @@ -158,45 +159,12 @@ fun InfoCardContainer( } } -@PreviewLightDark -@Composable -private fun InfoCardContainerPreview() { - FirefoxTheme { - Surface { - Column(modifier = Modifier.padding(16.dp)) { - var isExpanded by remember { mutableStateOf(true) } - - InfoCardContainer( - modifier = Modifier.fillMaxWidth(), - ) { - Text( - text = "Info Check Card Content", - style = FirefoxTheme.typography.headline8, - ) - } - - Spacer(modifier = Modifier.height(16.dp)) - - ExpandableInfoCardContainer( - title = "Info Expandable Card", - modifier = Modifier.fillMaxWidth(), - isExpanded = isExpanded, - onExpandToggleClick = { isExpanded = !isExpanded }, - ) { - Text( - text = "content", - style = FirefoxTheme.typography.body2, - ) - } - } - } - } -} - @Preview @Composable -private fun InfoCardContainerPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun InfoCardContainerPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { Column(modifier = Modifier.padding(16.dp)) { var isExpanded by remember { mutableStateOf(true) } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/MessageCard.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/MessageCard.kt index e86b0560c246f..a36c343c9a08e 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/MessageCard.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/MessageCard.kt @@ -13,7 +13,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.compose.base.Banner import mozilla.components.compose.base.BannerColors @@ -22,6 +22,7 @@ import org.mozilla.fenix.R import org.mozilla.fenix.home.fake.FakeHomepagePreview import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import org.mozilla.fenix.wallpapers.WallpaperState /** @@ -60,10 +61,12 @@ fun MessageCard( } } +@Preview @Composable -@PreviewLightDark -private fun MessageCardPreview() { - FirefoxTheme { +private fun MessageCardPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { MessageCard( messageCardState = FakeHomepagePreview.messageCardState(), @@ -75,27 +78,12 @@ private fun MessageCardPreview() { } } -@Composable @Preview -private fun MessageCardPrivatePreview() { - FirefoxTheme( - theme = Theme.Private, - ) { - Surface { - MessageCard( - messageCardState = FakeHomepagePreview.messageCardState(), - modifier = Modifier.padding(all = 16.dp), - onClick = {}, - onCloseButtonClick = {}, - ) - } - } -} - @Composable -@PreviewLightDark -private fun MessageCardWithoutTitlePreview() { - FirefoxTheme { +private fun MessageCardWithoutTitlePreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { MessageCard( messageCardState = MessageCardState( @@ -110,51 +98,12 @@ private fun MessageCardWithoutTitlePreview() { } } -@Composable @Preview -private fun MessageCardWithoutTitlePrivatePreview() { - FirefoxTheme( - theme = Theme.Private, - ) { - Surface { - MessageCard( - messageCardState = MessageCardState( - messageText = stringResource(id = R.string.default_browser_experiment_card_text), - bannerColors = BannerColors.bannerColors(), - ), - modifier = Modifier.padding(all = 16.dp), - onClick = {}, - onCloseButtonClick = {}, - ) - } - } -} - -@Composable -@PreviewLightDark -private fun MessageCardWithButtonLabelPreview() { - FirefoxTheme { - Surface { - MessageCard( - messageCardState = MessageCardState( - messageText = stringResource(id = R.string.default_browser_experiment_card_text), - titleText = stringResource(id = R.string.default_browser_experiment_card_title), - buttonText = stringResource(id = R.string.preferences_set_as_default_browser), - bannerColors = BannerColors.bannerColors(), - ), - onClick = {}, - onCloseButtonClick = {}, - ) - } - } -} - @Composable -@Preview -private fun MessageCardWithButtonLabelPrivatePreview() { - FirefoxTheme( - theme = Theme.Private, - ) { +private fun MessageCardWithButtonLabelPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { MessageCard( messageCardState = MessageCardState( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/home/HomeSectionHeader.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/home/HomeSectionHeader.kt index 84b04e86e3192..ecdd408accb13 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/home/HomeSectionHeader.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/home/HomeSectionHeader.kt @@ -27,13 +27,14 @@ import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import mozilla.components.compose.base.utils.inComposePreview import mozilla.components.lib.state.ext.observeAsComposableState import org.mozilla.fenix.R import org.mozilla.fenix.components.components import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import org.mozilla.fenix.wallpapers.Wallpaper import mozilla.components.ui.icons.R as iconsR @@ -151,25 +152,12 @@ private fun HomeSectionHeaderContent( } } -@Composable -@PreviewLightDark -private fun HomeSectionsHeaderPreview() { - FirefoxTheme { - Surface { - HomeSectionHeader( - headerText = stringResource(R.string.home_bookmarks_title), - modifier = Modifier.padding(horizontal = FirefoxTheme.layout.size.static300), - description = stringResource(R.string.home_bookmarks_show_all_content_description), - onButtonClick = {}, - ) - } - } -} - -@Composable @Preview -private fun HomeSectionsHeaderPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun HomeSectionsHeaderPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { HomeSectionHeader( headerText = stringResource(R.string.home_bookmarks_title), diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/settings/SettingsSectionHeader.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/settings/SettingsSectionHeader.kt index a4c3056d93c87..b3427b286db8f 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/settings/SettingsSectionHeader.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/settings/SettingsSectionHeader.kt @@ -12,9 +12,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * Settings section header. @@ -32,20 +33,12 @@ fun SettingsSectionHeader(text: String, modifier: Modifier = Modifier) { ) } -@Composable -@PreviewLightDark -private fun SettingsSectionHeaderPreview() { - FirefoxTheme { - Surface { - SettingsSectionHeader("Settings") - } - } -} - -@Composable @Preview -private fun SettingsSectionHeaderPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun SettingsSectionHeaderPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { SettingsSectionHeader("Settings") } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/addresses/AddressesTools.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/addresses/AddressesTools.kt index 8e29b7daa7c0b..a948ec0925ed2 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/addresses/AddressesTools.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/addresses/AddressesTools.kt @@ -25,7 +25,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import mozilla.components.compose.base.button.FilledButton @@ -37,6 +37,7 @@ import org.mozilla.fenix.compose.list.RadioButtonListItem import org.mozilla.fenix.compose.list.TextListItem import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -230,21 +231,12 @@ private fun List.updateRegionEnabled(regionToUpdate: De } } -@Composable -@PreviewLightDark -private fun AddressesScreenPreview() { - FirefoxTheme { - AddressesTools( - debugRegionRepository = FakeAddressesDebugRegionRepository(), - creditCardsAddressesStorage = FakeCreditCardsAddressesStorage(), - ) - } -} - -@Composable @Preview -private fun AddressesScreenPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun AddressesScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { AddressesTools( debugRegionRepository = FakeAddressesDebugRegionRepository(), creditCardsAddressesStorage = FakeCreditCardsAddressesStorage(), diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/cfrs/CfrTools.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/cfrs/CfrTools.kt index ed5e0651b49d6..f9fbba847f6a4 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/cfrs/CfrTools.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/cfrs/CfrTools.kt @@ -20,8 +20,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import androidx.compose.ui.tooling.preview.PreviewParameter +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.OutlinedButton import mozilla.components.lib.state.ext.observeAsState import org.mozilla.fenix.R @@ -29,6 +29,7 @@ import org.mozilla.fenix.compose.SwitchWithLabel import org.mozilla.fenix.nimbus.FxNimbus import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * CFR Tools UI that allows for the CFR states to be reset. @@ -219,19 +220,11 @@ private fun CfrSectionTitle( } @Composable -@FlexibleWindowLightDarkPreview -private fun CfrToolsPreview() { - FirefoxTheme { - CfrTools( - cfrToolsStore = CfrToolsStore(), - ) - } -} - -@Composable -@Preview -private fun CfrToolsPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@FlexibleWindowPreview +private fun CfrToolsPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { CfrTools( cfrToolsStore = CfrToolsStore(), ) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/crashtools/CrashTools.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/crashtools/CrashTools.kt index b1b4409c48c88..2c3ba7d178166 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/crashtools/CrashTools.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/crashtools/CrashTools.kt @@ -24,16 +24,17 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import kotlinx.coroutines.delay -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.FilledButton import org.mozilla.fenix.R import org.mozilla.fenix.components.components import org.mozilla.fenix.startupCrash.StartupCrashActivity import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import org.mozilla.fenix.utils.Settings private const val SECOND_IN_MILLISECOND = 1000L @@ -111,18 +112,12 @@ internal fun convertMillisToDHMS(milliseconds: Long): String { return DateUtils.formatElapsedTime(milliseconds / SECOND_IN_MILLISECOND) } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun CrashToolsPreview() { - FirefoxTheme { - CrashTools(Settings(LocalContext.current)) - } -} - -@Preview -@Composable -private fun CrashToolsPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun CrashToolsPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { CrashTools(Settings(LocalContext.current)) } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/creditcards/CreditCardsTools.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/creditcards/CreditCardsTools.kt index defa97ff4b8a9..01b98aef4e302 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/creditcards/CreditCardsTools.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/creditcards/CreditCardsTools.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import mozilla.components.compose.base.button.FilledButton @@ -33,6 +33,7 @@ import org.mozilla.fenix.compose.list.TextListItem import org.mozilla.fenix.debugsettings.addresses.FakeCreditCardsAddressesStorage import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -123,20 +124,12 @@ private fun CreditCardsContent( } } -@Composable -@PreviewLightDark -private fun CreditCardsScreenPreview() { - FirefoxTheme { - CreditCardsTools( - creditCardsAddressesStorage = FakeCreditCardsAddressesStorage(), - ) - } -} - -@Composable @Preview -private fun CreditCardsScreenPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun CreditCardsScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { CreditCardsTools( creditCardsAddressesStorage = FakeCreditCardsAddressesStorage(), ) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/gleandebugtools/ui/GleanDebugToolsScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/gleandebugtools/ui/GleanDebugToolsScreen.kt index 5ee95063e4a08..2ff6528f02046 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/gleandebugtools/ui/GleanDebugToolsScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/gleandebugtools/ui/GleanDebugToolsScreen.kt @@ -23,9 +23,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import mozilla.components.compose.base.Dropdown -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.FilledButton import mozilla.components.compose.base.menu.MenuItem import mozilla.components.compose.base.text.Text @@ -39,6 +39,7 @@ import org.mozilla.fenix.debugsettings.gleandebugtools.GleanDebugToolsState import org.mozilla.fenix.debugsettings.gleandebugtools.GleanDebugToolsStore import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * Glean Debug Tools UI that allows for glean test pings to be sent. @@ -274,30 +275,11 @@ private fun getPingDropdownMenu( } @Composable -@FlexibleWindowLightDarkPreview -private fun GleanDebugToolsPreview() { - FirefoxTheme { - GleanDebugToolsScreen( - gleanDebugToolsStore = GleanDebugToolsStore( - initialState = GleanDebugToolsState( - logPingsToConsoleEnabled = false, - debugViewTag = "", - pingTypes = listOf( - "metrics", - "baseline", - "ping type 3", - "ping type 4", - ), - ), - ), - ) - } -} - -@Composable -@Preview -private fun GleanDebugToolsPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@FlexibleWindowPreview +private fun GleanDebugToolsPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { GleanDebugToolsScreen( gleanDebugToolsStore = GleanDebugToolsStore( initialState = GleanDebugToolsState( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/logins/LoginsTools.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/logins/LoginsTools.kt index 850d5577a2043..39fd2f9050982 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/logins/LoginsTools.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/logins/LoginsTools.kt @@ -25,7 +25,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import mozilla.components.browser.state.selector.findTab @@ -43,6 +43,7 @@ import org.mozilla.fenix.compose.list.TextListItem import org.mozilla.fenix.debugsettings.ui.DebugDrawer import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import java.util.UUID import mozilla.components.ui.icons.R as iconsR @@ -185,27 +186,12 @@ internal class FakeLoginsStorage : LoginsStorage { ) } -@Composable -@PreviewLightDark -private fun LoginsScreenPreview() { - FirefoxTheme { - val selectedTab = createTab("https://example.com") - LoginsTools( - browserStore = BrowserStore( - BrowserState( - selectedTabId = selectedTab.id, - tabs = listOf(selectedTab), - ), - ), - loginsStorage = FakeLoginsStorage(), - ) - } -} - -@Composable @Preview -private fun LoginsScreenPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun LoginsScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { val selectedTab = createTab("https://example.com") LoginsTools( browserStore = BrowserStore( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/region/RegionTools.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/region/RegionTools.kt index 57f1cb56ce309..8b44738feeb5e 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/region/RegionTools.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/region/RegionTools.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.core.content.edit import androidx.lifecycle.ViewModel @@ -37,6 +37,7 @@ import org.mozilla.fenix.Config import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider private const val DEFAULT_REGION = "XX" private const val MAX_REGION_LENGTH = 2 @@ -182,20 +183,12 @@ class RegionToolsViewModel : ViewModel() { var currentRegion by mutableStateOf("") } -@Composable -@PreviewLightDark -private fun RegionScreenPreview() { - FirefoxTheme { - RegionTools( - browserStore = BrowserStore(), - ) - } -} - -@Composable @Preview -private fun RegionScreenPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun RegionScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { RegionTools( browserStore = BrowserStore(), ) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/tabs/TabTools.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/tabs/TabTools.kt index e57e477271bd6..0e8ce79b2463f 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/tabs/TabTools.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/tabs/TabTools.kt @@ -32,9 +32,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.core.text.isDigitsOnly import mozilla.components.browser.state.action.TabListAction @@ -50,6 +48,7 @@ import org.mozilla.fenix.ext.maxActiveTime import org.mozilla.fenix.tabstray.ext.isNormalTabInactive import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider @VisibleForTesting internal const val MAX_TABS_GENERATED = 1000 @@ -314,44 +313,28 @@ internal fun validateTextField(text: String): Int? { } } -private data class TabToolsPreviewModel( - val inactiveTabsEnabled: Boolean = true, -) - -private class TabToolsPreviewParameterProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf( - TabToolsPreviewModel( - inactiveTabsEnabled = true, - ), - TabToolsPreviewModel( - inactiveTabsEnabled = false, - ), - ) -} - +@Preview @Composable -@PreviewLightDark private fun TabToolsPreview( - @PreviewParameter(TabToolsPreviewParameterProvider::class) model: TabToolsPreviewModel, + @PreviewParameter(ThemeProvider::class) theme: Theme, ) { - FirefoxTheme { + FirefoxTheme(theme) { TabTools( store = BrowserStore(), - inactiveTabsEnabled = model.inactiveTabsEnabled, + inactiveTabsEnabled = true, ) } } -@Composable @Preview -private fun TabToolsPrivatePreview( - @PreviewParameter(TabToolsPreviewParameterProvider::class) model: TabToolsPreviewModel, +@Composable +private fun TabToolsInactiveTabsDisabledPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, ) { - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { TabTools( store = BrowserStore(), - inactiveTabsEnabled = model.inactiveTabsEnabled, + inactiveTabsEnabled = false, ) } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/ui/DebugDrawer.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/ui/DebugDrawer.kt index 4ca7da777a2ee..4c269fcb00d2a 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/ui/DebugDrawer.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/ui/DebugDrawer.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.draw.shadow import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost @@ -33,6 +33,7 @@ import org.mozilla.fenix.R import org.mozilla.fenix.debugsettings.navigation.DebugDrawerDestination import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -111,42 +112,11 @@ fun DebugDrawer( } } -@Composable -@PreviewLightDark -private fun DebugDrawerPreview() { - val navController = rememberNavController() - val destinations = remember { - List(size = 15) { index -> - DebugDrawerDestination( - route = "screen_$index", - title = R.string.debug_drawer_title, - onClick = { - navController.navigate(route = "screen_$index") - }, - content = { - Text( - text = "Tool $index", - style = FirefoxTheme.typography.headline6, - ) - }, - ) - } - } - - FirefoxTheme { - DebugDrawer( - navController = navController, - destinations = destinations, - onBackButtonClick = { - navController.popBackStack() - }, - ) - } -} - -@Composable @Preview -private fun DebugDrawerPrivatePreview() { +@Composable +private fun DebugDrawerPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val navController = rememberNavController() val destinations = remember { List(size = 15) { index -> @@ -166,7 +136,7 @@ private fun DebugDrawerPrivatePreview() { } } - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { DebugDrawer( navController = navController, destinations = destinations, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/ui/DebugDrawerHome.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/ui/DebugDrawerHome.kt index f395293b23067..e0be227e76e1c 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/ui/DebugDrawerHome.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/ui/DebugDrawerHome.kt @@ -27,7 +27,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import mozilla.components.compose.base.snackbar.Snackbar @@ -40,6 +40,7 @@ import org.mozilla.fenix.compose.list.TextListItem import org.mozilla.fenix.debugsettings.navigation.DebugDrawerDestination import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * The navigation route for [DebugDrawerHome]. @@ -111,13 +112,15 @@ fun DebugDrawerHome( } } +@Preview @Composable -@PreviewLightDark -private fun DebugDrawerHomePreview() { +private fun DebugDrawerHomePreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val scope = rememberCoroutineScope() val snackbarState = remember { SnackbarHostState() } - FirefoxTheme { + FirefoxTheme(theme) { Box { DebugDrawerHome( destinations = List(size = 30) { @@ -143,34 +146,3 @@ private fun DebugDrawerHomePreview() { } } } - -@Composable -@Preview -private fun DebugDrawerHomePrivatePreview() { - val scope = rememberCoroutineScope() - val snackbarState = remember { SnackbarHostState() } - - FirefoxTheme(theme = Theme.Private) { - Box { - DebugDrawerHome( - destinations = List(size = 30) { - DebugDrawerDestination( - route = "screen_$it", - title = R.string.debug_drawer_title, - onClick = { - scope.launch { - snackbarState.displaySnackbar(message = "item $it clicked") - } - }, - content = {}, - ) - }, - ) - - SnackbarHost( - hostState = snackbarState, - modifier = Modifier.align(Alignment.BottomCenter), - ) - } - } -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/listscreen/DeleteDownloadFileDialog.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/listscreen/DeleteDownloadFileDialog.kt index cad63fe0919ad..9fb74e17619b8 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/listscreen/DeleteDownloadFileDialog.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/listscreen/DeleteDownloadFileDialog.kt @@ -8,12 +8,13 @@ import androidx.compose.material3.AlertDialog import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import androidx.compose.ui.tooling.preview.PreviewParameter +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.TextButton import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * This dialog is used to prompt the user to confirm if they want to delete @@ -52,21 +53,12 @@ fun DeleteDownloadFileDialog( ) } +@FlexibleWindowPreview @Composable -@FlexibleWindowLightDarkPreview -private fun DeleteDownloadFileDialogPreview() { - FirefoxTheme { - DeleteDownloadFileDialog( - onConfirmDelete = {}, - onCancel = {}, - ) - } -} - -@Composable -@Preview -private fun DeleteDownloadFileDialogPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun DeleteDownloadFileDialogPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { DeleteDownloadFileDialog( onConfirmDelete = {}, onCancel = {}, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/listscreen/ui/DownloadSearchField.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/listscreen/ui/DownloadSearchField.kt index 88dc344a8c515..ffea95d1c7f6e 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/listscreen/ui/DownloadSearchField.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/listscreen/ui/DownloadSearchField.kt @@ -34,11 +34,12 @@ import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -132,27 +133,12 @@ private fun PlaceholderText() { ) } -@PreviewLightDark -@Composable -private fun DownloadSearchFieldPreview() { - FirefoxTheme { - Surface { - DownloadSearchField( - initialText = "", - onValueChange = {}, - onSearchDismissRequest = {}, - modifier = Modifier - .height(56.dp) - .fillMaxWidth(), - ) - } - } -} - @Preview @Composable -private fun DownloadSearchFieldPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun DownloadSearchFieldPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { DownloadSearchField( initialText = "", diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/toolbar/BrowserSimpleToolbar.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/toolbar/BrowserSimpleToolbar.kt index 612ce039a9921..f7391f5311e69 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/toolbar/BrowserSimpleToolbar.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/toolbar/BrowserSimpleToolbar.kt @@ -19,7 +19,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.compose.browser.toolbar.ActionContainer import mozilla.components.compose.browser.toolbar.concept.Action @@ -38,6 +38,7 @@ import mozilla.components.lib.state.ext.observeAsComposableState import org.mozilla.fenix.components.AppStore import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -162,38 +163,26 @@ private fun SimpleBrowserToolbarPreview(actions: List, theme: Theme = Th } } -@Composable -@PreviewLightDark -private fun BrowserSimpleToolbarPreview_Edit() { - SimpleBrowserToolbarPreview(editEndActions()) -} - -@Composable @Preview -private fun BrowserSimpleToolbarPrivatePreview_Edit() { - SimpleBrowserToolbarPreview(editEndActions(), theme = Theme.Private) -} - @Composable -@PreviewLightDark -private fun BrowserSimpleToolbarPreview_Initial() { - SimpleBrowserToolbarPreview(initialActions()) +private fun BrowserSimpleToolbarPreview_Edit( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + SimpleBrowserToolbarPreview(editEndActions(), theme = theme) } -@Composable @Preview -private fun BrowserSimpleToolbarPrivatePreview_Initial() { - SimpleBrowserToolbarPreview(initialActions(), theme = Theme.Private) -} - @Composable -@PreviewLightDark -private fun BrowserSimpleToolbarPreview_Search() { - SimpleBrowserToolbarPreview(searchEndActions()) +private fun BrowserSimpleToolbarPreview_Initial( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + SimpleBrowserToolbarPreview(initialActions(), theme = theme) } -@Composable @Preview -private fun BrowserSimpleToolbarPrivatePreview_Search() { - SimpleBrowserToolbarPreview(searchEndActions(), theme = Theme.Private) +@Composable +private fun BrowserSimpleToolbarPreview_Search( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + SimpleBrowserToolbarPreview(searchEndActions(), theme = theme) } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/topsites/TopSites.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/topsites/TopSites.kt index 1224344057e02..99c13086c798f 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/topsites/TopSites.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/topsites/TopSites.kt @@ -48,10 +48,10 @@ import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.modifier.rightClickable import mozilla.components.feature.top.sites.TopSite import mozilla.components.ui.colors.PhotonColors @@ -64,6 +64,7 @@ import org.mozilla.fenix.home.topsites.TopSitesTestTag.TOP_SITE_CARD_FAVICON import org.mozilla.fenix.home.topsites.interactor.TopSiteInteractor import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import org.mozilla.fenix.wallpapers.WallpaperState import mozilla.components.ui.icons.R as iconsR @@ -503,35 +504,12 @@ internal fun getMenuItems( return result } +@FlexibleWindowPreview @Composable -@FlexibleWindowLightDarkPreview -private fun TopSitesPreview() { - FirefoxTheme { - Surface { - Box( - modifier = Modifier.padding(all = FirefoxTheme.layout.space.static200), - ) { - TopSites( - topSites = FakeHomepagePreview.topSites(), - onTopSiteClick = {}, - onTopSiteLongClick = {}, - onTopSiteImpression = { _, _ -> }, - onOpenInPrivateTabClicked = {}, - onEditTopSiteClicked = {}, - onRemoveTopSiteClicked = {}, - onSettingsClicked = {}, - onSponsorPrivacyClicked = {}, - onTopSitesItemBound = {}, - ) - } - } - } -} - -@Composable -@Preview -private fun TopSitesPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun TopSitesPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { Box( modifier = Modifier.padding(all = FirefoxTheme.layout.space.static200), diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/HomepageHeader.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/HomepageHeader.kt index 619f5af7e7234..f5559d60d9563 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/HomepageHeader.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/HomepageHeader.kt @@ -31,7 +31,7 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTag import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import org.mozilla.fenix.R import org.mozilla.fenix.browser.browsingmode.BrowsingMode @@ -40,6 +40,7 @@ import org.mozilla.fenix.home.ui.HomepageTestTag.HOMEPAGE_WORDMARK_TEXT import org.mozilla.fenix.home.ui.HomepageTestTag.PRIVATE_BROWSING_HOMEPAGE_BUTTON import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -142,10 +143,12 @@ internal fun getAttr(resId: Int): Int { return newResId } +@Preview @Composable -@PreviewLightDark -private fun HomepageHeaderPreview() { - FirefoxTheme { +private fun HomepageHeaderPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { HomepageHeader( wordmarkTextColor = null, @@ -160,22 +163,3 @@ private fun HomepageHeaderPreview() { } } } - -@Composable -@Preview -private fun PrivateHomepageHeaderPreview() { - FirefoxTheme(theme = Theme.Private) { - Surface { - HomepageHeader( - wordmarkTextColor = null, - privateBrowsingButtonColor = colorResource( - getAttr( - iconsR.attr.mozac_ic_private_mode_circle_fill_icon_color, - ), - ), - browsingMode = BrowsingMode.Private, - browsingModeChanged = {}, - ) - } - } -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/iconpicker/ui/AppIconPreference.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/iconpicker/ui/AppIconPreference.kt index d64c26ab9d6a7..b475da365a3c2 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/iconpicker/ui/AppIconPreference.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/iconpicker/ui/AppIconPreference.kt @@ -21,12 +21,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.navigation.findNavController import androidx.preference.Preference import androidx.preference.PreferenceViewHolder -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import org.mozilla.fenix.GleanMetrics.CustomizationSettings import org.mozilla.fenix.R import org.mozilla.fenix.iconpicker.AppIcon @@ -36,6 +36,7 @@ import org.mozilla.fenix.iconpicker.DefaultPackageManagerWrapper import org.mozilla.fenix.settings.CustomizationFragmentDirections import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider private val IconSize = 40.dp @@ -118,18 +119,12 @@ private fun SelectAppIcon( } } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun SelectAppIconPreview() { - FirefoxTheme { - SelectAppIcon(AppIcon.AppDefault) {} - } -} - -@Preview -@Composable -private fun SelectAppIconPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun SelectAppIconPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { SelectAppIcon(AppIcon.AppDefault) {} } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/iconpicker/ui/AppIconSelection.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/iconpicker/ui/AppIconSelection.kt index a99393ac3ea92..d5bc5de4ae7c8 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/iconpicker/ui/AppIconSelection.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/iconpicker/ui/AppIconSelection.kt @@ -48,11 +48,12 @@ import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.TextButton import mozilla.components.compose.base.snackbar.Snackbar import mozilla.components.compose.base.snackbar.displaySnackbar @@ -72,6 +73,7 @@ import org.mozilla.fenix.iconpicker.SystemAction import org.mozilla.fenix.iconpicker.UserAction import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider private val ListItemHeight = 56.dp private val AppIconSize = 40.dp @@ -387,55 +389,32 @@ private fun AppIconSelectionPreview() { } } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun AppIconOptionPreview() { - FirefoxTheme { - AppIconOption(AppIcon.AppDefault, false) {} - } -} - -@Preview -@Composable -private fun AppIconOptionPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun AppIconOptionPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { AppIconOption(AppIcon.AppDefault, false) {} } } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun AppIconOptionWithSubtitlePreview() { - FirefoxTheme { - AppIconOption(AppIcon.AppMomo, false) {} - } -} - -@Preview -@Composable -private fun AppIconOptionWithSubtitlePrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun AppIconOptionWithSubtitlePreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { AppIconOption(AppIcon.AppMomo, false) {} } } -@FlexibleWindowLightDarkPreview -@Composable -private fun RestartWarningDialogPreview() { - FirefoxTheme { - RestartWarningDialog( - shortcutRemovalWarning = { false }, - onConfirmClicked = {}, - onDismissClicked = {}, - onDismissed = {}, - ) - } -} - -@Preview +@FlexibleWindowPreview @Composable -private fun RestartWarningDialogPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun RestartWarningDialogPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { RestartWarningDialog( shortcutRemovalWarning = { false }, onConfirmClicked = {}, @@ -445,23 +424,12 @@ private fun RestartWarningDialogPrivatePreview() { } } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun ShortcutRemovalWarningDialogPreview() { - FirefoxTheme { - RestartWarningDialog( - shortcutRemovalWarning = { true }, - onConfirmClicked = {}, - onDismissClicked = {}, - onDismissed = {}, - ) - } -} - -@Preview -@Composable -private fun ShortcutRemovalWarningDialogPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun ShortcutRemovalWarningDialogPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { RestartWarningDialog( shortcutRemovalWarning = { true }, onConfirmClicked = {}, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyBottomSheet.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyBottomSheet.kt index ad5aee24c5ef3..552997dbebc27 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyBottomSheet.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyBottomSheet.kt @@ -29,12 +29,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.traversalIndex import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import org.mozilla.fenix.R import org.mozilla.fenix.compose.BottomSheetHandle import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR private const val BOTTOM_SHEET_HANDLE_WIDTH_PERCENT = 0.1f @@ -127,36 +129,16 @@ fun MicrosurveyBottomSheet( } } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Preview( name = "Large Font", fontScale = 2.0f, ) @Composable -private fun MicrosurveyBottomSheetPreview() { - FirefoxTheme { - MicrosurveyBottomSheet( - question = "How satisfied are you with printing in Firefox?", - icon = iconsR.drawable.mozac_ic_print_24, - onPrivacyPolicyLinkClick = {}, - onCloseButtonClicked = {}, - onSubmitButtonClicked = {}, - answers = listOf( - stringResource(id = R.string.likert_scale_option_1), - stringResource(id = R.string.likert_scale_option_2), - stringResource(id = R.string.likert_scale_option_3), - stringResource(id = R.string.likert_scale_option_4), - stringResource(id = R.string.likert_scale_option_5), - stringResource(id = R.string.likert_scale_option_6), - ), - ) - } -} - -@Preview -@Composable -private fun MicrosurveyBottomSheetPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun MicrosurveyBottomSheetPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { MicrosurveyBottomSheet( question = "How satisfied are you with printing in Firefox?", icon = iconsR.drawable.mozac_ic_print_24, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyCompleted.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyCompleted.kt index a4241e239668e..24be993633601 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyCompleted.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyCompleted.kt @@ -25,12 +25,13 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider private val shape = RoundedCornerShape(8.dp) @@ -79,18 +80,12 @@ fun MicrosurveyCompleted( } } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun MicrosurveyCompletedPreview() { - FirefoxTheme { - MicrosurveyCompleted() - } -} - -@Preview -@Composable -private fun MicrosurveyCompletedPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun MicrosurveyCompletedPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { MicrosurveyCompleted() } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyContent.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyContent.kt index 1b1ec22aede8f..8b64cc2267f84 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyContent.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyContent.kt @@ -31,13 +31,14 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import org.mozilla.fenix.R import org.mozilla.fenix.compose.list.RadioButtonListItem import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR private val shape = RoundedCornerShape(8.dp) @@ -115,29 +116,12 @@ private fun Header(icon: Int, question: String) { } } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun MicrosurveyContentPreview() { - FirefoxTheme { - MicrosurveyContent( - question = "How satisfied are you with printing in Firefox?", - icon = iconsR.drawable.mozac_ic_print_24, - answers = listOf( - stringResource(id = R.string.likert_scale_option_1), - stringResource(id = R.string.likert_scale_option_2), - stringResource(id = R.string.likert_scale_option_3), - stringResource(id = R.string.likert_scale_option_4), - stringResource(id = R.string.likert_scale_option_5), - ), - onSelectionChange = {}, - ) - } -} - -@Preview -@Composable -private fun MicrosurveyContentPrivatereview() { - FirefoxTheme(theme = Theme.Private) { +private fun MicrosurveyContentPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { MicrosurveyContent( question = "How satisfied are you with printing in Firefox?", icon = iconsR.drawable.mozac_ic_print_24, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyRequestPrompt.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyRequestPrompt.kt index 52ecce9e66bab..e79db4dc4efde 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyRequestPrompt.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyRequestPrompt.kt @@ -28,9 +28,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.FilledButton import mozilla.components.compose.base.button.IconButton import mozilla.components.support.utils.KeyboardState @@ -40,6 +40,7 @@ import org.mozilla.fenix.R import org.mozilla.fenix.microsurvey.ui.ext.MicrosurveyUIData import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR private const val TABLET_WIDTH_FRACTION = 0.5f @@ -135,29 +136,12 @@ private fun Header( } } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun MicrosurveyRequestPromptPreview() { - FirefoxTheme { - MicrosurveyRequestPrompt( - microsurvey = MicrosurveyUIData( - id = "", - promptTitle = "Help make printing in Firefox better. It only takes a sec.", - icon = iconsR.drawable.mozac_ic_lightbulb_24, - question = "", - answers = emptyList(), - ), - activity = HomeActivity(), - onStartSurveyClicked = {}, - onCloseButtonClicked = {}, - ) - } -} - -@Preview -@Composable -private fun MicrosurveyRequestPromptPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun MicrosurveyRequestPromptPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { MicrosurveyRequestPrompt( microsurvey = MicrosurveyUIData( id = "", diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/ManagePrivacyPreferencesDialog.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/ManagePrivacyPreferencesDialog.kt index fe41ce220a921..ee2ebba0c48f2 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/ManagePrivacyPreferencesDialog.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/onboarding/ManagePrivacyPreferencesDialog.kt @@ -22,11 +22,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.TextButton import mozilla.components.lib.state.ext.observeAsState import org.mozilla.fenix.R @@ -37,6 +37,7 @@ import org.mozilla.fenix.onboarding.store.PrivacyPreferencesAction import org.mozilla.fenix.onboarding.store.PrivacyPreferencesStore import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * Dialog to manage privacy preferences during onboarding. @@ -185,18 +186,12 @@ private fun PositiveButton(onDismissRequest: () -> Unit) { } } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun ManagePrivacyPreferencesDialogPreview() { - FirefoxTheme { - ManagePrivacyPreferencesDialog(PrivacyPreferencesStore(), {}, {}, {}) - } -} - -@Preview -@Composable -private fun ManagePrivacyPreferencesDialogPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun ManagePrivacyPreferencesDialogPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { ManagePrivacyPreferencesDialog(PrivacyPreferencesStore(), {}, {}, {}) } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/pbmlock/UnlockPrivateTabsScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/pbmlock/UnlockPrivateTabsScreen.kt index aea34e95d6a83..fcde6dd24ecf0 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/pbmlock/UnlockPrivateTabsScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/pbmlock/UnlockPrivateTabsScreen.kt @@ -4,7 +4,6 @@ package org.mozilla.fenix.pbmlock -import android.content.res.Configuration import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -24,8 +23,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.FilledButton import mozilla.components.compose.base.button.TextButton import mozilla.components.compose.base.utils.getResolvedAttrResId @@ -33,13 +33,10 @@ import org.mozilla.fenix.R import org.mozilla.fenix.ext.isLargeWindow import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider private const val FILL_WIDTH_LARGE_WINDOW = 0.5f private const val FILL_WIDTH_DEFAULT = 1.0f -private const val PHONE_WIDTH = 400 -private const val PHONE_HEIGHT = 640 -private const val TABLET_WIDTH = 700 -private const val TABLET_HEIGHT = 1280 /** * A screen allowing users to unlock their private tabs. @@ -143,32 +140,11 @@ private fun Footer(onUnlockClicked: () -> Unit, onLeaveClicked: () -> Unit, show } } -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, widthDp = PHONE_WIDTH, heightDp = PHONE_HEIGHT) +@FlexibleWindowPreview @Composable -private fun ScreenPreviewLightPhone() = ScreenPreview(Theme.Light) - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = PHONE_WIDTH, heightDp = PHONE_HEIGHT) -@Composable -private fun ScreenPreviewDarkPhone() = ScreenPreview(Theme.Dark) - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = PHONE_WIDTH, heightDp = PHONE_HEIGHT) -@Composable -private fun ScreenPreviewPrivatePhone() = ScreenPreview(Theme.Private) - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, widthDp = TABLET_WIDTH, heightDp = TABLET_HEIGHT) -@Composable -private fun ScreenPreviewLightTablet() = ScreenPreview(Theme.Light) - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = TABLET_WIDTH, heightDp = TABLET_HEIGHT) -@Composable -private fun ScreenPreviewDarkTablet() = ScreenPreview(Theme.Dark) - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = TABLET_WIDTH, heightDp = TABLET_HEIGHT) -@Composable -private fun ScreenPreviewPrivateTablet() = ScreenPreview(Theme.Private) - -@Composable -private fun ScreenPreview(theme: Theme) { +private fun ScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { FirefoxTheme(theme) { UnlockPrivateTabsScreen( onUnlockClicked = {}, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/perf/ProfilerReusableComposable.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/perf/ProfilerReusableComposable.kt index 141f92cc7afa5..75648d0171eae 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/perf/ProfilerReusableComposable.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/perf/ProfilerReusableComposable.kt @@ -28,13 +28,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import mozilla.components.compose.base.button.TextButton import org.mozilla.fenix.compose.list.RadioButtonListItem import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * Top-level card container for profiler dialogs @@ -194,35 +195,15 @@ fun ProfilerErrorDialog( } } -@Composable -@PreviewLightDark -private fun ProfilerDialogueCardPreview() { - val radioOptions = listOf("Firefox", "Graphics", "Media", "Networking") - val selectedOption = remember { mutableStateOf("Firefox") } - - FirefoxTheme { - ProfilerDialogueCard { - radioOptions.forEach { text -> - ProfilerLabeledRadioButton( - text = text, - subText = "Sub", - selected = selectedOption.value == text, - onClick = { - selectedOption.value = text - }, - ) - } - } - } -} - -@Composable @Preview -private fun ProfilerDialogueCardPrivatePreview() { +@Composable +private fun ProfilerDialogueCardPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val radioOptions = listOf("Firefox", "Graphics", "Media", "Networking") val selectedOption = remember { mutableStateOf("Firefox") } - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { ProfilerDialogueCard { radioOptions.forEach { text -> ProfilerLabeledRadioButton( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/reviewprompt/ui/CustomReviewPrompt.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/reviewprompt/ui/CustomReviewPrompt.kt index 74fd23fa25209..f39028a9016c7 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/reviewprompt/ui/CustomReviewPrompt.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/reviewprompt/ui/CustomReviewPrompt.kt @@ -36,9 +36,9 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.FilledButton import mozilla.components.compose.base.theme.surfaceDimVariant import mozilla.components.lib.state.ext.observeAsState @@ -51,6 +51,7 @@ import org.mozilla.fenix.reviewprompt.CustomReviewPromptState.Rate import org.mozilla.fenix.reviewprompt.CustomReviewPromptStore import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * Prompt that can show either: @@ -263,12 +264,14 @@ private fun FeedbackStep(onLeaveFeedbackButtonClick: () -> Unit, modifier: Modif // *** Code below used for previews only *** // @OptIn(ExperimentalMaterial3Api::class) -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun BottomSheetPreview() { +private fun BottomSheetPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - FirefoxTheme { + FirefoxTheme(theme) { BottomSheet( sheetState = sheetState, customReviewPromptState = PrePrompt, @@ -282,12 +285,14 @@ private fun BottomSheetPreview() { } @OptIn(ExperimentalMaterial3Api::class) -@PreviewLightDark +@Preview @Composable -private fun PrePromptPreview() { +private fun PrePromptPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - FirefoxTheme { + FirefoxTheme(theme) { BottomSheet( sheetState = sheetState, customReviewPromptState = PrePrompt, @@ -301,12 +306,14 @@ private fun PrePromptPreview() { } @OptIn(ExperimentalMaterial3Api::class) -@PreviewLightDark +@Preview @Composable -private fun RatePromptPreview() { +private fun RatePromptPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - FirefoxTheme { + FirefoxTheme(theme) { BottomSheet( sheetState = sheetState, customReviewPromptState = Rate, @@ -319,32 +326,15 @@ private fun RatePromptPreview() { } } -@OptIn(ExperimentalMaterial3Api::class) -@PreviewLightDark -@Composable -private fun FeedbackPromptPreview() { - val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - - FirefoxTheme { - BottomSheet( - sheetState = sheetState, - customReviewPromptState = Feedback, - onDismissRequest = {}, - onNegativePrePromptButtonClick = {}, - onPositivePrePromptButtonClick = {}, - onRateButtonClick = {}, - onLeaveFeedbackButtonClick = {}, - ) - } -} - @OptIn(ExperimentalMaterial3Api::class) @Preview @Composable -private fun FeedbackPromptPrivatePreview() { +private fun FeedbackPromptPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { BottomSheet( sheetState = sheetState, customReviewPromptState = Feedback, @@ -357,27 +347,12 @@ private fun FeedbackPromptPrivatePreview() { } } -@PreviewLightDark -@Composable -private fun FoxEmojiButtonPreview() { - FirefoxTheme { - Surface { - FoxEmojiButton( - emoji = painterResource(R.drawable.review_prompt_positive_button), - label = "It’s great!", - onClick = {}, - modifier = Modifier - .padding(16.dp) - .width(176.dp), - ) - } - } -} - @Preview @Composable -private fun FoxEmojiButtonPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun FoxEmojiButtonPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { FoxEmojiButton( emoji = painterResource(R.drawable.review_prompt_positive_button), @@ -391,46 +366,18 @@ private fun FoxEmojiButtonPrivatePreview() { } } -@OptIn(ExperimentalMaterial3Api::class) -@PreviewLightDark -@Composable -private fun InteractiveBottomSheetPreview() { - val store = CustomReviewPromptStore(PrePrompt) - val promptState by store.observeAsState(PrePrompt) { it } - - val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - - FirefoxTheme { - BottomSheet( - sheetState = sheetState, - customReviewPromptState = promptState, - onDismissRequest = {}, - onNegativePrePromptButtonClick = { - store.dispatch(CustomReviewPromptAction.PositivePrePromptButtonClicked) - }, - onPositivePrePromptButtonClick = { - store.dispatch(CustomReviewPromptAction.NegativePrePromptButtonClicked) - }, - onRateButtonClick = { - store.dispatch(CustomReviewPromptAction.RateButtonClicked) - }, - onLeaveFeedbackButtonClick = { - store.dispatch(CustomReviewPromptAction.LeaveFeedbackButtonClicked) - }, - ) - } -} - @OptIn(ExperimentalMaterial3Api::class) @Preview @Composable -private fun InteractiveBottomSheetPrivatePreview() { +private fun InteractiveBottomSheetPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val store = CustomReviewPromptStore(PrePrompt) val promptState by store.observeAsState(PrePrompt) { it } val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { BottomSheet( sheetState = sheetState, customReviewPromptState = promptState, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/search/awesomebar/ClipboardSuggestionBar.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/search/awesomebar/ClipboardSuggestionBar.kt index aadf9a53c2d4c..05b939bd30c3e 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/search/awesomebar/ClipboardSuggestionBar.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/search/awesomebar/ClipboardSuggestionBar.kt @@ -22,11 +22,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -75,21 +76,12 @@ fun ClipboardSuggestionBar( } } -@PreviewLightDark -@Composable -private fun ClipboardBarPreview() { - FirefoxTheme { - ClipboardSuggestionBar( - shouldUseBottomToolbar = false, - onClick = {}, - ) - } -} - @Preview @Composable -private fun ClipboardBarPreviewPreview() { - FirefoxTheme(theme = Theme.Private) { +private fun ClipboardBarPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { ClipboardSuggestionBar( shouldUseBottomToolbar = false, onClick = {}, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/FontSizeSlider.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/FontSizeSlider.kt index 01287098b8f0d..03060534bf71d 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/FontSizeSlider.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/FontSizeSlider.kt @@ -35,12 +35,13 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTag import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import kotlin.math.roundToInt private const val HALF_ALPHA = 0.5F @@ -254,25 +255,12 @@ private fun FilledTrack(fraction: Float, isEnabled: Boolean) { ) {} } -@PreviewLightDark -@Composable -private fun FontSizePreferencePreview() { - FirefoxTheme { - FontSizePreference( - isEnabled = true, - value = 100f, - onValueChange = {}, - onValueChangeFinished = {}, - ) - } -} - @Preview @Composable -private fun PrivateFontSizePreferencePreview() { - FirefoxTheme( - theme = Theme.Private, - ) { +private fun FontSizePreferencePreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { FontSizePreference( isEnabled = true, value = 100f, @@ -282,25 +270,12 @@ private fun PrivateFontSizePreferencePreview() { } } -@PreviewLightDark -@Composable -private fun FontSizePreferenceDisabledPreview() { - FirefoxTheme { - FontSizePreference( - isEnabled = false, - value = 200f, - onValueChange = {}, - onValueChangeFinished = {}, - ) - } -} - @Preview @Composable -private fun PrivateFontSizePreferenceDisabledPreview() { - FirefoxTheme( - theme = Theme.Private, - ) { +private fun FontSizePreferenceDisabledPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { FontSizePreference( isEnabled = false, value = 200f, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/edit/DeleteAddressDialog.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/edit/DeleteAddressDialog.kt index 0ddf4b487167b..cc7217faa9e64 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/edit/DeleteAddressDialog.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/edit/DeleteAddressDialog.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import mozilla.components.compose.base.button.TextButton import mozilla.components.lib.state.ext.observeAsState import org.mozilla.fenix.R @@ -24,6 +24,7 @@ import org.mozilla.fenix.settings.address.store.DialogState import org.mozilla.fenix.settings.address.store.ViewAppeared import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * Dialog that is presented when deleting an address. @@ -61,30 +62,17 @@ internal fun DeleteAddressDialog(store: AddressStore) { } } -@PreviewLightDark -@Composable -private fun DeleteAddressDialogPreview() { - val store = AddressStore( - AddressState.initial().copy(deleteDialog = DialogState.Presenting), - listOf(), - ).also { it.dispatch(ViewAppeared) } - - FirefoxTheme { - Surface { - DeleteAddressDialog(store) - } - } -} - @Preview @Composable -private fun DeleteAddressDialogPrivatePreview() { +private fun DeleteAddressDialogPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val store = AddressStore( AddressState.initial().copy(deleteDialog = DialogState.Presenting), listOf(), ).also { it.dispatch(ViewAppeared) } - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { Surface { DeleteAddressDialog(store) } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/edit/EditAddressScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/edit/EditAddressScreen.kt index 0c6bb645a4dde..67168f9ba6702 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/edit/EditAddressScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/edit/EditAddressScreen.kt @@ -30,11 +30,11 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.browser.state.search.RegionState import mozilla.components.compose.base.Dropdown -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.DestructiveButton import mozilla.components.compose.base.button.FilledButton import mozilla.components.compose.base.button.OutlinedButton @@ -58,6 +58,7 @@ import org.mozilla.fenix.settings.address.store.isEditing import org.mozilla.fenix.settings.address.utils.generateAddress import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.compose.base.text.Text as DropdownText /** @@ -318,46 +319,28 @@ private fun createStore( listOf(), ).also { it.dispatch(ViewAppeared) } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun AddAddressPreview() { - val store = createStore() - - FirefoxTheme { - EditAddressScreen(store) - } -} - -@Preview -@Composable -private fun AddAddressPrivatePreview() { +private fun AddAddressPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val store = createStore() - FirefoxTheme(theme = Theme.Private) { - EditAddressScreen(store) - } -} - -@FlexibleWindowLightDarkPreview -@Composable -private fun EditAddressPreview() { - val store = createStore( - address = generateAddress(), - ) - - FirefoxTheme { + FirefoxTheme(theme) { EditAddressScreen(store) } } -@Preview +@FlexibleWindowPreview @Composable -private fun EditAddressPrivatePreview() { +private fun EditAddressPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val store = createStore( address = generateAddress(), ) - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { EditAddressScreen(store) } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/edit/EditAddressTopBar.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/edit/EditAddressTopBar.kt index ff1dbd9742362..75f2d3b7d0eca 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/edit/EditAddressTopBar.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/edit/EditAddressTopBar.kt @@ -18,7 +18,7 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.compose.base.button.IconButton import mozilla.components.concept.storage.Address @@ -31,6 +31,7 @@ import org.mozilla.fenix.settings.address.store.SaveTapped import org.mozilla.fenix.settings.address.store.isEditing import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -101,44 +102,27 @@ private val AddressState.titleId: Int R.string.addresses_add_address } -@PreviewLightDark -@Composable -private fun AddTopBarPreview() { - val store = AddressStore(AddressState.initial(), listOf()) - - FirefoxTheme { - EditAddressTopBar(store) - } -} - @Preview @Composable -private fun AddTopBarPrivatePreview() { +private fun AddTopBarPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val store = AddressStore(AddressState.initial(), listOf()) - FirefoxTheme(theme = Theme.Private) { - EditAddressTopBar(store) - } -} - -@PreviewLightDark -@Composable -private fun EditTopBarPreview() { - val address = Address("BEEF", "Work", "Mozilla", "", "", "", "", "", "", "", "") - val store = AddressStore(AddressState.initial(address = address), listOf()) - - FirefoxTheme { + FirefoxTheme(theme) { EditAddressTopBar(store) } } @Preview @Composable -private fun EditTopBarPrivatePreview() { +private fun EditTopBarPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val address = Address("BEEF", "Work", "Mozilla", "", "", "", "", "", "", "", "") val store = AddressStore(AddressState.initial(address = address), listOf()) - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { EditAddressTopBar(store) } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/list/AddressList.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/list/AddressList.kt index 4d622cc1c6f33..9950b36b6d2fd 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/list/AddressList.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/ui/list/AddressList.kt @@ -12,9 +12,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.concept.storage.Address import org.mozilla.fenix.R import org.mozilla.fenix.compose.list.IconListItem @@ -22,6 +22,7 @@ import org.mozilla.fenix.compose.list.TextListItem import org.mozilla.fenix.settings.address.ext.getAddressLabel import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -78,24 +79,12 @@ private val addresses = listOf( ), ) -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun AddressListPreview() { - FirefoxTheme { - Surface { - AddressList( - addresses = addresses, - onAddressClick = {}, - onAddAddressButtonClick = {}, - ) - } - } -} - -@Preview -@Composable -private fun AddressListPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun AddressListPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { AddressList( addresses = addresses, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/biometric/ui/UnlockScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/biometric/ui/UnlockScreen.kt index 9e9631bbfad39..b0fbf5f1ab973 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/biometric/ui/UnlockScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/biometric/ui/UnlockScreen.kt @@ -4,7 +4,6 @@ package org.mozilla.fenix.settings.biometric.ui -import android.content.res.Configuration import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -23,9 +22,9 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.FilledButton import mozilla.components.compose.base.button.TextButton import mozilla.components.compose.base.utils.getResolvedAttrResId @@ -33,13 +32,10 @@ import org.mozilla.fenix.R import org.mozilla.fenix.ext.isLargeWindow import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider private const val FILL_WIDTH_LARGE_WINDOW = 0.5f private const val FILL_WIDTH_DEFAULT = 1.0f -private const val PHONE_WIDTH = 400 -private const val PHONE_HEIGHT = 640 -private const val TABLET_WIDTH = 700 -private const val TABLET_HEIGHT = 1280 /** * A screen allowing users to unlock their logins. @@ -141,20 +137,11 @@ private fun Footer(onUnlockClicked: () -> Unit, onLeaveClicked: () -> Unit) { } } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun UnlockLoginsScreenPreview() = UnlockLoginsScreenContent(Theme.getTheme()) - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = PHONE_WIDTH, heightDp = PHONE_HEIGHT) -@Composable -private fun UnlockLoginsScreenPreviewPrivatePhone() = UnlockLoginsScreenContent(Theme.Private) - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = TABLET_WIDTH, heightDp = TABLET_HEIGHT) -@Composable -private fun UnlockLoginsScreenPreviewPrivateTablet() = UnlockLoginsScreenContent(Theme.Private) - -@Composable -private fun UnlockLoginsScreenContent(theme: Theme) { +private fun UnlockLoginsScreenContent( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { FirefoxTheme(theme) { UnlockScreen( title = "Unlock to view your secure feature", diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/datachoices/DataChoicesScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/datachoices/DataChoicesScreen.kt index 35e6ee3309bbc..af3774f706c56 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/datachoices/DataChoicesScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/datachoices/DataChoicesScreen.kt @@ -26,9 +26,9 @@ import androidx.compose.ui.semantics.testTag import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.lib.crash.store.CrashReportOption import mozilla.components.lib.state.ext.observeAsComposableState import org.mozilla.fenix.R @@ -40,6 +40,7 @@ import org.mozilla.fenix.compose.list.TextListItem import org.mozilla.fenix.compose.settings.SettingsSectionHeader import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * Composable function that renders the Data Choices settings screen. @@ -333,10 +334,12 @@ private fun LearnMoreLink(onLearnMoreClicked: () -> Unit, learnMoreText: String) } } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun DataChoicesPreview() { - FirefoxTheme { +private fun DataChoicesPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { DataChoicesScreen( store = DataChoicesStore( initialState = DataChoicesState(), @@ -347,20 +350,10 @@ private fun DataChoicesPreview() { @Preview @Composable -private fun DataChoicesPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { - DataChoicesScreen( - store = DataChoicesStore( - initialState = DataChoicesState(), - ), - ) - } -} - -@PreviewLightDark -@Composable -private fun DataChoicesTelemetryDisabledPreview() { - FirefoxTheme { +private fun DataChoicesTelemetryDisabledPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { DataChoicesScreen( store = DataChoicesStore( initialState = DataChoicesState( @@ -374,23 +367,10 @@ private fun DataChoicesTelemetryDisabledPreview() { @Preview @Composable -private fun DataChoicesTelemetryDisabledPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { - DataChoicesScreen( - store = DataChoicesStore( - initialState = DataChoicesState( - studiesEnabled = false, - telemetryEnabled = false, - ), - ), - ) - } -} - -@PreviewLightDark -@Composable -private fun DataChoicesMarketingSectionDisabledPreview() { - FirefoxTheme { +private fun DataChoicesMarketingSectionDisabledPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { DataChoicesScreen( store = DataChoicesStore( initialState = DataChoicesState( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/addexception/AddExceptionScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/addexception/AddExceptionScreen.kt index c7599f49053c4..09903862c4764 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/addexception/AddExceptionScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/addexception/AddExceptionScreen.kt @@ -18,9 +18,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.FilledButton import mozilla.components.compose.base.textfield.TextField import org.mozilla.fenix.R @@ -28,6 +28,7 @@ import org.mozilla.fenix.settings.doh.DohSettingsState import org.mozilla.fenix.settings.doh.ProtectionLevel import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * Composable function that displays the exceptions list screen of DoH settings. @@ -73,32 +74,12 @@ internal fun AddExceptionScreen( } } +@FlexibleWindowPreview @Composable -@FlexibleWindowLightDarkPreview -private fun AddExceptionScreenPreview() { - FirefoxTheme { - AddExceptionScreen( - state = DohSettingsState( - allProtectionLevels = listOf( - ProtectionLevel.Default, - ProtectionLevel.Increased, - ProtectionLevel.Max, - ProtectionLevel.Off, - ), - selectedProtectionLevel = ProtectionLevel.Off, - providers = emptyList(), - selectedProvider = null, - exceptionsList = emptyList(), - isUserExceptionValid = false, - ), - ) - } -} - -@Composable -@Preview -private fun AddExceptionScreenPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun AddExceptionScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { AddExceptionScreen( state = DohSettingsState( allProtectionLevels = listOf( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/exceptionslist/ExceptionsListScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/exceptionslist/ExceptionsListScreen.kt index 86abac77cad73..d1a90e39f2f8c 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/exceptionslist/ExceptionsListScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/exceptionslist/ExceptionsListScreen.kt @@ -19,9 +19,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.DestructiveButton import org.mozilla.fenix.R import org.mozilla.fenix.compose.list.FaviconListItem @@ -30,6 +30,7 @@ import org.mozilla.fenix.settings.doh.DohSettingsState import org.mozilla.fenix.settings.doh.ProtectionLevel import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -115,20 +116,12 @@ private fun createState() = DohSettingsState( isUserExceptionValid = true, ) +@FlexibleWindowPreview @Composable -@FlexibleWindowLightDarkPreview -private fun ExceptionsListScreenPreview() { - FirefoxTheme { - ExceptionsListScreen( - state = createState(), - ) - } -} - -@Composable -@Preview -private fun ExceptionsListScreenPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun ExceptionsListScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { ExceptionsListScreen( state = createState(), ) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/info/InfoScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/info/InfoScreen.kt index 5441edd1144ce..42a816c1e2a2d 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/info/InfoScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/info/InfoScreen.kt @@ -20,15 +20,16 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import org.mozilla.fenix.R import org.mozilla.fenix.compose.LinkText import org.mozilla.fenix.compose.LinkTextState import org.mozilla.fenix.settings.SupportUtils import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * Composable function that displays the info screen of DoH settings. @@ -207,20 +208,12 @@ internal enum class InfoScreenTopic( ), } +@FlexibleWindowPreview @Composable -@FlexibleWindowLightDarkPreview -private fun InfoScreenPreview() { - FirefoxTheme { - InfoScreen( - infoScreenTopic = InfoScreenTopic.DEFAULT, - ) - } -} - -@Composable -@Preview -private fun InfoScreenPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun InfoScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { InfoScreen( infoScreenTopic = InfoScreenTopic.DEFAULT, ) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/root/DohSettingsScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/root/DohSettingsScreen.kt index 3ff22c3732ee5..a8299a9eafe91 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/root/DohSettingsScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/doh/root/DohSettingsScreen.kt @@ -35,10 +35,10 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.compose.base.Dropdown -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.IconButton import mozilla.components.compose.base.button.TextButton import mozilla.components.compose.base.menu.MenuItem @@ -56,6 +56,7 @@ import org.mozilla.fenix.settings.doh.ProtectionLevel import org.mozilla.fenix.settings.doh.Provider import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -529,10 +530,12 @@ private fun ExceptionsRow(onExceptionsClicked: () -> Unit) { ) } +@FlexibleWindowPreview @Composable -@FlexibleWindowLightDarkPreview -private fun DohScreenDefaultProviderPreview() { - FirefoxTheme { +private fun DohScreenDefaultProviderPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { val provider = Provider.BuiltIn( url = "https://mozilla.cloudflare-dns.com/dns-query", name = "Cloudflare", @@ -558,10 +561,12 @@ private fun DohScreenDefaultProviderPreview() { } } +@FlexibleWindowPreview @Composable -@FlexibleWindowLightDarkPreview -private fun DohScreenCustomProviderPreview() { - FirefoxTheme { +private fun DohScreenCustomProviderPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { val provider = Provider.Custom(url = "") DohSettingsScreen( state = DohSettingsState( @@ -583,64 +588,12 @@ private fun DohScreenCustomProviderPreview() { } } -@Composable @Preview -private fun DohScreenDefaultProviderPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { - val provider = Provider.BuiltIn( - url = "https://mozilla.cloudflare-dns.com/dns-query", - name = "Cloudflare", - default = true, - ) - DohSettingsScreen( - state = DohSettingsState( - allProtectionLevels = listOf( - ProtectionLevel.Default, - ProtectionLevel.Increased, - ProtectionLevel.Max, - ProtectionLevel.Off, - ), - selectedProtectionLevel = ProtectionLevel.Increased, - providers = listOf( - provider, - ), - selectedProvider = provider, - exceptionsList = emptyList(), - isUserExceptionValid = true, - ), - ) - } -} - @Composable -@Preview -private fun DohScreenCustomProviderPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { - val provider = Provider.Custom(url = "") - DohSettingsScreen( - state = DohSettingsState( - allProtectionLevels = listOf( - ProtectionLevel.Default, - ProtectionLevel.Increased, - ProtectionLevel.Max, - ProtectionLevel.Off, - ), - selectedProtectionLevel = ProtectionLevel.Increased, - providers = listOf( - provider, - ), - selectedProvider = provider, - exceptionsList = emptyList(), - isUserExceptionValid = true, - ), - ) - } -} - -@Composable -@PreviewLightDark -private fun AlertDialogAddCustomProviderPreview() { - FirefoxTheme { +private fun AlertDialogAddCustomProviderPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { AlertDialogAddCustomProvider( customProviderUrl = "https://mozilla.cloudflare-dns.com/dns-query", customProviderErrorState = CustomProviderErrorState.Valid, @@ -650,10 +603,12 @@ private fun AlertDialogAddCustomProviderPreview() { } } -@Composable @Preview -private fun AlertDialogAddCustomProviderPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun AlertDialogAddCustomProviderErrorPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { AlertDialogAddCustomProvider( customProviderUrl = "https://mozilla.cloudflare-dns.com/dns-query", customProviderErrorState = CustomProviderErrorState.Invalid, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/labs/ui/FirefoxLabsScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/labs/ui/FirefoxLabsScreen.kt index ac5ac1efbe997..26b3eeac185c2 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/labs/ui/FirefoxLabsScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/labs/ui/FirefoxLabsScreen.kt @@ -30,7 +30,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp @@ -50,6 +49,7 @@ import org.mozilla.fenix.settings.labs.store.LabsState import org.mozilla.fenix.settings.labs.store.LabsStore import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -358,10 +358,12 @@ private fun FirefoxLabsScreenPrivatePreview( } } +@Preview @Composable -@PreviewLightDark -private fun ToggleFeatureDialogPreview() { - FirefoxTheme { +private fun ToggleFeatureDialogPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { ToggleFeatureDialog( featureEnabled = true, onConfirm = {}, @@ -370,10 +372,12 @@ private fun ToggleFeatureDialogPreview() { } } +@Preview @Composable -@PreviewLightDark -private fun RestoreDefaultsDialogPreview() { - FirefoxTheme { +private fun RestoreDefaultsDialogPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { RestoreDefaultsDialog( onConfirm = {}, onDismiss = {}, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/AddLoginScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/AddLoginScreen.kt index bf0288278522a..5ae6c00a05c3f 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/AddLoginScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/AddLoginScreen.kt @@ -33,9 +33,9 @@ import androidx.compose.ui.semantics.testTag import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.IconButton import mozilla.components.compose.base.text.Text import mozilla.components.compose.base.textfield.TextField @@ -45,6 +45,7 @@ import mozilla.components.support.ktx.util.URLStringUtils.isValidHost import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR @Composable @@ -250,24 +251,15 @@ private fun AddLoginPassword(store: LoginsStore) { ) } +@FlexibleWindowPreview @Composable -@FlexibleWindowLightDarkPreview -private fun AddLoginScreenPreview() { +private fun AddLoginScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val store = LoginsStore( initialState = LoginsState.default, ) - FirefoxTheme { - AddLoginScreen(store) - } -} - -@Composable -@Preview -private fun AddLoginScreenPrivatePreview() { - val store = LoginsStore( - initialState = LoginsState.default, - ) - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { AddLoginScreen(store) } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/EditLoginScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/EditLoginScreen.kt index 9e6c9d87a33f5..3fc1396853b11 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/EditLoginScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/EditLoginScreen.kt @@ -34,9 +34,9 @@ import androidx.compose.ui.semantics.testTag import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.IconButton import mozilla.components.compose.base.text.Text import mozilla.components.compose.base.textfield.TextField @@ -44,6 +44,7 @@ import mozilla.components.lib.state.ext.observeAsState import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR @Composable @@ -270,20 +271,12 @@ private fun createStore() = LoginsStore( ), ) +@FlexibleWindowPreview @Composable -@FlexibleWindowLightDarkPreview -private fun EditLoginScreenPreview() { - FirefoxTheme { - Surface { - EditLoginScreen(store = createStore()) - } - } -} - -@Composable -@Preview -private fun EditLoginScreenPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun EditLoginScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { EditLoginScreen(store = createStore()) } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/LoginDetailsScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/LoginDetailsScreen.kt index f0de5137f593c..5753eb34eb06c 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/LoginDetailsScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/LoginDetailsScreen.kt @@ -39,11 +39,11 @@ import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.IconButton import mozilla.components.compose.base.button.TextButton import mozilla.components.compose.base.menu.DropdownMenu @@ -56,6 +56,7 @@ import mozilla.components.lib.state.ext.observeAsState import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR @Composable @@ -394,37 +395,22 @@ private fun createStore() = LoginsStore( ), ) +@FlexibleWindowPreview @Composable -@FlexibleWindowLightDarkPreview -private fun LoginDetailsScreenPreview() { - FirefoxTheme { +private fun LoginDetailsScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { LoginDetailsScreen(store = createStore()) } } -@Composable @Preview -private fun LoginDetailsScreenPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { - LoginDetailsScreen(store = createStore()) - } -} - -@Composable -@PreviewLightDark -private fun LoginDeletionDialogPreview() { - FirefoxTheme { - LoginDeletionDialog( - onCancelTapped = {}, - onDeleteTapped = {}, - ) - } -} - @Composable -@Preview -private fun LoginDeletionDialogPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun LoginDeletionDialogPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { LoginDeletionDialog( onCancelTapped = {}, onDeleteTapped = {}, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/SavedLoginsScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/SavedLoginsScreen.kt index 8d48409684cb5..6a4be769acd00 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/SavedLoginsScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/SavedLoginsScreen.kt @@ -43,13 +43,13 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTag import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.IconButton import mozilla.components.compose.base.menu.DropdownMenu import mozilla.components.compose.base.menu.MenuItem @@ -65,6 +65,7 @@ import org.mozilla.fenix.settings.biometric.ui.SecureScreen import org.mozilla.fenix.settings.logins.ui.LoginsSortOrder.Alphabetical.isGuidToDelete import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -425,34 +426,22 @@ private fun createStore() = LoginsStore( ), ) +@FlexibleWindowPreview @Composable -@FlexibleWindowLightDarkPreview -private fun LoginsListScreenPreview() { - FirefoxTheme { - LoginsList(store = createStore()) - } -} - -@Composable -@Preview -private fun LoginsListScreenPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun LoginsListScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { LoginsList(store = createStore()) } } +@FlexibleWindowPreview @Composable -@FlexibleWindowLightDarkPreview -private fun EmptyLoginsListScreenPreview() { - FirefoxTheme { - LoginsList(store = LoginsStore()) - } -} - -@Composable -@Preview -private fun EmptyLoginsListScreenPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun EmptyLoginsListScreenPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { LoginsList(store = LoginsStore()) } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/TrailingIcons.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/TrailingIcons.kt index f7f1cbe08adec..1a879ae526c0e 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/TrailingIcons.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/ui/TrailingIcons.kt @@ -18,7 +18,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.compose.base.button.IconButton import mozilla.components.compose.base.text.Text @@ -26,6 +26,7 @@ import mozilla.components.compose.base.text.value import mozilla.components.compose.base.textfield.TextField import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -55,45 +56,14 @@ fun EyePasswordIconButton( } } -@PreviewLightDark -@Composable -private fun EyePasswordIconButtonPreview() { - var isPasswordVisible by remember { mutableStateOf(false) } - - FirefoxTheme { - Surface { - TextField( - value = "password", - onValueChange = {}, - isEnabled = true, - placeholder = "", - errorText = "", - modifier = Modifier - .fillMaxWidth() - .padding(8.dp), - label = "", - trailingIcon = { - EyePasswordIconButton( - isPasswordVisible = isPasswordVisible, - onTrailingIconClick = { isPasswordVisible = !isPasswordVisible }, - ) - }, - visualTransformation = if (isPasswordVisible) { - VisualTransformation.None - } else { - PasswordVisualTransformation() - }, - ) - } - } -} - @Preview @Composable -private fun EyePasswordIconButtonPrivatePreview() { +private fun EyePasswordIconButtonPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { var isPasswordVisible by remember { mutableStateOf(false) } - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { Surface { TextField( value = "password", diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/search/SearchEngineShortcuts.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/search/SearchEngineShortcuts.kt index 250728f96bb16..157169d4e873a 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/search/SearchEngineShortcuts.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/search/SearchEngineShortcuts.kt @@ -33,7 +33,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import androidx.core.graphics.createBitmap @@ -42,7 +42,7 @@ import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.SearchState import mozilla.components.browser.state.state.availableSearchEngines import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.IconButton import mozilla.components.compose.base.menu.DropdownMenu import mozilla.components.compose.base.menu.MenuItem @@ -52,6 +52,7 @@ import mozilla.components.lib.state.ext.observeAsComposableState import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -278,32 +279,12 @@ private fun generateFakeEngines( ) } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable -private fun SearchEngineShortcutsPreview() { - FirefoxTheme { - SearchEngineShortcuts( - categoryTitle = stringResource(id = R.string.preferences_category_engines_in_search_menu), - store = BrowserStore( - initialState = BrowserState( - search = SearchState( - regionSearchEngines = generateFakeEnginesList(), - disabledSearchEngineIds = listOf("7", "8"), - ), - ), - ), - onCheckboxClicked = { _, _ -> }, - onEditEngineClicked = {}, - onDeleteEngineClicked = {}, - onAddEngineClicked = {}, - ) - } -} - -@Preview -@Composable -private fun SearchEngineShortcutsPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun SearchEngineShortcutsPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { SearchEngineShortcuts( categoryTitle = stringResource(id = R.string.preferences_category_engines_in_search_menu), store = BrowserStore( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/trustpanel/ui/ProtectionPanelHeader.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/trustpanel/ui/ProtectionPanelHeader.kt index e69bddb505269..0d65495354f75 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/trustpanel/ui/ProtectionPanelHeader.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/trustpanel/ui/ProtectionPanelHeader.kt @@ -26,13 +26,14 @@ import androidx.compose.ui.semantics.testTag import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl import org.mozilla.fenix.compose.Favicon import org.mozilla.fenix.settings.trustpanel.store.WebsiteInfoState import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider private val ICON_SIZE = 16.dp private val ICON_PADDING = 8.dp @@ -119,10 +120,12 @@ private fun ProtectionPanelIcon( } } -@PreviewLightDark +@Preview @Composable -private fun ProtectionPanelHeaderPreview() { - FirefoxTheme { +private fun ProtectionPanelHeaderPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { ProtectionPanelHeader( websiteInfoState = WebsiteInfoState( isSecured = true, @@ -136,10 +139,12 @@ private fun ProtectionPanelHeaderPreview() { } } -@PreviewLightDark +@Preview @Composable -private fun ProtectionPanelHeaderUrlAsTitlePreview() { - FirefoxTheme { +private fun ProtectionPanelHeaderUrlAsTitlePreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { ProtectionPanelHeader( websiteInfoState = WebsiteInfoState( isSecured = true, @@ -152,20 +157,3 @@ private fun ProtectionPanelHeaderUrlAsTitlePreview() { ) } } - -@Preview -@Composable -private fun ProtectionPanelHeaderPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { - ProtectionPanelHeader( - websiteInfoState = WebsiteInfoState( - isSecured = false, - websiteUrl = "https://www.mozilla.org", - websiteTitle = "Mozilla", - certificate = null, - ), - icon = null, - modifier = Modifier.background(color = MaterialTheme.colorScheme.surface), - ) - } -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/wallpaper/WallpaperSettings.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/wallpaper/WallpaperSettings.kt index da1347021d359..c41ac7e714603 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/wallpaper/WallpaperSettings.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/wallpaper/WallpaperSettings.kt @@ -45,13 +45,15 @@ import androidx.compose.ui.semantics.onClick import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.core.graphics.createBitmap -import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview +import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.modifier.debouncedClickable import org.mozilla.fenix.R import org.mozilla.fenix.compose.ClickableSubstringLink import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import org.mozilla.fenix.wallpapers.Wallpaper /** @@ -311,61 +313,12 @@ private fun WallpaperThumbnailItem( } } -@FlexibleWindowLightDarkPreview +@FlexibleWindowPreview @Composable @Suppress("MagicNumber") -private fun WallpaperThumbnailsPreview() { - val downloadedCollection: List = List(size = 5) { index -> - Wallpaper( - name = "downloaded wallpaper $index", - textColor = 0L, - cardColorLight = 0L, - cardColorDark = 0L, - thumbnailFileState = Wallpaper.ImageFileState.Downloaded, - assetsFileState = Wallpaper.ImageFileState.Downloaded, - collection = Wallpaper.ClassicFirefoxCollection, - ) - } + Wallpaper.Default - val downloadingCollection: List = List(size = 5) { index -> - Wallpaper( - name = "downloading wallpaper $index", - textColor = 0L, - cardColorLight = 0L, - cardColorDark = 0L, - thumbnailFileState = Wallpaper.ImageFileState.Downloading, - assetsFileState = Wallpaper.ImageFileState.Downloading, - collection = Wallpaper.ClassicFirefoxCollection, - ) - } - var selectedWallpaper by remember { mutableStateOf(downloadedCollection[0]) } - - FirefoxTheme { - WallpaperSettings( - defaultWallpaper = Wallpaper.Default, - loadWallpaperResource = { wallpaper -> - if (wallpaper == Wallpaper.Default) { - null - } else { - createBitmap(100, 100, Bitmap.Config.ARGB_8888) - } - }, - wallpaperGroups = mapOf( - Wallpaper.DefaultCollection to downloadedCollection, - Wallpaper.ClassicFirefoxCollection to downloadingCollection, - ), - selectedWallpaper = selectedWallpaper, - onSelectWallpaper = { wallpaper -> - selectedWallpaper = wallpaper - }, - onLearnMoreClick = { _, _ -> }, - ) - } -} - -@FlexibleWindowLightDarkPreview -@Composable -@Suppress("MagicNumber") -private fun WallpaperThumbnailsPrivatePreview() { +private fun WallpaperThumbnailsPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { val downloadedCollection: List = List(size = 5) { index -> Wallpaper( name = "downloaded wallpaper $index", @@ -390,7 +343,7 @@ private fun WallpaperThumbnailsPrivatePreview() { } var selectedWallpaper by remember { mutableStateOf(downloadedCollection[0]) } - FirefoxTheme(theme = Theme.Private) { + FirefoxTheme(theme) { WallpaperSettings( defaultWallpaper = Wallpaper.Default, loadWallpaperResource = { wallpaper -> diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/DownloadIndicator.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/DownloadIndicator.kt index 30b29b6c467ba..98c12eabdff62 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/DownloadIndicator.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/DownloadIndicator.kt @@ -26,13 +26,14 @@ import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.role import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.compose.base.button.FilledButton import mozilla.components.compose.base.modifier.animateRotation import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -134,10 +135,12 @@ fun DownloadIndicator( ) } +@Preview @Composable -@PreviewLightDark -private fun DownloadIconIndicatorPreview() { - FirefoxTheme { +private fun DownloadIconIndicatorPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { DownloadIconIndicator( icon = painterResource(id = iconsR.drawable.mozac_ic_sync_24), @@ -149,25 +152,12 @@ private fun DownloadIconIndicatorPreview() { } } -@Composable @Preview -private fun DownloadIconIndicatorPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { - Surface { - DownloadIconIndicator( - icon = painterResource(id = iconsR.drawable.mozac_ic_sync_24), - contentDescription = stringResource( - id = R.string.translations_bottom_sheet_translating_in_progress, - ), - ) - } - } -} - @Composable -@PreviewLightDark -private fun DownloadInProgressIndicatorPreview() { - FirefoxTheme { +private fun DownloadInProgressIndicatorPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { DownloadInProgressIndicator( contentDescription = stringResource( @@ -178,41 +168,12 @@ private fun DownloadInProgressIndicatorPreview() { } } -@Composable @Preview -private fun DownloadInProgressIndicatorPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { - Surface { - DownloadInProgressIndicator( - contentDescription = stringResource( - id = R.string.translations_bottom_sheet_translating_in_progress, - ), - ) - } - } -} - @Composable -@PreviewLightDark -private fun DownloadIndicatorPreview() { - FirefoxTheme { - Surface { - DownloadIndicator( - modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), - text = stringResource(id = R.string.translations_bottom_sheet_translating_in_progress), - contentDescription = stringResource( - id = R.string.translations_bottom_sheet_translating_in_progress_content_description, - ), - icon = painterResource(id = iconsR.drawable.mozac_ic_sync_24), - ) - } - } -} - -@Composable -@Preview -private fun DownloadIndicatorPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun DownloadIndicatorPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { DownloadIndicator( modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationOptionsDialog.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationOptionsDialog.kt index 2cf26a0c79878..0243bbe2cf222 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationOptionsDialog.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationOptionsDialog.kt @@ -27,7 +27,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.concept.engine.translate.TranslationError import org.mozilla.fenix.R @@ -37,6 +37,7 @@ import org.mozilla.fenix.compose.SwitchWithLabel import org.mozilla.fenix.compose.list.TextListItem import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import java.util.Locale import mozilla.components.ui.icons.R as iconsR @@ -239,28 +240,12 @@ fun getTranslationOptionsList(): List { } } -@Composable -@PreviewLightDark -private fun TranslationSettingsPreview() { - FirefoxTheme { - Surface { - Column { - TranslationOptionsDialog( - translationOptionsList = getTranslationOptionsList(), - showGlobalSettings = true, - onBackClicked = {}, - onTranslationSettingsClicked = {}, - aboutTranslationClicked = {}, - ) - } - } - } -} - -@Composable @Preview -private fun TranslationSettingsPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun TranslationSettingsPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { Column { TranslationOptionsDialog( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationSettings.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationSettings.kt index ee27f0e114b04..54422ab1ed858 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationSettings.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationSettings.kt @@ -21,7 +21,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.concept.engine.translate.TranslationError import org.mozilla.fenix.R @@ -31,6 +31,7 @@ import org.mozilla.fenix.compose.SwitchWithLabel import org.mozilla.fenix.compose.list.TextListItem import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * Translation Settings Fragment. @@ -196,26 +197,12 @@ internal fun getTranslationSettingsSwitchList(): List { } } -@Composable -@PreviewLightDark -private fun TranslationSettingsPreview() { - FirefoxTheme { - TranslationSettings( - translationSwitchList = getTranslationSettingsSwitchList(), - showAutomaticTranslations = true, - showNeverTranslate = true, - showDownloads = true, - onAutomaticTranslationClicked = {}, - onDownloadLanguageClicked = {}, - onNeverTranslationClicked = {}, - ) - } -} - -@Composable @Preview -private fun TranslationSettingPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun TranslationSettingsPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { TranslationSettings( translationSwitchList = getTranslationSettingsSwitchList(), showAutomaticTranslations = true, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationToolbar.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationToolbar.kt index 788d0b60b6f5b..3b7d2631ecd80 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationToolbar.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationToolbar.kt @@ -22,11 +22,12 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR private val ROUNDED_CORNER_SHAPE = RoundedCornerShape(topStart = 4.dp, topEnd = 4.dp) @@ -92,20 +93,12 @@ fun TranslationToolbar( } } -@PreviewLightDark -@Composable -private fun TranslationToolbarPreview() { - FirefoxTheme { - TranslationToolbar( - label = "Translated from French to English", - ) - } -} - @Preview @Composable -private fun TranslationToolbarPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun TranslationToolbarPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { TranslationToolbar( label = "Translated from French to English", ) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationsDialogBottomSheet.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationsDialogBottomSheet.kt index d7129c0b57701..670620972d742 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationsDialogBottomSheet.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/TranslationsDialogBottomSheet.kt @@ -34,7 +34,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import mozilla.components.compose.base.Dropdown @@ -51,6 +51,7 @@ import org.mozilla.fenix.compose.LinkText import org.mozilla.fenix.compose.LinkTextState import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import java.util.Locale import mozilla.components.ui.icons.R as iconsR @@ -731,35 +732,12 @@ internal fun getTranslateToLanguageList(): List { } } -@Composable -@PreviewLightDark -private fun TranslationsDialogBottomSheetPreview() { - FirefoxTheme { - Surface { - TranslationsDialogBottomSheet( - translationsDialogState = TranslationsDialogState( - positiveButtonType = PositiveButtonType.Enabled, - toLanguages = getTranslateToLanguageList(), - fromLanguages = getTranslateFromLanguageList(), - ), - learnMoreUrl = "", - showPageSettings = true, - showFirstTimeFlow = true, - onSettingClicked = {}, - onLearnMoreClicked = {}, - onPositiveButtonClicked = {}, - onNegativeButtonClicked = {}, - onFromDropdownSelected = {}, - onToDropdownSelected = {}, - ) - } - } -} - -@Composable @Preview -private fun TranslationsDialogBottomSheetPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun TranslationsDialogBottomSheetPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { Surface { TranslationsDialogBottomSheet( translationsDialogState = TranslationsDialogState( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/preferences/automatic/AutomaticTranslationOptionsPreference.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/preferences/automatic/AutomaticTranslationOptionsPreference.kt index 5263c6d353fd9..7b4790ea4e5bb 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/preferences/automatic/AutomaticTranslationOptionsPreference.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/preferences/automatic/AutomaticTranslationOptionsPreference.kt @@ -15,11 +15,12 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import org.mozilla.fenix.compose.list.RadioButtonListItem import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider /** * Firefox Automatic Translation Options preference screen. @@ -63,21 +64,12 @@ fun AutomaticTranslationOptionsPreference( } } -@Composable -@PreviewLightDark -private fun AutomaticTranslationOptionsPreview() { - FirefoxTheme { - AutomaticTranslationOptionsPreference( - selectedOption = AutomaticTranslationOptionPreference.AlwaysTranslate(), - onItemClick = {}, - ) - } -} - -@Composable @Preview -private fun AutomaticTranslationOptionsPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun AutomaticTranslationOptionsPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { AutomaticTranslationOptionsPreference( selectedOption = AutomaticTranslationOptionPreference.AlwaysTranslate(), onItemClick = {}, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/preferences/automatic/AutomaticTranslationPreference.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/preferences/automatic/AutomaticTranslationPreference.kt index 42b8832410001..ae468975cc2cc 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/preferences/automatic/AutomaticTranslationPreference.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/preferences/automatic/AutomaticTranslationPreference.kt @@ -18,7 +18,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.concept.engine.translate.Language import org.mozilla.fenix.R @@ -27,6 +27,7 @@ import org.mozilla.fenix.compose.InfoType import org.mozilla.fenix.compose.list.TextListItem import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import java.util.Locale /** @@ -137,44 +138,25 @@ internal fun getAutomaticTranslationListPreferences(): List { } } -@Composable -@PreviewLightDark -private fun DownloadLanguagesPreferencePreview() { - FirefoxTheme { - DownloadLanguagesPreference( - downloadLanguageItemPreferences = getLanguageListPreference(), - learnMoreUrl = "https://www.mozilla.org", - fileSizeFormatter = DefaultFileSizeFormatter(LocalContext.current), - onLearnMoreClicked = {}, - onItemClick = {}, - ) - } -} - -@Composable @Preview -private fun DownloadLanguagesPreferencePrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun DownloadLanguagesPreferencePreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { DownloadLanguagesPreference( downloadLanguageItemPreferences = getLanguageListPreference(), learnMoreUrl = "https://www.mozilla.org", diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/preferences/nevertranslatesite/NeverTranslateSitesPreference.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/preferences/nevertranslatesite/NeverTranslateSitesPreference.kt index cd7b63ed503f3..e5fbbfeb0cf99 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/preferences/nevertranslatesite/NeverTranslateSitesPreference.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/translations/preferences/nevertranslatesite/NeverTranslateSitesPreference.kt @@ -23,7 +23,7 @@ import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import org.mozilla.fenix.R import org.mozilla.fenix.compose.InfoCard @@ -31,6 +31,7 @@ import org.mozilla.fenix.compose.InfoType import org.mozilla.fenix.compose.list.TextListItem import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider import mozilla.components.ui.icons.R as iconsR /** @@ -121,10 +122,12 @@ internal fun getNeverTranslateSitesList(): List { } } +@Preview @Composable -@PreviewLightDark -private fun NeverTranslateSitePreferencePreview() { - FirefoxTheme { +private fun NeverTranslateSitePreferencePreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { NeverTranslateSitesPreference( neverTranslateSitesListPreferences = getNeverTranslateSitesList(), hasNeverTranslateSitesError = false, @@ -132,32 +135,12 @@ private fun NeverTranslateSitePreferencePreview() { } } -@Composable -@PreviewLightDark -private fun NeverTranslateSitePreferenceErrorPreview() { - FirefoxTheme { - NeverTranslateSitesPreference( - neverTranslateSitesListPreferences = getNeverTranslateSitesList(), - hasNeverTranslateSitesError = true, - ) {} - } -} - -@Composable @Preview -private fun NeverTranslateSitePreferencePrivatePreview() { - FirefoxTheme(theme = Theme.Private) { - NeverTranslateSitesPreference( - neverTranslateSitesListPreferences = getNeverTranslateSitesList(), - hasNeverTranslateSitesError = false, - ) {} - } -} - @Composable -@Preview -private fun NeverTranslateSitePreferenceErrorPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +private fun NeverTranslateSitePreferenceErrorPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { NeverTranslateSitesPreference( neverTranslateSitesListPreferences = getNeverTranslateSitesList(), hasNeverTranslateSitesError = true, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/webcompat/ui/WebCompatReporterPreviewSheet.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/webcompat/ui/WebCompatReporterPreviewSheet.kt index 1bdb6af6009a2..007b31832c798 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/webcompat/ui/WebCompatReporterPreviewSheet.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/webcompat/ui/WebCompatReporterPreviewSheet.kt @@ -29,13 +29,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.traversalIndex import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import mozilla.components.compose.base.button.FilledButton import org.mozilla.fenix.R import org.mozilla.fenix.compose.BottomSheetHandle import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import org.mozilla.fenix.theme.ThemeProvider @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -122,18 +123,12 @@ private fun WebCompatReporterPreviewSheetContent() { ) } -@Composable -@PreviewLightDark -private fun WebCompatReporterSheetPreview() { - FirefoxTheme { - WebCompatReporterPreviewSheetContent() - } -} - -@Composable @Preview -private fun WebCompatReporterSheetPrivatePreview() { - FirefoxTheme(theme = Theme.Private) { +@Composable +private fun WebCompatReporterSheetPreview( + @PreviewParameter(ThemeProvider::class) theme: Theme, +) { + FirefoxTheme(theme) { WebCompatReporterPreviewSheetContent() } } diff --git a/mobile/android/fenix/app/src/main/res/values-be/strings.xml b/mobile/android/fenix/app/src/main/res/values-be/strings.xml index 565d46c2268a6..f4401d7799ca9 100644 --- a/mobile/android/fenix/app/src/main/res/values-be/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-be/strings.xml @@ -580,6 +580,8 @@ Пачаць сінхранізацыю Дапамажыце нам будаваць лепшы інтэрнэт + + Дадаць віджэт Firefox @@ -1521,6 +1523,10 @@ Адпраўляць URL-адрасы, заблакаваныя аховай ад сачэння Узмоцненая ахова ад сачэння можа блакаваць трэкеры і\u00a0скрыпты, неабходныя для правільнай працы некаторых сайтаў. + + Перадпаказ справаздачы + + Папярэдні прагляд справаздачы Даслаць @@ -1531,6 +1537,8 @@ Не магу ўвайсці або зарэгістравацца Сайт запытаў адключыць блакаванне рэкламы + + Браўзер не падтрымліваецца або заблакаваны Нешта іншае Даслаць справаздачу пра крах + Падрабязней Падрабязней @@ -1587,10 +1596,16 @@ Адкрыць усё ў прыватных картках Выдаліць + + Налады сартавання Размеркаваць па найноўшых Размеркаваць па старэйшых + + Сартаваць ад А да Я + + Сартаваць ад Я да А Пакінуць водгук + + Налады пошуку + + Ачысціць пошук + + Перайсці назад + + Няма нядаўніх пошукаў + + Узнікла памылка + + Ачысціць усё + + Нядаўнія вынікі пошуку @@ -3111,5 +3144,12 @@ Нагадаць пазней + + Умовы выкарыстання + + + Паведамленне аб прыватнасці + + Падрабязней diff --git a/mobile/android/fenix/app/src/main/res/values-bqi/strings.xml b/mobile/android/fenix/app/src/main/res/values-bqi/strings.xml index 395b3d47e812c..cdfaee1961244 100644 --- a/mobile/android/fenix/app/src/main/res/values-bqi/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-bqi/strings.xml @@ -2964,7 +2964,7 @@ اوستان - Pin + دیسنیڌن کود پوستی diff --git a/mobile/android/fenix/app/src/main/res/values-de/strings.xml b/mobile/android/fenix/app/src/main/res/values-de/strings.xml index 51d50ae01830c..b79aca0f5cc4f 100644 --- a/mobile/android/fenix/app/src/main/res/values-de/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-de/strings.xml @@ -1998,6 +1998,10 @@ Ordner durchsuchen Element-Menü für %s + + Ordner ausklappen: %s + + Ordner schließen: %s Lesezeichen schließen @@ -4002,6 +4006,8 @@ Keine Ergebnisse Versuchen Sie es mit einer anderen Suchanfrage. + + Suchschaltfläche für Einstellungen diff --git a/mobile/android/fenix/app/src/main/res/values-dsb/strings.xml b/mobile/android/fenix/app/src/main/res/values-dsb/strings.xml index 14f6832653a73..43e1ff790de96 100644 --- a/mobile/android/fenix/app/src/main/res/values-dsb/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-dsb/strings.xml @@ -1998,6 +1998,10 @@ Zarědniki pśepytaś Zapiskowy meni za %s + + Zarědnik wócyniś: %s + + Zarědnik zacyniś: %s Cytańske znamjenja zacyniś @@ -4002,6 +4006,8 @@ Žedne wuslědki Snaź druge pytanje wopytaś? + + Tłocašk za pytanje nastajenjow diff --git a/mobile/android/fenix/app/src/main/res/values-en-rCA/strings.xml b/mobile/android/fenix/app/src/main/res/values-en-rCA/strings.xml index 35665445d4a08..5912c01bb164b 100644 --- a/mobile/android/fenix/app/src/main/res/values-en-rCA/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-en-rCA/strings.xml @@ -626,6 +626,82 @@ Agree and start browsing + + Continue + + Not now + + Get ready to run free + + Speedy, safe, and won’t sell you out.\nBrowsing just got better. + + By continuing, you agree to the %1$s. + + Firefox Terms of Use + + Firefox cares about your privacy. Learn more in our %1$s. + + Privacy Notice + + To help improve the browser, Firefox sends diagnostic and interaction data to Mozilla. %1$s + + Manage settings + + Say goodbye to creepy trackers + + We protect your data and automatically block companies from spying on your clicks. + + Set as default browser + + Choose your address bar + + Top + + Bottom + + Pick your theme + + Automatic + + Light + + Dark + + Instantly pick up where you left off + + Grab bookmarks, passwords, and more on any device in a snap. Your personal data stays safe and secure with encryption. + + Start syncing + + Help us build a better internet + + Share how you discovered Firefox, and that you use it, with Mozilla’s marketing partners. This data is never sold. %1$s + + How we use the data + + Notifications help you stay safer with Firefox + + Discover the latest privacy features in Firefox so you’re always up to date on how to stay protected. + + Turn on notifications + + Make every search more private + + Start every search from your phone’s home screen and know Firefox’s automatic protections have your back. + + Add Firefox widget @@ -1471,6 +1547,8 @@ Private Sync + + Search your tabs Share all tabs @@ -1513,12 +1591,18 @@ Remove tab from collection Select tabs + + No matches found. + + Try another search! Close tab Close tab %s Open tabs menu + + Return to tab manager Save tabs to collection @@ -1910,8 +1994,14 @@ Delete folder Search bookmarks + + Search folders Item Menu for %s + + Expand folder: %s + + Close folder: %s Close bookmarks @@ -1928,6 +2018,8 @@ Add bookmarks as you browse so you can find your favourite sites later. New folder + + Moved %1$s to %2$s Maybe try a different search? + + Settings search button @@ -3942,6 +4036,20 @@ Remind me later + + Terms of Use + + A note from %1$s + + You can learn more %1$s. + + here Item Menu for %s + + Expand folder: %s + + Close folder: %s Close bookmarks @@ -4002,6 +4006,8 @@ Nothing turned up Maybe try a different search? + + Settings search button diff --git a/mobile/android/fenix/app/src/main/res/values-eo/strings.xml b/mobile/android/fenix/app/src/main/res/values-eo/strings.xml index 2a7ac9fc4cbe3..e80f81ca76df3 100644 --- a/mobile/android/fenix/app/src/main/res/values-eo/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-eo/strings.xml @@ -1998,6 +1998,10 @@ Serĉi dosierojn Menuo por %s + + Malfaldi dosierujon: %s + + Fermi dosierujon: %s Fermi legosignojn @@ -4002,6 +4006,8 @@ Neniu rezulto Ĉu eble provi alian serĉon? + + Butono por serĉi agordojn diff --git a/mobile/android/fenix/app/src/main/res/values-es-rAR/strings.xml b/mobile/android/fenix/app/src/main/res/values-es-rAR/strings.xml index 307c75340de7d..b797165d323f9 100644 --- a/mobile/android/fenix/app/src/main/res/values-es-rAR/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-es-rAR/strings.xml @@ -1998,6 +1998,10 @@ Buscar carpetas Menú ítem para %s + + Expandir la carpeta: %s + + Cerrar la carpeta: %s Cerrar marcadores diff --git a/mobile/android/fenix/app/src/main/res/values-es-rCL/strings.xml b/mobile/android/fenix/app/src/main/res/values-es-rCL/strings.xml index dfafa4560bb9b..900c1069ec66c 100644 --- a/mobile/android/fenix/app/src/main/res/values-es-rCL/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-es-rCL/strings.xml @@ -1998,6 +1998,10 @@ Buscar carpetas Menú de elementos para %s + + Expandir carpeta: %s + + Cerrar carpeta: %s Cerrar marcadores @@ -4002,6 +4006,8 @@ No apareció nada ¿Quizás probar con otra búsqueda? + + Botón de búsqueda de ajustes diff --git a/mobile/android/fenix/app/src/main/res/values-eu/strings.xml b/mobile/android/fenix/app/src/main/res/values-eu/strings.xml index c7f78de7b36b9..981b053087438 100644 --- a/mobile/android/fenix/app/src/main/res/values-eu/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-eu/strings.xml @@ -1998,6 +1998,10 @@ Bilatu karpetak %s laster-markarako menu-elementua + + Zabaldu karpeta: %s + + Itxi karpeta: %s Itxi laster-markak @@ -4002,6 +4006,8 @@ Ez da ezer aurkitu Agian saiatu beste bilaketa batekin? + + Ezarpenen bilaketa-botoia diff --git a/mobile/android/fenix/app/src/main/res/values-fy-rNL/strings.xml b/mobile/android/fenix/app/src/main/res/values-fy-rNL/strings.xml index dc42827ea590c..056fef3732851 100644 --- a/mobile/android/fenix/app/src/main/res/values-fy-rNL/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-fy-rNL/strings.xml @@ -1998,6 +1998,10 @@ Mappen trochsykje Itemmenu foar %s + + Map útklappe: %s + + Map slute: %s Blêdwizers slute @@ -4002,6 +4006,8 @@ Neat fûn Miskien in oare sykopdracht probearje? + + Knop Ynstellingen sykje diff --git a/mobile/android/fenix/app/src/main/res/values-hsb/strings.xml b/mobile/android/fenix/app/src/main/res/values-hsb/strings.xml index f4cb3490c42a7..2a8cd5c1e260f 100644 --- a/mobile/android/fenix/app/src/main/res/values-hsb/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-hsb/strings.xml @@ -1998,6 +1998,10 @@ Rjadowaki přepytać Zapiskowy meni za %s + + Rjadowak wočinić: %s + + Rjadowak začinić: %s Zapołožki začinić @@ -4002,6 +4006,8 @@ Žane wuslědki Snano druhe pytanje spytać? + + Tłóčatko za pytanje nastajenjow diff --git a/mobile/android/fenix/app/src/main/res/values-hy-rAM/strings.xml b/mobile/android/fenix/app/src/main/res/values-hy-rAM/strings.xml index b9dae6522b193..406b4beb9089b 100644 --- a/mobile/android/fenix/app/src/main/res/values-hy-rAM/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-hy-rAM/strings.xml @@ -1998,6 +1998,10 @@ Փնտրել թղթապանակներ Միավորի ցանկը %s-ի համար + + Ընդարձակել պանակը՝ %s + + Փակել պանակը՝ %s Փակել էջանիշերը @@ -4002,6 +4006,8 @@ Ոչինչ չհայտնվեց Գուցե փորձեք այլ որոնում կատարե՞լ։ + + Կարգավորումների որոնման կոճակ diff --git a/mobile/android/fenix/app/src/main/res/values-ia/strings.xml b/mobile/android/fenix/app/src/main/res/values-ia/strings.xml index 2601b3cea21a7..f09109d3655b7 100644 --- a/mobile/android/fenix/app/src/main/res/values-ia/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-ia/strings.xml @@ -1998,6 +1998,10 @@ Cercar in plicas Menu de elementos pro %s + + Expander plica: %s + + Clauder plica: %s Clauder marcapaginas @@ -4002,6 +4006,8 @@ Necun resultato Proba tu un recerca differente? + + Button de recerca parametros diff --git a/mobile/android/fenix/app/src/main/res/values-it/strings.xml b/mobile/android/fenix/app/src/main/res/values-it/strings.xml index a2c2d8e00f099..11d0d37663d7b 100644 --- a/mobile/android/fenix/app/src/main/res/values-it/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-it/strings.xml @@ -1998,6 +1998,10 @@ Cerca nelle cartelle Menu per %s + + Espandi cartella: %s + + Chiudi cartella: %s Chiudi segnalibri @@ -4002,6 +4006,8 @@ Nessun risultato Prova con un’altra ricerca? + + Pulsante di ricerca nelle impostazioni diff --git a/mobile/android/fenix/app/src/main/res/values-iw/strings.xml b/mobile/android/fenix/app/src/main/res/values-iw/strings.xml index 52f0fe7089b79..a4b77160ede3f 100644 --- a/mobile/android/fenix/app/src/main/res/values-iw/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-iw/strings.xml @@ -317,6 +317,8 @@ סגירת תפריט השמירה סגירת תפריט הכלים + + סגירת גיליון תפריט לשונית מותאמת אישית הוספת העמוד לסימניות @@ -1996,6 +1998,10 @@ חיפוש בתיקיות תפריט הפריט עבור %s + + הרחבת התיקייה: %s + + סגירת התיקייה: %s סגירת סימניות @@ -3996,6 +4002,8 @@ לא נמצאו תוצאות אולי לנסות חיפוש אחר? + + כפתור חיפוש הגדרות diff --git a/mobile/android/fenix/app/src/main/res/values-kk/strings.xml b/mobile/android/fenix/app/src/main/res/values-kk/strings.xml index 8e72ccb9c4f3e..991086dd790b6 100644 --- a/mobile/android/fenix/app/src/main/res/values-kk/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-kk/strings.xml @@ -1998,6 +1998,10 @@ Бумалардан іздеу %s үшін элемент мәзірі + + Буманы жазық қылу: %s + + Буманы жабу: %s Бетбелгілерді жабу @@ -4002,6 +4006,8 @@ Ештеңе шықпады Басқа іздеуді қолданып қарау керек пе? + + Баптауларды іздеу батырмасы diff --git a/mobile/android/fenix/app/src/main/res/values-ko/strings.xml b/mobile/android/fenix/app/src/main/res/values-ko/strings.xml index 83e1af27c6223..6d9c99d11ff57 100644 --- a/mobile/android/fenix/app/src/main/res/values-ko/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-ko/strings.xml @@ -1998,6 +1998,10 @@ 폴더 검색 %s 항목 메뉴 + + 폴더 펼치기: %s + + 폴더 닫기: %s 북마크 닫기 @@ -4002,6 +4006,8 @@ 검색 결과 없음 다른 검색을 해보는 게 어떨까요? + + 설정 검색 버튼 diff --git a/mobile/android/fenix/app/src/main/res/values-nb-rNO/strings.xml b/mobile/android/fenix/app/src/main/res/values-nb-rNO/strings.xml index e796b9e5449e0..d035ced98a7a4 100644 --- a/mobile/android/fenix/app/src/main/res/values-nb-rNO/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-nb-rNO/strings.xml @@ -1998,6 +1998,10 @@ Søk i mapper Elementmeny for %s + + Utvid mappen: %s + + Lukk mappen: %s Lukk bokmerker @@ -3265,7 +3269,7 @@ sammenslått - fold ut + utvid utvidet @@ -4002,6 +4006,8 @@ Ingenting dukket opp Kanskje prøve et annet søk? + + Søkeknapp for innstillinger diff --git a/mobile/android/fenix/app/src/main/res/values-nl/strings.xml b/mobile/android/fenix/app/src/main/res/values-nl/strings.xml index 25d7c4d0c2776..511397e7f0441 100644 --- a/mobile/android/fenix/app/src/main/res/values-nl/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-nl/strings.xml @@ -1998,6 +1998,10 @@ Mappen doorzoeken Itemmenu voor %s + + Map uitvouwen: %s + + Map sluiten: %s Bladwijzers sluiten @@ -4002,6 +4006,8 @@ Niets gevonden Misschien een andere zoekopdracht proberen? + + Knop Instellingen zoeken diff --git a/mobile/android/fenix/app/src/main/res/values-nn-rNO/strings.xml b/mobile/android/fenix/app/src/main/res/values-nn-rNO/strings.xml index 4f6122d9a4144..5ae1156ec8904 100644 --- a/mobile/android/fenix/app/src/main/res/values-nn-rNO/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-nn-rNO/strings.xml @@ -1998,6 +1998,10 @@ Søk i mapper Elementmeny for %s + + Utvid mappe: %s + + Lat att mappe: %s Lat att bokmerke @@ -4002,6 +4006,8 @@ Ingenting dukka opp Prøve eit anna søk? + + Søkjeknapp for innstillingar diff --git a/mobile/android/fenix/app/src/main/res/values-ru/strings.xml b/mobile/android/fenix/app/src/main/res/values-ru/strings.xml index 551e5dfb0a03d..b68b2b7f0154b 100644 --- a/mobile/android/fenix/app/src/main/res/values-ru/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-ru/strings.xml @@ -1998,6 +1998,10 @@ Поиск в папках Элемент меню для %s + + Развернуть папку: %s + + Закрыть папку: %s Закрыть закладки @@ -4002,6 +4006,8 @@ Ничего не найдено Возможно, попробовать другой поиск? + + Кнопка поиска настроек diff --git a/mobile/android/fenix/app/src/main/res/values-sl/strings.xml b/mobile/android/fenix/app/src/main/res/values-sl/strings.xml index a6a79637e62a6..5cb432e84e636 100644 --- a/mobile/android/fenix/app/src/main/res/values-sl/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-sl/strings.xml @@ -1523,6 +1523,8 @@ Zasebno Sinhroniziraj + + Iskanje po zavihkih Deli vse zavihke @@ -1972,6 +1974,8 @@ Iskanje map Meni za %s + + Razširi mapo: %s Zapri zaznamke diff --git a/mobile/android/fenix/app/src/main/res/values-sv-rSE/strings.xml b/mobile/android/fenix/app/src/main/res/values-sv-rSE/strings.xml index 8519bd1be69d9..c51e57c43bfde 100644 --- a/mobile/android/fenix/app/src/main/res/values-sv-rSE/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-sv-rSE/strings.xml @@ -1998,6 +1998,10 @@ Sök i mappar Objektmeny för %s + + Expandera mapp: %s + + Stäng mapp: %s Stäng bokmärken @@ -4002,6 +4006,8 @@ Ingenting dök upp Testa en annan sökning? + + Sökknapp för inställningar diff --git a/mobile/android/fenix/app/src/main/res/values-th/strings.xml b/mobile/android/fenix/app/src/main/res/values-th/strings.xml index 1803c73fc0ee3..dbfa3d9700edf 100644 --- a/mobile/android/fenix/app/src/main/res/values-th/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-th/strings.xml @@ -1998,6 +1998,10 @@ ค้นหาโฟลเดอร์ เมนูรายการสำหรับ %s + + ขยายโฟลเดอร์: %s + + ปิดโฟลเดอร์: %s ปิดที่คั่นหน้า @@ -4002,6 +4006,8 @@ ไม่พบผลลัพธ์อะไรเลย ลองค้นหาแบบอื่นดูไหม? + + ปุ่มค้นหาการตั้งค่า diff --git a/mobile/android/fenix/app/src/main/res/values-tr/strings.xml b/mobile/android/fenix/app/src/main/res/values-tr/strings.xml index da6335b2e699e..8f5a190b9f47c 100644 --- a/mobile/android/fenix/app/src/main/res/values-tr/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-tr/strings.xml @@ -1998,6 +1998,10 @@ Klasörlerde ara %s öğe menüsü + + Klasörü genişlet: %s + + Klasörü kapat: %s Yer imlerini kapat @@ -4002,6 +4006,8 @@ Hiç sonuç bulunamadı Başka bir şey aramak ister misiniz? + + Ayarlarda arama düğmesi diff --git a/mobile/android/fenix/app/src/main/res/values-zh-rTW/strings.xml b/mobile/android/fenix/app/src/main/res/values-zh-rTW/strings.xml index c6a7cd1da8e7a..5610a54f4ab47 100644 --- a/mobile/android/fenix/app/src/main/res/values-zh-rTW/strings.xml +++ b/mobile/android/fenix/app/src/main/res/values-zh-rTW/strings.xml @@ -1998,6 +1998,10 @@ 搜尋資料夾 %s 的項目選單 + + 展開資料夾:%s + + 關閉資料夾:%s 關閉書籤 @@ -4002,6 +4006,8 @@ 什麼都沒找到 試試不同的搜尋條件? + + 搜尋選項按鈕 diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenMiddlewareTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenMiddlewareTest.kt index 43a018e615e0c..954b3e4972b4e 100644 --- a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenMiddlewareTest.kt +++ b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenMiddlewareTest.kt @@ -4,8 +4,8 @@ package org.mozilla.fenix.browser.store +import android.content.Context import androidx.fragment.app.FragmentManager -import androidx.test.ext.junit.runners.AndroidJUnit4 import io.mockk.every import io.mockk.mockk import io.mockk.spyk @@ -14,17 +14,16 @@ import mozilla.components.feature.downloads.ui.DownloadCancelDialogFragment import mozilla.components.lib.crash.CrashReporter import mozilla.components.lib.state.Middleware import mozilla.components.support.test.middleware.CaptureActionsMiddleware -import mozilla.components.support.test.robolectric.testContext import org.junit.Test -import org.junit.runner.RunWith import org.mozilla.fenix.browser.store.BrowserScreenAction.CancelPrivateDownloadsOnPrivateTabsClosedAccepted import org.mozilla.fenix.browser.store.BrowserScreenMiddleware.Companion.CANCEL_PRIVATE_DOWNLOADS_DIALOG_FRAGMENT_TAG -@RunWith(AndroidJUnit4::class) class BrowserScreenMiddlewareTest { private val fragmentManager: FragmentManager = mockk(relaxed = true) private val crashReporter: CrashReporter = mockk(relaxed = true) + private val testContext: Context = mockk() + @Test fun `WHEN the last private tab is closing THEN record a breadcrumb and show a warning dialog`() { val middleware = spyk(BrowserScreenMiddleware(testContext, crashReporter, fragmentManager)) diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenStoreKtTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenStoreKtTest.kt index a5e6b692c4b0d..0ae04f9470968 100644 --- a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenStoreKtTest.kt +++ b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenStoreKtTest.kt @@ -4,30 +4,20 @@ package org.mozilla.fenix.browser.store -import androidx.fragment.app.FragmentManager -import io.mockk.mockk import mozilla.components.concept.engine.translate.Language import mozilla.components.lib.state.Middleware -import mozilla.components.support.test.rule.MainCoroutineRule import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue -import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith import org.mozilla.fenix.browser.PageTranslationStatus import org.mozilla.fenix.browser.ReaderModeStatus import org.mozilla.fenix.browser.store.BrowserScreenAction.CancelPrivateDownloadsOnPrivateTabsClosedAccepted import org.mozilla.fenix.browser.store.BrowserScreenAction.ClosingLastPrivateTab import org.mozilla.fenix.browser.store.BrowserScreenAction.PageTranslationStatusUpdated import org.mozilla.fenix.browser.store.BrowserScreenAction.ReaderModeStatusUpdated -import org.robolectric.RobolectricTestRunner -@RunWith(RobolectricTestRunner::class) class BrowserScreenStoreKtTest { - @get:Rule - val mainCoroutineRule = MainCoroutineRule() - private val fragmentManager: FragmentManager = mockk() @Test fun `WHEN the last private tab is closed THEN reset whether cancelling all private downloads was accepted`() { diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenStoreReducerTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenStoreReducerTest.kt index b85b10c7c69c0..b2e2afa749d2b 100644 --- a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenStoreReducerTest.kt +++ b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenStoreReducerTest.kt @@ -9,13 +9,10 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test -import org.junit.runner.RunWith import org.mozilla.fenix.browser.store.BrowserScreenAction.CancelPrivateDownloadsOnPrivateTabsClosedAccepted import org.mozilla.fenix.browser.store.BrowserScreenAction.ClosingLastPrivateTab import org.mozilla.fenix.browser.store.BrowserScreenAction.CustomTabColorsUpdated -import org.robolectric.RobolectricTestRunner -@RunWith(RobolectricTestRunner::class) class BrowserScreenStoreReducerTest { @Test fun `WHEN closing the last private tab THEN reset the state of accepting the risks`() { diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenStoreTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenStoreTest.kt index 174e13e4b93a7..50f5cce08ba38 100644 --- a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenStoreTest.kt +++ b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/browser/store/BrowserScreenStoreTest.kt @@ -4,23 +4,13 @@ package org.mozilla.fenix.browser.store -import androidx.lifecycle.Lifecycle -import androidx.test.ext.junit.runners.AndroidJUnit4 -import mozilla.components.support.test.rule.MainCoroutineRule import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue -import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith import org.mozilla.fenix.browser.store.BrowserScreenAction.CancelPrivateDownloadsOnPrivateTabsClosedAccepted import org.mozilla.fenix.browser.store.BrowserScreenAction.ClosingLastPrivateTab -import org.mozilla.fenix.helpers.lifecycle.TestLifecycleOwner -@RunWith(AndroidJUnit4::class) class BrowserScreenStoreTest { - @get:Rule - val coroutinesTestRule = MainCoroutineRule() - private val lifecycleOwner = TestLifecycleOwner(Lifecycle.State.RESUMED) @Test fun `WHEN closing the last private tab THEN remember this in state`() { diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/BrowserVisualCompletenessMiddlewareTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/BrowserVisualCompletenessMiddlewareTest.kt index 32607f23f4ef3..fae0413ad13ea 100644 --- a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/BrowserVisualCompletenessMiddlewareTest.kt +++ b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/BrowserVisualCompletenessMiddlewareTest.kt @@ -1,20 +1,16 @@ package org.mozilla.fenix.components +import kotlinx.coroutines.test.runTest import mozilla.components.browser.state.action.ContentAction import mozilla.components.support.test.mock -import mozilla.components.support.test.rule.MainCoroutineRule import mozilla.components.support.utils.RunWhenReadyQueue import org.junit.Assert.assertTrue -import org.junit.Rule import org.junit.Test class BrowserVisualCompletenessMiddlewareTest { - @get:Rule - val coroutineTestRule = MainCoroutineRule() - @Test - fun `WHEN first contentful paint occurs THEN queue is marked as ready`() { - val queue = RunWhenReadyQueue() + fun `WHEN first contentful paint occurs THEN queue is marked as ready`() = runTest { + val queue = RunWhenReadyQueue(this) val middleware = BrowserVisualCompletenessMiddleware(queue) middleware.invoke(mock(), mock(), ContentAction.UpdateFirstContentfulPaintStateAction("id", true)) diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivityTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivityTest.kt index 498a0f858c54e..15288ac5725e7 100644 --- a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivityTest.kt +++ b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivityTest.kt @@ -99,6 +99,8 @@ class ExternalAppBrowserActivityTest { every { bundle.getString(any()) } returns "" every { intent.extras } returns bundle every { intent.getBooleanExtra(any(), any()) } returns false + every { intent.dataString } returns null + return intent } }, @@ -123,6 +125,8 @@ class ExternalAppBrowserActivityTest { every { bundle.getString(any()) } returns "" every { intent.getBooleanExtra(any(), any()) } returns true every { intent.extras } returns bundle + every { intent.dataString } returns null + return intent } }, @@ -151,6 +155,8 @@ class ExternalAppBrowserActivityTest { every { bundle.getString(any()) } returns "" every { intent.getBooleanExtra(any(), any()) } returns false every { intent.extras } returns bundle + every { intent.dataString } returns null + return intent } }, diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/extension/WebExtensionPromptFeatureTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/extension/WebExtensionPromptFeatureTest.kt index d09114da5359d..4d41b38af7538 100644 --- a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/extension/WebExtensionPromptFeatureTest.kt +++ b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/extension/WebExtensionPromptFeatureTest.kt @@ -19,7 +19,6 @@ import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.webextension.WebExtensionInstallException import mozilla.components.feature.addons.Addon import mozilla.components.support.ktx.android.content.appVersionName -import mozilla.components.support.test.ext.joinBlocking import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.test.rule.MainCoroutineRule import org.junit.Assert.assertFalse @@ -268,7 +267,14 @@ class WebExtensionPromptFeatureTest { @Test fun `GIVEN Optional Permissions WHEN handleAfterInstallationRequest is called THEN handleOptionalPermissionsRequest is called`() { webExtensionPromptFeature.start() - val request = mockk(relaxed = true) + + val request = mockk(relaxed = true) { + every { extension } returns mockk(relaxed = true) { + every { getMetadata() } returns mockk(relaxed = true) { + every { updateDate } returns "2023-10-27T10:15:30.500Z" + } + } + } webExtensionPromptFeature.handleAfterInstallationRequest(request) diff --git a/mobile/android/fenix/tools/data_renewal_request.py b/mobile/android/fenix/tools/data_renewal_request.py index 9d23a788130a4..cdec3fe6a8f5b 100755 --- a/mobile/android/fenix/tools/data_renewal_request.py +++ b/mobile/android/fenix/tools/data_renewal_request.py @@ -29,19 +29,19 @@ if row["keep(Y/N)"] == "n": continue total_count += 1 - output_string += f'` {row["name"]}`\n' + output_string += f"` {row['name']}`\n" output_string += "1) Provide a link to the initial Data Collection Review Request for this collection.\n" - output_string += f' - {eval(row["data_reviews"])[0]}\n' + output_string += f" - {eval(row['data_reviews'])[0]}\n" output_string += "\n" output_string += "2) When will this collection now expire?\n" if len(row["new expiry version"]) == 0: output_string += f" - {updated_version}\n" else: - output_string += f' - {row["new expiry version"]}\n' + output_string += f" - {row['new expiry version']}\n" output_string += "\n" output_string += "3) Why was the initial period of collection insufficient?\n" - output_string += f' - {row["reason to extend"]}\n' + output_string += f" - {row['reason to extend']}\n" output_string += "\n" output_string += "———\n" diff --git a/mobile/android/fenix/tools/update-glean-tags.py b/mobile/android/fenix/tools/update-glean-tags.py index b9e9e7e08e768..e8863a0497bfe 100755 --- a/mobile/android/fenix/tools/update-glean-tags.py +++ b/mobile/android/fenix/tools/update-glean-tags.py @@ -5,6 +5,7 @@ See https://mozilla.github.io/glean/book/reference/yaml/tags.html """ + import urllib from pathlib import Path diff --git a/mobile/android/focus-android/app/build.gradle b/mobile/android/focus-android/app/build.gradle index 803294d09e780..250e550fba1ca 100644 --- a/mobile/android/focus-android/app/build.gradle +++ b/mobile/android/focus-android/app/build.gradle @@ -243,15 +243,6 @@ nimbus { } dependencies { - // We pick up JNA transitively by way of Glean, which is currently on version 5.14.0. - // However, we need to force version 5.17.0 due to Google Play's 16KB page size requirement. - // JNA 5.15.0+ crashes on Android <7, however, so it can't be bumped in Glean at this time. - // Therefore, manually force the use of version 5.17.0 at the app level since we only support - // running on Android 8+ now anyway. This can be removed once Glean updates. - constraints { - implementation(libs.jna) - } - implementation project(':components:browser-domains') implementation project(':components:browser-engine-gecko') implementation project(':components:browser-errorpages') diff --git a/mobile/android/focus-android/tools/data_renewal_request.py b/mobile/android/focus-android/tools/data_renewal_request.py index 9d23a788130a4..cdec3fe6a8f5b 100755 --- a/mobile/android/focus-android/tools/data_renewal_request.py +++ b/mobile/android/focus-android/tools/data_renewal_request.py @@ -29,19 +29,19 @@ if row["keep(Y/N)"] == "n": continue total_count += 1 - output_string += f'` {row["name"]}`\n' + output_string += f"` {row['name']}`\n" output_string += "1) Provide a link to the initial Data Collection Review Request for this collection.\n" - output_string += f' - {eval(row["data_reviews"])[0]}\n' + output_string += f" - {eval(row['data_reviews'])[0]}\n" output_string += "\n" output_string += "2) When will this collection now expire?\n" if len(row["new expiry version"]) == 0: output_string += f" - {updated_version}\n" else: - output_string += f' - {row["new expiry version"]}\n' + output_string += f" - {row['new expiry version']}\n" output_string += "\n" output_string += "3) Why was the initial period of collection insufficient?\n" - output_string += f' - {row["reason to extend"]}\n' + output_string += f" - {row['reason to extend']}\n" output_string += "\n" output_string += "———\n" diff --git a/mobile/android/focus-android/tools/update-glean-tags.py b/mobile/android/focus-android/tools/update-glean-tags.py index 0096f3a939ee8..e4acf433f8445 100755 --- a/mobile/android/focus-android/tools/update-glean-tags.py +++ b/mobile/android/focus-android/tools/update-glean-tags.py @@ -5,6 +5,7 @@ See https://mozilla.github.io/glean/book/reference/yaml/tags.html """ + import urllib from pathlib import Path diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebAuthnTokenManager.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebAuthnTokenManager.java index 2eea4c6af1763..453e55b619c00 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebAuthnTokenManager.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebAuthnTokenManager.java @@ -135,7 +135,7 @@ private static PublicKeyCredentialCreationOptions getRequestOptionsForMakeCreden } final AuthenticationExtensions ext = extBuilder.build(); - // requireUserVerification are not yet consumed by Android's API + // userVerification are not yet consumed by Android's FIDO API final List excludedList = new ArrayList(); diff --git a/mobile/android/gradle/plugins/apilint/apidoc-plugin/src/test/resources/apidoc_test.py b/mobile/android/gradle/plugins/apilint/apidoc-plugin/src/test/resources/apidoc_test.py index 2f579f517c910..19b1fa94fe5da 100644 --- a/mobile/android/gradle/plugins/apilint/apidoc-plugin/src/test/resources/apidoc_test.py +++ b/mobile/android/gradle/plugins/apilint/apidoc-plugin/src/test/resources/apidoc_test.py @@ -20,47 +20,41 @@ output = args.out_dir + "/api.txt" -sp.check_call( - [ - args.javadoc, - "-doclet", - "org.mozilla.doclet.ApiDoclet", - "-docletpath", - args.doclet_jar, - "-subpackages", - "org.mozilla.test", - "-sourcepath", - args.java_root, - "-root-dir", - args.java_root, - "-skip-class-regex", - "TestSkippedClass$:^org.mozilla.test.TestClass.TestSkippedClass2$", - "-output", - output, - ] -) - -result = sp.call( - [ - "python3", - "../apilint/src/main/resources/diff.py", - "--existing", - args.expected, - "--local", - output, - ] -) - -result_map = sp.call( - [ - "python3", - "../apilint/src/main/resources/diff.py", - "--existing", - args.expected_map, - "--local", - output + ".map", - ] -) +sp.check_call([ + args.javadoc, + "-doclet", + "org.mozilla.doclet.ApiDoclet", + "-docletpath", + args.doclet_jar, + "-subpackages", + "org.mozilla.test", + "-sourcepath", + args.java_root, + "-root-dir", + args.java_root, + "-skip-class-regex", + "TestSkippedClass$:^org.mozilla.test.TestClass.TestSkippedClass2$", + "-output", + output, +]) + +result = sp.call([ + "python3", + "../apilint/src/main/resources/diff.py", + "--existing", + args.expected, + "--local", + output, +]) + +result_map = sp.call([ + "python3", + "../apilint/src/main/resources/diff.py", + "--existing", + args.expected_map, + "--local", + output + ".map", +]) # result == 0 from `diff` means that the files are identical if result != 0 or result_map != 0: diff --git a/mobile/android/gradle/plugins/apilint/apilint/src/main/resources/apilint.py b/mobile/android/gradle/plugins/apilint/apilint/src/main/resources/apilint.py index 3b151ee2689b6..5fe34044959c6 100644 --- a/mobile/android/gradle/plugins/apilint/apilint/src/main/resources/apilint.py +++ b/mobile/android/gradle/plugins/apilint/apilint/src/main/resources/apilint.py @@ -449,9 +449,12 @@ def __init__(self, pkg, location, raw, blame, imports): self.name = self.fullname[self.fullname.rindex(".") + 1 :] def __hash__(self): - return hash( - (self.raw, tuple(self.ctors), tuple(self.fields), tuple(self.methods)) - ) + return hash(( + self.raw, + tuple(self.ctors), + tuple(self.fields), + tuple(self.methods), + )) def __repr__(self): return self.raw diff --git a/mobile/android/gradle/plugins/apilint/apilint/src/main/resources/changelog-check.py b/mobile/android/gradle/plugins/apilint/apilint/src/main/resources/changelog-check.py index 501cfc7231cc1..4fa15ef813042 100644 --- a/mobile/android/gradle/plugins/apilint/apilint/src/main/resources/changelog-check.py +++ b/mobile/android/gradle/plugins/apilint/apilint/src/main/resources/changelog-check.py @@ -73,16 +73,14 @@ def dumpJsonError(info): if args.result_json is None: return if info is not None: - results["failures"].append( - { - "column": info["column"], - "file": args.changelog_file.name, - "line": info["line"], - "msg": info["message"], - "rule": info["rule"], - "error": True, - } - ) + results["failures"].append({ + "column": info["column"], + "file": args.changelog_file.name, + "line": info["line"], + "msg": info["message"], + "rule": info["rule"], + "error": True, + }) args.result_json.seek(0) args.result_json.truncate(0) json.dump(results, args.result_json) @@ -90,16 +88,14 @@ def dumpJsonError(info): try: (lineNumber, expectedApiVersion) = findApiVersion(args.changelog_file) except MissingApiVersionError: - dumpJsonError( - { - "column": 0, - "line": 1, - "message": "The api changelog file does not have a version pin. " - "Please update the file and add the following line: " - f"[api-version]: {currentApiVersion}", - "rule": "missing_api_version", - } - ) + dumpJsonError({ + "column": 0, + "line": 1, + "message": "The api changelog file does not have a version pin. " + "Please update the file and add the following line: " + f"[api-version]: {currentApiVersion}", + "rule": "missing_api_version", + }) print( "ERROR: The api changelog file does not have a version pin. Please update" ) @@ -115,16 +111,14 @@ def dumpJsonError(info): sys.exit(11) if currentApiVersion != expectedApiVersion: - dumpJsonError( - { - "column": 14, - "line": lineNumber, - "message": "The api changelog file is out of date. Please update the " - "file and modify the [api-version] line as follows: " - f"[api-version]: {currentApiVersion}", - "rule": "wrong_api_version", - } - ) + dumpJsonError({ + "column": 14, + "line": lineNumber, + "message": "The api changelog file is out of date. Please update the " + "file and modify the [api-version] line as follows: " + f"[api-version]: {currentApiVersion}", + "rule": "wrong_api_version", + }) print("ERROR: The api changelog file is out of date. Please update the file at") print("") print(args.changelog_file.name) diff --git a/mobile/android/gradle/plugins/apilint/apilint/src/test/resources/apilint_test.py b/mobile/android/gradle/plugins/apilint/apilint/src/test/resources/apilint_test.py index 9b341d54b9186..4ac921bea5357 100644 --- a/mobile/android/gradle/plugins/apilint/apilint/src/test/resources/apilint_test.py +++ b/mobile/android/gradle/plugins/apilint/apilint/src/test/resources/apilint_test.py @@ -37,16 +37,14 @@ after_api = test_base + ".txt" if check_compat: - sp.call( - [ - "python3", - "src/main/resources/diff.py", - "--existing", - before_api, - "--local", - after_api, - ] - ) + sp.call([ + "python3", + "src/main/resources/diff.py", + "--existing", + before_api, + "--local", + after_api, + ]) json_file = "{}/{}-result.json".format(args.build_dir, t["test"]) test = [ diff --git a/mobile/android/mach_commands.py b/mobile/android/mach_commands.py index 020cc8f985994..4244dae4adbcf 100644 --- a/mobile/android/mach_commands.py +++ b/mobile/android/mach_commands.py @@ -569,15 +569,13 @@ def gradle(command_context, args, verbose=False, gradle_path=None, topsrcdir=Non env = os.environ.copy() - env.update( - { - "GRADLE_OPTS": "-Dfile.encoding=utf-8", - "JAVA_HOME": java_home, - "JAVA_TOOL_OPTIONS": "-Dfile.encoding=utf-8", - # Let Gradle get the right Python path on Windows - "GRADLE_MACH_PYTHON": sys.executable, - } - ) + env.update({ + "GRADLE_OPTS": "-Dfile.encoding=utf-8", + "JAVA_HOME": java_home, + "JAVA_TOOL_OPTIONS": "-Dfile.encoding=utf-8", + # Let Gradle get the right Python path on Windows + "GRADLE_MACH_PYTHON": sys.executable, + }) # Set ANDROID_SDK_ROOT if --with-android-sdk was set. # See https://bugzilla.mozilla.org/show_bug.cgi?id=1576471 android_sdk_root = command_context.substs.get("ANDROID_SDK_ROOT", "") diff --git a/mobile/locales/l10n-changesets.json b/mobile/locales/l10n-changesets.json index 80818cbf20abb..6eea9da8b78cd 100644 --- a/mobile/locales/l10n-changesets.json +++ b/mobile/locales/l10n-changesets.json @@ -6,7 +6,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "an": { "pin": false, @@ -15,7 +15,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ar": { "pin": false, @@ -24,7 +24,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ast": { "pin": false, @@ -33,7 +33,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "az": { "pin": false, @@ -42,7 +42,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "be": { "pin": false, @@ -51,7 +51,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "bg": { "pin": false, @@ -60,7 +60,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "bn": { "pin": false, @@ -69,7 +69,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "br": { "pin": false, @@ -78,7 +78,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "bs": { "pin": false, @@ -87,7 +87,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ca": { "pin": false, @@ -96,7 +96,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "cak": { "pin": false, @@ -105,7 +105,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "cs": { "pin": false, @@ -114,7 +114,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "cy": { "pin": false, @@ -123,7 +123,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "da": { "pin": false, @@ -132,7 +132,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "de": { "pin": false, @@ -141,7 +141,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "dsb": { "pin": false, @@ -150,7 +150,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "el": { "pin": false, @@ -159,7 +159,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "en-CA": { "pin": false, @@ -168,7 +168,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "en-GB": { "pin": false, @@ -177,7 +177,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "eo": { "pin": false, @@ -186,7 +186,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "es-AR": { "pin": false, @@ -195,7 +195,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "es-CL": { "pin": false, @@ -204,7 +204,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "es-ES": { "pin": false, @@ -213,7 +213,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "es-MX": { "pin": false, @@ -222,7 +222,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "et": { "pin": false, @@ -231,7 +231,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "eu": { "pin": false, @@ -240,7 +240,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "fa": { "pin": false, @@ -249,7 +249,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ff": { "pin": false, @@ -258,7 +258,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "fi": { "pin": false, @@ -267,7 +267,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "fr": { "pin": false, @@ -276,7 +276,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "fy-NL": { "pin": false, @@ -285,7 +285,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ga-IE": { "pin": false, @@ -294,7 +294,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "gd": { "pin": false, @@ -303,7 +303,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "gl": { "pin": false, @@ -312,7 +312,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "gn": { "pin": false, @@ -321,7 +321,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "gu-IN": { "pin": false, @@ -330,7 +330,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "he": { "pin": false, @@ -339,7 +339,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "hi-IN": { "pin": false, @@ -348,7 +348,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "hr": { "pin": false, @@ -357,7 +357,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "hsb": { "pin": false, @@ -366,7 +366,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "hu": { "pin": false, @@ -375,7 +375,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "hy-AM": { "pin": false, @@ -384,7 +384,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ia": { "pin": false, @@ -393,7 +393,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "id": { "pin": false, @@ -402,7 +402,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "is": { "pin": false, @@ -411,7 +411,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "it": { "pin": false, @@ -420,7 +420,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ja": { "pin": false, @@ -429,7 +429,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ka": { "pin": false, @@ -438,7 +438,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "kab": { "pin": false, @@ -447,7 +447,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "kk": { "pin": false, @@ -456,7 +456,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "km": { "pin": false, @@ -465,7 +465,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "kn": { "pin": false, @@ -474,7 +474,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ko": { "pin": false, @@ -483,7 +483,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "lij": { "pin": false, @@ -492,7 +492,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "lo": { "pin": false, @@ -501,7 +501,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "lt": { "pin": false, @@ -510,7 +510,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ltg": { "pin": false, @@ -519,7 +519,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "lv": { "pin": false, @@ -528,7 +528,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "meh": { "pin": false, @@ -537,7 +537,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "mix": { "pin": false, @@ -546,7 +546,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ml": { "pin": false, @@ -555,7 +555,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "mr": { "pin": false, @@ -564,7 +564,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ms": { "pin": false, @@ -573,7 +573,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "my": { "pin": false, @@ -582,7 +582,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "nb-NO": { "pin": false, @@ -591,7 +591,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ne-NP": { "pin": false, @@ -600,7 +600,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "nl": { "pin": false, @@ -609,7 +609,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "nn-NO": { "pin": false, @@ -618,7 +618,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "oc": { "pin": false, @@ -627,7 +627,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "pa-IN": { "pin": false, @@ -636,7 +636,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "pl": { "pin": false, @@ -645,7 +645,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "pt-BR": { "pin": false, @@ -654,7 +654,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "pt-PT": { "pin": false, @@ -663,7 +663,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "rm": { "pin": false, @@ -672,7 +672,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ro": { "pin": false, @@ -681,7 +681,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ru": { "pin": false, @@ -690,7 +690,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sk": { "pin": false, @@ -699,7 +699,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sl": { "pin": false, @@ -708,7 +708,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "son": { "pin": false, @@ -717,7 +717,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sq": { "pin": false, @@ -726,7 +726,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sr": { "pin": false, @@ -735,7 +735,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "sv-SE": { "pin": false, @@ -744,7 +744,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ta": { "pin": false, @@ -753,7 +753,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "te": { "pin": false, @@ -762,7 +762,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "th": { "pin": false, @@ -771,7 +771,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "tl": { "pin": false, @@ -780,7 +780,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "tr": { "pin": false, @@ -789,7 +789,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "trs": { "pin": false, @@ -798,7 +798,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "uk": { "pin": false, @@ -807,7 +807,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "ur": { "pin": false, @@ -816,7 +816,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "uz": { "pin": false, @@ -825,7 +825,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "vi": { "pin": false, @@ -834,7 +834,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "wo": { "pin": false, @@ -843,7 +843,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "xh": { "pin": false, @@ -852,7 +852,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "zam": { "pin": false, @@ -861,7 +861,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "zh-CN": { "pin": false, @@ -870,7 +870,7 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" }, "zh-TW": { "pin": false, @@ -879,6 +879,6 @@ "android-arm", "android-multilocale" ], - "revision": "94115fb79711bb1e27bc063c80ec36ce6178f04a" + "revision": "3492920c261e218aaef24f7e0050900af5ff8027" } } \ No newline at end of file diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index a3b203baac227..2317858133c55 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -7830,6 +7830,11 @@ mirror: once #endif +- name: gfx.webrender.dcomp-texture-overlay-win + type: bool + value: false + mirror: once + - name: gfx.webrender.scissored-cache-clears.enabled type: bool value: true diff --git a/modules/libpref/init/generate_static_pref_list.py b/modules/libpref/init/generate_static_pref_list.py index 06ac40372abec..d86607389faab 100644 --- a/modules/libpref/init/generate_static_pref_list.py +++ b/modules/libpref/init/generate_static_pref_list.py @@ -34,23 +34,21 @@ } VALID_TYPES = VALID_BOOL_TYPES.copy() -VALID_TYPES.update( - { - "int32_t": "int32_t", - "uint32_t": "uint32_t", - "float": "float", - # These ones are defined in StaticPrefsBase.h. - "RelaxedAtomicInt32": "int32_t", - "RelaxedAtomicUint32": "uint32_t", - "ReleaseAcquireAtomicInt32": "int32_t", - "ReleaseAcquireAtomicUint32": "uint32_t", - "SequentiallyConsistentAtomicInt32": "int32_t", - "SequentiallyConsistentAtomicUint32": "uint32_t", - "AtomicFloat": "float", - "String": None, - "DataMutexString": "nsACString", - } -) +VALID_TYPES.update({ + "int32_t": "int32_t", + "uint32_t": "uint32_t", + "float": "float", + # These ones are defined in StaticPrefsBase.h. + "RelaxedAtomicInt32": "int32_t", + "RelaxedAtomicUint32": "uint32_t", + "ReleaseAcquireAtomicInt32": "int32_t", + "ReleaseAcquireAtomicUint32": "uint32_t", + "SequentiallyConsistentAtomicInt32": "int32_t", + "SequentiallyConsistentAtomicUint32": "uint32_t", + "AtomicFloat": "float", + "String": None, + "DataMutexString": "nsACString", +}) # Map non-atomic C++ types to equivalent Rust types. RUST_TYPES = { diff --git a/modules/libpref/test/test_generate_static_pref_list.py b/modules/libpref/test/test_generate_static_pref_list.py index 503572e43d9a6..0062c0b049f63 100644 --- a/modules/libpref/test/test_generate_static_pref_list.py +++ b/modules/libpref/test/test_generate_static_pref_list.py @@ -108,18 +108,14 @@ # The corresponding code for good_input. good = {} -good[ - "static_pref_list_all_h" -] = """\ +good["static_pref_list_all_h"] = """\ // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. #include "mozilla/StaticPrefList_my.h" #include "mozilla/StaticPrefList_my_dashed.h" """ -good[ - "static_prefs_all_h" -] = """\ +good["static_prefs_all_h"] = """\ // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. #include "mozilla/StaticPrefs_my.h" @@ -217,9 +213,7 @@ """ } -good[ - "static_prefs_c_getters_cpp" -] = """\ +good["static_prefs_c_getters_cpp"] = """\ // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. extern "C" uint32_t StaticPrefs_my_uint() { @@ -240,9 +234,7 @@ } """ -good[ - "static_prefs_rs" -] = """\ +good["static_prefs_rs"] = """\ // This file was generated by generate_static_pref_list.py from (string input). DO NOT EDIT. pub use nsstring::nsCString; diff --git a/moz.configure b/moz.configure index 2babb391317ad..e7f32df5db54f 100755 --- a/moz.configure +++ b/moz.configure @@ -131,8 +131,7 @@ set_define("FMT_USE_LOCALE", 0) option( "--enable-debug", nargs="?", - help="Enable building with developer debug info " - "(using the given compiler flags)", + help="Enable building with developer debug info (using the given compiler flags)", ) diff --git a/mozglue/baseprofiler/build/generate_profiling_categories.py b/mozglue/baseprofiler/build/generate_profiling_categories.py index 2d1fc6d6481d2..19c4c9e74b31e 100644 --- a/mozglue/baseprofiler/build/generate_profiling_categories.py +++ b/mozglue/baseprofiler/build/generate_profiling_categories.py @@ -145,9 +145,9 @@ def generate_macro_header(c_out, yaml_path): color = category["color"] assert isinstance(color, str) subcategories = category.get("subcategories", None) - assert ( - isinstance(subcategories, list) and len(subcategories) > 0 - ), f"At least one subcategory expected as default in {name}." + assert isinstance(subcategories, list) and len(subcategories) > 0, ( + f"At least one subcategory expected as default in {name}." + ) category_items.append( generate_category_macro(name, label, color, subcategories) @@ -270,9 +270,9 @@ def generate_rust_enums(c_out, yaml_path): # This will be used as our main enum field and sub category enum. cat_label = "".join(filter(str.isalnum, cat_label)) cat_subcategories = category.get("subcategories", None) - assert ( - isinstance(cat_subcategories, list) and len(cat_subcategories) > 0 - ), f"At least one subcategory expected as default in {cat_name}." + assert isinstance(cat_subcategories, list) and len(cat_subcategories) > 0, ( + f"At least one subcategory expected as default in {cat_name}." + ) # Create a new enum for this sub category and append it to the enums list. category_enum = RustEnum(cat_label) @@ -300,9 +300,9 @@ def generate_rust_enums(c_out, yaml_path): ) profiling_category_pair_value += 1 - assert ( - category_enum.default_category is not None - ), "There must be a default subcategory with the same name." + assert category_enum.default_category is not None, ( + "There must be a default subcategory with the same name." + ) # Append the main enums. profiling_category_pair_enum.append_optional_tuple_field(cat_label) diff --git a/netwerk/protocol/http/binary_http/Cargo.toml b/netwerk/protocol/http/binary_http/Cargo.toml index 3913b3221a8a1..5e2996493aae2 100644 --- a/netwerk/protocol/http/binary_http/Cargo.toml +++ b/netwerk/protocol/http/binary_http/Cargo.toml @@ -6,6 +6,6 @@ edition = "2021" [dependencies] nserror = { path = "../../../../xpcom/rust/nserror" } nsstring = { path = "../../../../xpcom/rust/nsstring" } -bhttp = "0.6" +bhttp = "0.7.2" thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } xpcom = { path = "../../../../xpcom/rust/xpcom" } diff --git a/netwerk/protocol/http/oblivious_http/Cargo.toml b/netwerk/protocol/http/oblivious_http/Cargo.toml index 74a75fc9ff4d0..ecc2e9c3e3d74 100644 --- a/netwerk/protocol/http/oblivious_http/Cargo.toml +++ b/netwerk/protocol/http/oblivious_http/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] nserror = { path = "../../../../xpcom/rust/nserror" } -ohttp = { version = "0.6", default-features = false, features = ["gecko", "nss", "client", "server"] } +ohttp = { version = "0.7.2", default-features = false, features = ["gecko", "nss", "client", "server"] } rand = "0.8" thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } xpcom = { path = "../../../../xpcom/rust/xpcom" } diff --git a/netwerk/test/marionette/test_purge_http_cache_at_shutdown.py b/netwerk/test/marionette/test_purge_http_cache_at_shutdown.py index f138264b29e20..225eef90043b5 100644 --- a/netwerk/test/marionette/test_purge_http_cache_at_shutdown.py +++ b/netwerk/test/marionette/test_purge_http_cache_at_shutdown.py @@ -12,13 +12,11 @@ class PurgeHTTPCacheAtShutdownTestCase(MarionetteTestCase): def setUp(self): super().setUp() - self.marionette.enforce_gecko_prefs( - { - "privacy.sanitize.sanitizeOnShutdown": True, - "privacy.clearOnShutdown.cache": True, - "network.cache.shutdown_purge_in_background_task": True, - } - ) + self.marionette.enforce_gecko_prefs({ + "privacy.sanitize.sanitizeOnShutdown": True, + "privacy.clearOnShutdown.cache": True, + "network.cache.shutdown_purge_in_background_task": True, + }) self.profile_path = Path(self.marionette.profile_path) self.cache_path = self.profile_path.joinpath("cache2") diff --git a/netwerk/test/perf/hooks_throttling.py b/netwerk/test/perf/hooks_throttling.py index 5f46b3f0d35d3..2bfab52383c49 100644 --- a/netwerk/test/perf/hooks_throttling.py +++ b/netwerk/test/perf/hooks_throttling.py @@ -5,6 +5,7 @@ Drives the throttling feature when the test calls our controlled server. """ + import http.client import json import os diff --git a/pyproject.toml b/pyproject.toml index b1fb454914a7e..e618bcf1aa072 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.ruff] -line-length = 99 -target-version = "py39" +line-length = 88 +target-version = "py38" # See https://beta.ruff.rs/docs/rules/ for a full list of rules. lint.select = [ "E", "W", # pycodestyle @@ -20,8 +20,8 @@ lint.ignore = [ "PLC0206", # dict-index-missing-items "PLC0415", # `import` should be at the top-level of a file - # These are handled by black. - "E1", "E4", "E5", "W2", "W5", + # These are handled by ruff format. + "E1", "E4", "E5", "W1", "W2", "W5", ] builtins = ["gdb"] extend-include = ["*.configure"] @@ -72,6 +72,9 @@ exclude = [ "toolkit/moz.configure", "toolkit/nss.configure", + # Can be removed once remaining CI workers updated past Python 3.8 + "python/mozbuild/mozbuild/action/tooltool.py", + # mako files are not really python files "*.mako.py", @@ -133,3 +136,8 @@ exclude = [ "python/mozbuild/mozbuild/util.py" = ["F821"] "testing/mozharness/mozharness/mozilla/testing/android.py" = ["F821"] "testing/mochitest/runtests.py" = ["F821"] + +[tool.ruff.format] +# Enable preview mode for --output-format=json to get improved line/column reporting. +# TODO: Remove preview once these features are in the stable release. +preview = true diff --git a/python/mach/mach/commands/commandinfo.py b/python/mach/mach/commands/commandinfo.py index a3d6701db2413..e6b554dcb13d5 100644 --- a/python/mach/mach/commands/commandinfo.py +++ b/python/mach/mach/commands/commandinfo.py @@ -203,9 +203,9 @@ def run_completion(command_context, args): def _zsh_describe(value, description=None): value = '"' + value.replace(":", "\\:") if description: - description = subprocess.list2cmdline( - [re.sub(r'(["\'#&;`|*?~<>^()\[\]{}$\\\x0A\xFF])', r"\\\1", description)] - ).lstrip('"') + description = subprocess.list2cmdline([ + re.sub(r'(["\'#&;`|*?~<>^()\[\]{}$\\\x0A\xFF])', r"\\\1", description) + ]).lstrip('"') if description.endswith('"') and not description.endswith(r"\""): description = description[:-1] @@ -247,14 +247,12 @@ def completion_bash(command_context, outfile): if options: case_options.append( - "\n".join( - [ - f" ({cmd.name})", - ' opts="${{opts}} {}"'.format(" ".join(options)), - " ;;", - "", - ] - ) + "\n".join([ + f" ({cmd.name})", + ' opts="${{opts}} {}"'.format(" ".join(options)), + " ;;", + "", + ]) ) # Build case statement for subcommand options. @@ -266,14 +264,12 @@ def completion_bash(command_context, outfile): if options: case_options.append( - "\n".join( - [ - f' ("{sub.name} {sub.subcommand}")', - ' opts="${{opts}} {}"'.format(" ".join(options)), - " ;;", - "", - ] - ) + "\n".join([ + f' ("{sub.name} {sub.subcommand}")', + ' opts="${{opts}} {}"'.format(" ".join(options)), + " ;;", + "", + ]) ) # Build case statement for subcommands. @@ -281,25 +277,21 @@ def completion_bash(command_context, outfile): if subcommands: comsubs = " ".join([h.subcommand for h in cmd.subcommands]) case_commands_subcommands.append( - "\n".join( - [ - f" ({cmd.name})", - f' comsubs=" {comsubs} "', - " ;;", - "", - ] - ) + "\n".join([ + f" ({cmd.name})", + f' comsubs=" {comsubs} "', + " ;;", + "", + ]) ) case_subcommands.append( - "\n".join( - [ - f" ({cmd.name})", - ' subs="${{subs}} {}"'.format(" ".join(subcommands)), - " ;;", - "", - ] - ) + "\n".join([ + f" ({cmd.name})", + ' subs="${{subs}} {}"'.format(" ".join(subcommands)), + " ;;", + "", + ]) ) globalopts = [ @@ -345,14 +337,12 @@ def completion_zsh(command_context, outfile): if options: case_options.append( - "\n".join( - [ - f" ({cmd.name})", - " opts+=({})".format(" ".join(options)), - " ;;", - "", - ] - ) + "\n".join([ + f" ({cmd.name})", + " opts+=({})".format(" ".join(options)), + " ;;", + "", + ]) ) # Build case statement for subcommand options. @@ -364,14 +354,12 @@ def completion_zsh(command_context, outfile): if options: case_options.append( - "\n".join( - [ - f" ({sub.name} {sub.subcommand})", - " opts+=({})".format(" ".join(options)), - " ;;", - "", - ] - ) + "\n".join([ + f" ({sub.name} {sub.subcommand})", + " opts+=({})".format(" ".join(options)), + " ;;", + "", + ]) ) # Build case statement for subcommands. @@ -386,14 +374,12 @@ def completion_zsh(command_context, outfile): ) case_subcommands.append( - "\n".join( - [ - f" ({cmd.name})", - " subs+=({})".format(" ".join(subcommands)), - " ;;", - "", - ] - ) + "\n".join([ + f" ({cmd.name})", + " subs+=({})".format(" ".join(subcommands)), + " ;;", + "", + ]) ) globalopts = [] @@ -436,9 +422,8 @@ def _append_opt_strs(comp, opt_strs): globalopts = [] for opt_strs, description in global_options(command_context).items(): - comp = ( - "complete -c mach -n '__fish_mach_complete_no_command' " - "-d '{}'".format(description.replace("'", "\\'")) + comp = "complete -c mach -n '__fish_mach_complete_no_command' -d '{}'".format( + description.replace("'", "\\'") ) comp = _append_opt_strs(comp, opt_strs) globalopts.append(comp) diff --git a/python/mach/mach/decorators.py b/python/mach/mach/decorators.py index 9d9c229c13c97..d0dedd6cf704b 100644 --- a/python/mach/mach/decorators.py +++ b/python/mach/mach/decorators.py @@ -76,7 +76,7 @@ def __init__( self.hidden = hidden if ok_if_tests_disabled and category != "testing": raise ValueError( - "ok_if_tests_disabled should only be set for " "`testing` mach commands" + "ok_if_tests_disabled should only be set for `testing` mach commands" ) self.ok_if_tests_disabled = ok_if_tests_disabled diff --git a/python/mach/mach/dispatcher.py b/python/mach/mach/dispatcher.py index 54b0f9494f17e..0dfdff7952240 100644 --- a/python/mach/mach/dispatcher.py +++ b/python/mach/mach/dispatcher.py @@ -145,9 +145,10 @@ def __call__(self, parser, namespace, values, option_string=None): # If there are sub-commands, parse the intent out immediately. if handler.subcommand_handlers and args: # mach help - if set(args[: args.index("--")] if "--" in args else args).intersection( - ("help", "--help") - ): + if set(args[: args.index("--")] if "--" in args else args).intersection(( + "help", + "--help", + )): self._handle_subcommand_help(parser, handler, args) sys.exit(0) # mach ... diff --git a/python/mach/mach/main.py b/python/mach/mach/main.py index 9275def859b88..82a8f19ef5859 100644 --- a/python/mach/mach/main.py +++ b/python/mach/mach/main.py @@ -528,7 +528,7 @@ def get_argument_parser(context=None, action=CommandAction, topsrcdir=None): parser = ArgumentParser( add_help=False, - usage="%(prog)s [global arguments] " "command [command arguments]", + usage="%(prog)s [global arguments] command [command arguments]", ) # WARNING!!! If you add a global argument here, also add it to the diff --git a/python/mach/mach/registrar.py b/python/mach/mach/registrar.py index 20f9f4dd1d428..5dc6be33666ae 100644 --- a/python/mach/mach/registrar.py +++ b/python/mach/mach/registrar.py @@ -33,7 +33,7 @@ def register_command_handler(self, handler): if not handler.category: raise MachError( - "Cannot register a mach command without a " "category: %s" % name + "Cannot register a mach command without a category: %s" % name ) if handler.category not in self.categories: diff --git a/python/mach/mach/sentry.py b/python/mach/mach/sentry.py index b1a2513596c5b..0b59877df9baa 100644 --- a/python/mach/mach/sentry.py +++ b/python/mach/mach/sentry.py @@ -214,9 +214,9 @@ def _is_unmodified_mach_core(topsrcdir: Path): repo = _get_repository_object(topsrcdir) try: files = set(repo.get_outgoing_files()) | set(repo.get_changed_files()) - _is_unmodified_mach_core_result = not any( - [file for file in files if file == "mach" or file.endswith(".py")] - ) + _is_unmodified_mach_core_result = not any([ + file for file in files if file == "mach" or file.endswith(".py") + ]) except MissingUpstreamRepo: # If we don't know the upstream state, we don't know if the mach files # have been unmodified. diff --git a/python/mach/mach/site.py b/python/mach/mach/site.py index 99b3fc2634c36..32c0c0d0e9bf1 100644 --- a/python/mach/mach/site.py +++ b/python/mach/mach/site.py @@ -342,9 +342,9 @@ def from_environment( # yet, and the system isn't guaranteed to have the packages we need. For example, # "./mach bootstrap" can't have any dependencies. # So, all external dependencies of Mach's must be optional. - assert ( - not requirements.pypi_requirements - ), "Mach pip package requirements must be optional." + assert not requirements.pypi_requirements, ( + "Mach pip package requirements must be optional." + ) # external_python is the Python interpreter that invoked Mach for this process. external_python = ExternalPythonSite(sys.executable) @@ -607,9 +607,9 @@ def from_environment( should be created """ active_metadata = MozSiteMetadata.from_runtime() - assert ( - active_metadata - ), "A Mach-managed site must be active before doing work with command sites" + assert active_metadata, ( + "A Mach-managed site must be active before doing work with command sites" + ) mach_site_packages_source = active_metadata.mach_site_packages_source pip_restricted_site = site_name in PIP_NETWORK_INSTALL_RESTRICTED_VIRTUALENVS @@ -1044,12 +1044,10 @@ def pip_install_with_constraints(self, pip_args): constraints_path = os.path.join(tempdir, "site-constraints.txt") with open(constraints_path, "w") as file: file.write( - "\n".join( - [ - f"{name}=={version}" - for name, version in existing_packages.items() - ] - ) + "\n".join([ + f"{name}=={version}" + for name, version in existing_packages.items() + ]) ) self.pip_install(["--constraint", constraints_path] + pip_args) @@ -1536,9 +1534,11 @@ def _create_venv_with_pthfile( if populate_with_pip: for requirements_txt_file in requirements.requirements_txt_files: - target_venv.pip_install( - ["--requirement", requirements_txt_file.path, "--require-hashes"] - ) + target_venv.pip_install([ + "--requirement", + requirements_txt_file.path, + "--require-hashes", + ]) if requirements.pypi_requirements: requirements_list = [ str(req.requirement) for req in requirements.pypi_requirements diff --git a/python/mach/mach/test/script_site_activation.py b/python/mach/mach/test/script_site_activation.py index 8c23f1a19c92d..5e4278b7d36b4 100644 --- a/python/mach/mach/test/script_site_activation.py +++ b/python/mach/mach/test/script_site_activation.py @@ -54,13 +54,11 @@ def resolve_requirements(topsrcdir, site_name): ) command_site.activate() command_sys_path = sys.path.copy() - print( - [ - initial_sys_path, - mach_sys_path, - command_sys_path, - ] - ) + print([ + initial_sys_path, + mach_sys_path, + command_sys_path, + ]) if __name__ == "__main__": diff --git a/python/mach/mach/test/test_site.py b/python/mach/mach/test/test_site.py index 0e155dd321e07..2562ce379b252 100644 --- a/python/mach/mach/test/test_site.py +++ b/python/mach/mach/test/test_site.py @@ -142,9 +142,9 @@ def test_requirements_txt_install_requires_hashes( pytest.fail("Expected CalledProcessError to be raised due to missing hashes") except subprocess.CalledProcessError as e: error_output = e.stderr if e.stderr else "" - assert ( - "hash" in error_output.lower() - ), f"Expected hash error in stderr, got: {error_output}" + assert "hash" in error_output.lower(), ( + f"Expected hash error in stderr, got: {error_output}" + ) def test_requirements_txt_installs_with_hashes(run_create_venv_with_pthfile): diff --git a/python/mach/mach/test/test_site_activation.py b/python/mach/mach/test/test_site_activation.py index 5dd79d7dbc540..8e82436ed9759 100644 --- a/python/mach/mach/test/test_site_activation.py +++ b/python/mach/mach/test/test_site_activation.py @@ -63,14 +63,12 @@ def test_new_package_metadta_is_found(): pass with tempfile.TemporaryDirectory() as venv_dir: - subprocess.check_call( - [ - sys.executable, - "-m", - "venv", - venv_dir, - ] - ) + subprocess.check_call([ + sys.executable, + "-m", + "venv", + venv_dir, + ]) venv = PythonVirtualenv(venv_dir) venv.pip_install([f"{pkg}=={version}"]) diff --git a/python/mach/mach/util.py b/python/mach/mach/util.py index 6069219ba5161..f7f023567579c 100644 --- a/python/mach/mach/util.py +++ b/python/mach/mach/util.py @@ -121,4 +121,4 @@ def strtobool(value: str): if value in false_vals: return 0 - raise ValueError(f'Expected one of: {", ".join(true_vals + false_vals)}') + raise ValueError(f"Expected one of: {', '.join(true_vals + false_vals)}") diff --git a/python/mozboot/bin/bootstrap.py b/python/mozboot/bin/bootstrap.py index 84c25f92d92f3..6be38685493e9 100755 --- a/python/mozboot/bin/bootstrap.py +++ b/python/mozboot/bin/bootstrap.py @@ -444,7 +444,7 @@ def main(args): "--no-system-changes", dest="no_system_changes", action="store_true", - help="Only executes actions that leave the system " "configuration alone.", + help="Only executes actions that leave the system configuration alone.", ) options, leftover = parser.parse_args(args) diff --git a/python/mozboot/mozboot/base.py b/python/mozboot/mozboot/base.py index 323ab83c50049..1456bfdaa3529 100644 --- a/python/mozboot/mozboot/base.py +++ b/python/mozboot/mozboot/base.py @@ -667,18 +667,16 @@ def install_rust(self): rustup_init.chmod(mode | stat.S_IRWXU) print("Ok") print("Running rustup-init...") - subprocess.check_call( - [ - str(rustup_init), - "-y", - "--default-toolchain", - "stable", - "--default-host", - platform, - "--component", - "rustfmt", - ] - ) + subprocess.check_call([ + str(rustup_init), + "-y", + "--default-toolchain", + "stable", + "--default-host", + platform, + "--component", + "rustfmt", + ]) cargo_home, cargo_bin = self.cargo_home() self.print_rust_path_advice(RUST_INSTALL_COMPLETE, cargo_home, cargo_bin) finally: diff --git a/python/mozboot/mozboot/bootstrap.py b/python/mozboot/mozboot/bootstrap.py index 04e6a51a78a81..0036e37753efe 100644 --- a/python/mozboot/mozboot/bootstrap.py +++ b/python/mozboot/mozboot/bootstrap.py @@ -58,15 +58,13 @@ %s Your choice: """ -APPLICATIONS = OrderedDict( - [ - ("Firefox for Desktop Artifact Mode", "browser_artifact_mode"), - ("Firefox for Desktop", "browser"), - ("GeckoView/Firefox for Android Artifact Mode", "mobile_android_artifact_mode"), - ("GeckoView/Firefox for Android", "mobile_android"), - ("SpiderMonkey JavaScript engine", "js"), - ] -) +APPLICATIONS = OrderedDict([ + ("Firefox for Desktop Artifact Mode", "browser_artifact_mode"), + ("Firefox for Desktop", "browser"), + ("GeckoView/Firefox for Android Artifact Mode", "mobile_android_artifact_mode"), + ("GeckoView/Firefox for Android", "mobile_android"), + ("SpiderMonkey JavaScript engine", "js"), +]) FINISHED = """ Your system should be ready to build %s! @@ -320,7 +318,7 @@ def __init__( cls = WindowsBootstrapper if cls is None: raise NotImplementedError( - "Bootstrap support is not yet available " "for your OS." + "Bootstrap support is not yet available for your OS." ) self.instance = cls(**args) @@ -718,12 +716,10 @@ def current_firefox_checkout(env, hg: Optional[Path] = None): Returns one of None, ``git``, or ``hg``. """ - HG_ROOT_REVISIONS = set( - [ - # From mozilla-unified. - "8ba995b74e18334ab3707f27e9eb8f4e37ba3d29" - ] - ) + HG_ROOT_REVISIONS = set([ + # From mozilla-unified. + "8ba995b74e18334ab3707f27e9eb8f4e37ba3d29" + ]) path = Path.cwd() while path: diff --git a/python/mozboot/mozboot/linux_common.py b/python/mozboot/mozboot/linux_common.py index 3a21ce3ae1858..6bdb40aa5075b 100644 --- a/python/mozboot/mozboot/linux_common.py +++ b/python/mozboot/mozboot/linux_common.py @@ -56,17 +56,15 @@ def ensure_sccache_packages(self): pass def install_system_packages(self): - self.install_packages( - [ - "bash", - "findutils", # contains xargs - "gzip", - "make", - "perl", - "tar", - "unzip", - ] - ) + self.install_packages([ + "bash", + "findutils", # contains xargs + "gzip", + "make", + "perl", + "tar", + "unzip", + ]) # Optional packages try: self.install_packages(["watchman"]) diff --git a/python/mozboot/mozboot/mozconfig.py b/python/mozboot/mozboot/mozconfig.py index c6fedb049b52d..07d0f5361d5cb 100644 --- a/python/mozboot/mozboot/mozconfig.py +++ b/python/mozboot/mozboot/mozconfig.py @@ -117,7 +117,7 @@ def find_mozconfig(topsrcdir: Union[str, Path], env=os.environ): if not env_path.is_file(): raise MozconfigFindException( - "MOZCONFIG environment variable refers to a " f"non-file: {env_path}" + f"MOZCONFIG environment variable refers to a non-file: {env_path}" ) srcdir_paths = [topsrcdir / p for p in DEFAULT_TOPSRCDIR_PATHS] diff --git a/python/mozboot/mozboot/osx.py b/python/mozboot/mozboot/osx.py index 92f00098a2cc5..77fb4d2e5b121 100644 --- a/python/mozboot/mozboot/osx.py +++ b/python/mozboot/mozboot/osx.py @@ -215,18 +215,22 @@ def _ensure_homebrew_casks(self, casks): # Ensure that we can access old versions of packages. if b"homebrew/cask-versions" not in known_taps: - subprocess.check_output( - [to_optional_str(self.brew), "tap", "homebrew/cask-versions"] - ) + subprocess.check_output([ + to_optional_str(self.brew), + "tap", + "homebrew/cask-versions", + ]) # "caskroom/versions" has been renamed to "homebrew/cask-versions", so # it is safe to remove the old tap. Removing the old tap is necessary # to avoid the error "Cask [name of cask] exists in multiple taps". # See https://bugzilla.mozilla.org/show_bug.cgi?id=1544981 if b"caskroom/versions" in known_taps: - subprocess.check_output( - [to_optional_str(self.brew), "untap", "caskroom/versions"] - ) + subprocess.check_output([ + to_optional_str(self.brew), + "untap", + "caskroom/versions", + ]) self._ensure_homebrew_packages(casks, is_for_cask=True) diff --git a/python/mozbuild/mozbuild/action/check_binary.py b/python/mozbuild/mozbuild/action/check_binary.py index 7d4469999fb4c..e31137a1d0c7e 100644 --- a/python/mozbuild/mozbuild/action/check_binary.py +++ b/python/mozbuild/mozbuild/action/check_binary.py @@ -208,43 +208,39 @@ def check_mozglue_order(binary): def check_networking(binary): retcode = 0 - networking_functions = set( - [ - # socketpair is not concerning; it is restricted to AF_UNIX - "recv", - "send", - # We would be concerned by recvmsg and sendmsg; but we believe - # they are okay as documented in 1376621#c23 - "gethostbyname", - "gethostbyaddr", - "gethostent", - "sethostent", - "endhostent", - "gethostent_r", - "gethostbyname2", - "gethostbyaddr_r", - "gethostbyname_r", - "gethostbyname2_r", - "getservent", - "getservbyname", - "getservbyport", - "setservent", - "getprotoent", - "getprotobyname", - "getprotobynumber", - "setprotoent", - "endprotoent", - ] - ) + networking_functions = set([ + # socketpair is not concerning; it is restricted to AF_UNIX + "recv", + "send", + # We would be concerned by recvmsg and sendmsg; but we believe + # they are okay as documented in 1376621#c23 + "gethostbyname", + "gethostbyaddr", + "gethostent", + "sethostent", + "endhostent", + "gethostent_r", + "gethostbyname2", + "gethostbyaddr_r", + "gethostbyname_r", + "gethostbyname2_r", + "getservent", + "getservbyname", + "getservbyport", + "setservent", + "getprotoent", + "getprotobyname", + "getprotobynumber", + "setprotoent", + "endprotoent", + ]) # These are used by the crash monitor & crash monitor client to talk with # the main process on Linux and macOS. - socket_functions = set( - [ - "connect", - "accept", - "listen", - ] - ) + socket_functions = set([ + "connect", + "accept", + "listen", + ]) if PLATFORM == "WINNT": networking_functions |= socket_functions diff --git a/python/mozbuild/mozbuild/action/exe_7z_archive.py b/python/mozbuild/mozbuild/action/exe_7z_archive.py index b0d35be2bf88e..9c358022fcac8 100644 --- a/python/mozbuild/mozbuild/action/exe_7z_archive.py +++ b/python/mozbuild/mozbuild/action/exe_7z_archive.py @@ -45,23 +45,21 @@ def archive_exe(pkg_dir, tagfile, sfx_package, package, use_upx): except BuildEnvironmentNotFoundException: # configure hasn't been run, just use the default sevenz = "7z" - subprocess.check_call( - [ - sevenz, - "a", - "-r", - "-t7z", - mozpath.join(tmpdir, "app.7z"), - "-mx", - "-m0=BCJ2", - "-m1=LZMA:d25", - "-m2=LZMA:d19", - "-m3=LZMA:d19", - "-mb0:1", - "-mb0s1:2", - "-mb0s2:3", - ] - ) + subprocess.check_call([ + sevenz, + "a", + "-r", + "-t7z", + mozpath.join(tmpdir, "app.7z"), + "-mx", + "-m0=BCJ2", + "-m1=LZMA:d25", + "-m2=LZMA:d19", + "-m3=LZMA:d19", + "-mb0:1", + "-mb0s1:2", + "-mb0s2:3", + ]) with open(package, "wb") as o: for i in [final_sfx, tagfile, mozpath.join(tmpdir, "app.7z")]: diff --git a/python/mozbuild/mozbuild/action/fat_aar.py b/python/mozbuild/mozbuild/action/fat_aar.py index be791f75f5160..520365bb5f8bb 100644 --- a/python/mozbuild/mozbuild/action/fat_aar.py +++ b/python/mozbuild/mozbuild/action/fat_aar.py @@ -34,21 +34,19 @@ def _download_zip(distdir, arch): } dest = mozpath.join(distdir, "input", arch) - subprocess.check_call( - [ - sys.executable, - mozpath.join(buildconfig.topsrcdir, "mach"), - "artifact", - "install", - "--job", - jobs[arch], - "--distdir", - dest, - "--no-tests", - "--no-process", - "--maven-zip", - ] - ) + subprocess.check_call([ + sys.executable, + mozpath.join(buildconfig.topsrcdir, "mach"), + "artifact", + "install", + "--job", + jobs[arch], + "--distdir", + dest, + "--no-tests", + "--no-process", + "--maven-zip", + ]) return mozpath.join(dest, "target.maven.zip") diff --git a/python/mozbuild/mozbuild/action/langpack_manifest.py b/python/mozbuild/mozbuild/action/langpack_manifest.py index 7182cb495bb48..28f6244656b6b 100644 --- a/python/mozbuild/mozbuild/action/langpack_manifest.py +++ b/python/mozbuild/mozbuild/action/langpack_manifest.py @@ -64,13 +64,13 @@ def get_build_date(): ### def get_dt_from_hg(path): with mozversioncontrol.get_repository_object(path=path) as repo: - phase = repo._run("log", "-r", ".", "-T" "{phase}") + phase = repo._run("log", "-r", ".", "-T{phase}") if phase.strip() != "public": return get_build_date() repo_url = repo._run("paths", "default") repo_url = repo_url.strip().replace("ssh://", "https://") repo_url = repo_url.replace("hg://", "https://") - cs = repo._run("log", "-r", ".", "-T" "{node}") + cs = repo._run("log", "-r", ".", "-T{node}") url = pushlog_api_url.format(repo_url, cs) session = requests.Session() @@ -354,15 +354,13 @@ def parse_chrome_manifest(path, base_path, chrome_entries): entry_path = os.path.join( os.path.relpath(os.path.dirname(path), base_path), entry.relpath ) - chrome_entries.append( - { - "type": "locale", - "alias": entry.name, - "locale": entry.id, - "platforms": convert_entry_flags_to_platform_codes(entry.flags), - "path": mozpath.normsep(entry_path), - } - ) + chrome_entries.append({ + "type": "locale", + "alias": entry.name, + "locale": entry.id, + "platforms": convert_entry_flags_to_platform_codes(entry.flags), + "path": mozpath.normsep(entry_path), + }) else: raise Exception(f"Unknown type {entry.name}") diff --git a/python/mozbuild/mozbuild/action/test_archive.py b/python/mozbuild/mozbuild/action/test_archive.py index 41256b572fae3..b81c6076ab68b 100644 --- a/python/mozbuild/mozbuild/action/test_archive.py +++ b/python/mozbuild/mozbuild/action/test_archive.py @@ -709,13 +709,11 @@ } if buildconfig.substs.get("MOZ_CODE_COVERAGE"): - ARCHIVE_FILES["common"].append( - { - "source": buildconfig.topsrcdir, - "base": "python/mozbuild/", - "patterns": ["mozpack/**", "mozbuild/codecoverage/**"], - } - ) + ARCHIVE_FILES["common"].append({ + "source": buildconfig.topsrcdir, + "base": "python/mozbuild/", + "patterns": ["mozpack/**", "mozbuild/codecoverage/**"], + }) if ( diff --git a/python/mozbuild/mozbuild/action/zip.py b/python/mozbuild/mozbuild/action/zip.py index 8b9d2e4dafb8f..b527d9b8f8590 100644 --- a/python/mozbuild/mozbuild/action/zip.py +++ b/python/mozbuild/mozbuild/action/zip.py @@ -25,7 +25,7 @@ def main(args): "-C", metavar="DIR", default=".", - help="Change to given directory before considering " "other paths", + help="Change to given directory before considering other paths", ) parser.add_argument("--strip", action="store_true", help="Strip executables") parser.add_argument( diff --git a/python/mozbuild/mozbuild/android_version_code.py b/python/mozbuild/mozbuild/android_version_code.py index 3b4025bec7586..443329d0f22e4 100644 --- a/python/mozbuild/mozbuild/android_version_code.py +++ b/python/mozbuild/mozbuild/android_version_code.py @@ -27,8 +27,7 @@ def android_version_code_v0(buildid, cpu_arch=None, min_sdk=0, max_sdk=0): return base + min_sdk + 3 else: raise ValueError( - "Don't know how to compute android:versionCode " - "for CPU arch %s" % cpu_arch + "Don't know how to compute android:versionCode for CPU arch %s" % cpu_arch ) @@ -129,8 +128,7 @@ def hours_since_cutoff(buildid): pass else: raise ValueError( - "Don't know how to compute android:versionCode " - "for CPU arch %s" % cpu_arch + "Don't know how to compute android:versionCode for CPU arch %s" % cpu_arch ) # 'p' bit is 1 for 64-bit architectures. @@ -140,8 +138,7 @@ def hours_since_cutoff(buildid): pass else: raise ValueError( - "Don't know how to compute android:versionCode " - "for CPU arch %s" % cpu_arch + "Don't know how to compute android:versionCode for CPU arch %s" % cpu_arch ) # 'g' bit is currently always 1, but may depend on `min_sdk` in the future. diff --git a/python/mozbuild/mozbuild/artifact_cache.py b/python/mozbuild/mozbuild/artifact_cache.py index 0c1e567a28ae3..f6ba037641e1c 100644 --- a/python/mozbuild/mozbuild/artifact_cache.py +++ b/python/mozbuild/mozbuild/artifact_cache.py @@ -20,7 +20,6 @@ A future need, perhaps. """ - import binascii import hashlib import logging diff --git a/python/mozbuild/mozbuild/artifacts.py b/python/mozbuild/mozbuild/artifacts.py index a5036a589935c..c4ab5eacf0548 100644 --- a/python/mozbuild/mozbuild/artifacts.py +++ b/python/mozbuild/mozbuild/artifacts.py @@ -31,7 +31,6 @@ consumers will need to arrange this themselves. """ - import collections import functools import glob @@ -277,8 +276,7 @@ def find_candidate_artifacts(self, artifacts): ) if self._tests_re and not tests_artifact: raise ValueError( - f'Expected tests archive matching "{self._tests_re}", but ' - "found none!" + f'Expected tests archive matching "{self._tests_re}", but found none!' ) if self._maven_zip_re and not maven_zip_artifact: raise ValueError( @@ -746,15 +744,13 @@ class MacArtifactJob(ArtifactJob): @property def _extra_archives(self): extra_archives = super()._extra_archives - extra_archives.update( - { - ".update_framework_artifacts.zip": { - "description": "Update-related macOS Framework Artifacts", - "src_prefix": "", - "dest_prefix": "update_framework_artifacts", - }, - } - ) + extra_archives.update({ + ".update_framework_artifacts.zip": { + "description": "Update-related macOS Framework Artifacts", + "src_prefix": "", + "dest_prefix": "update_framework_artifacts", + }, + }) return extra_archives @property @@ -919,12 +915,10 @@ class UnfilteredProjectPackageArtifactJob(ArtifactJob): """ # Can't yet handle `AndroidArtifactJob` uniformly, since the `product` is "mobile". - package_re = "|".join( - [ - f"({cls.package_re})" - for cls in (LinuxArtifactJob, MacArtifactJob, WinArtifactJob) - ] - ) + package_re = "|".join([ + f"({cls.package_re})" + for cls in (LinuxArtifactJob, MacArtifactJob, WinArtifactJob) + ]) job_configuration = GeckoJobConfiguration @property diff --git a/python/mozbuild/mozbuild/backend/base.py b/python/mozbuild/mozbuild/backend/base.py index 286f6500b04a5..0d376af4fff8e 100644 --- a/python/mozbuild/mozbuild/backend/base.py +++ b/python/mozbuild/mozbuild/backend/base.py @@ -305,12 +305,10 @@ def _get_preprocessor(self, obj): in the current environment.""" pp = Preprocessor() srcdir = mozpath.dirname(obj.input_path) - pp.context.update( - { - k: " ".join(v) if isinstance(v, list) else v - for k, v in obj.config.substs.items() - } - ) + pp.context.update({ + k: " ".join(v) if isinstance(v, list) else v + for k, v in obj.config.substs.items() + }) pp.context.update( top_srcdir=obj.topsrcdir, topobjdir=obj.topobjdir, diff --git a/python/mozbuild/mozbuild/backend/common.py b/python/mozbuild/mozbuild/backend/common.py index 214955c506375..aa875a5685943 100644 --- a/python/mozbuild/mozbuild/backend/common.py +++ b/python/mozbuild/mozbuild/backend/common.py @@ -162,9 +162,9 @@ def consume_object(self, obj): return False elif isinstance(obj, SandboxedWasmLibrary): - self._handle_generated_sources( - [mozpath.join(obj.relobjdir, f"{obj.basename}.h")] - ) + self._handle_generated_sources([ + mozpath.join(obj.relobjdir, f"{obj.basename}.h") + ]) return False elif isinstance(obj, (Sources, HostSources)): @@ -203,9 +203,9 @@ def consume_object(self, obj): for f in files: basename = FinalTargetPreprocessedFiles.get_obj_basename(f) relpath = mozpath.join(obj.install_target, path, basename) - self._handle_generated_sources( - [ObjDirPath(obj._context, "!/" + relpath).full_path] - ) + self._handle_generated_sources([ + ObjDirPath(obj._context, "!/" + relpath).full_path + ]) return False else: diff --git a/python/mozbuild/mozbuild/backend/configenvironment.py b/python/mozbuild/mozbuild/backend/configenvironment.py index 0e5ef3530c4b3..dda5b5e1ff65e 100644 --- a/python/mozbuild/mozbuild/backend/configenvironment.py +++ b/python/mozbuild/mozbuild/backend/configenvironment.py @@ -150,12 +150,10 @@ def __init__( self.bin_suffix = self.substs.get("BIN_SUFFIX", "") global_defines = [name for name in self.defines] - self.substs["ACDEFINES"] = " ".join( - [ - "-D%s=%s" % (name, shell_quote(self.defines[name]).replace("$", "$$")) - for name in sorted(global_defines) - ] - ) + self.substs["ACDEFINES"] = " ".join([ + "-D%s=%s" % (name, shell_quote(self.defines[name]).replace("$", "$$")) + for name in sorted(global_defines) + ]) def serialize(name, obj): if isinstance(obj, str): @@ -165,13 +163,11 @@ def serialize(name, obj): raise Exception("Unhandled type %s for %s", type(obj), str(name)) self.substs["ALLSUBSTS"] = "\n".join( - sorted( - [ - "%s = %s" % (name, serialize(name, self.substs[name])) - for name in self.substs - if self.substs[name] - ] - ) + sorted([ + "%s = %s" % (name, serialize(name, self.substs[name])) + for name in self.substs + if self.substs[name] + ]) ) self.substs["ALLEMPTYSUBSTS"] = "\n".join( sorted(["%s =" % name for name in self.substs if not self.substs[name]]) @@ -334,13 +330,10 @@ def write_vars(self, config): defines = config["defines"].copy() global_defines = [name for name in config["defines"]] - acdefines = " ".join( - [ - "-D%s=%s" - % (name, shell_quote(config["defines"][name]).replace("$", "$$")) - for name in sorted(global_defines) - ] - ) + acdefines = " ".join([ + "-D%s=%s" % (name, shell_quote(config["defines"][name]).replace("$", "$$")) + for name in sorted(global_defines) + ]) substs["ACDEFINES"] = acdefines all_defines = OrderedDict() diff --git a/python/mozbuild/mozbuild/backend/cpp_eclipse.py b/python/mozbuild/mozbuild/backend/cpp_eclipse.py index bafc37ace730a..805d5efd54c6a 100644 --- a/python/mozbuild/mozbuild/backend/cpp_eclipse.py +++ b/python/mozbuild/mozbuild/backend/cpp_eclipse.py @@ -210,18 +210,16 @@ def _import_project(self): self._write_noindex() try: - subprocess.check_call( - [ - "eclipse", - "-application", - "-nosplash", - "org.eclipse.cdt.managedbuilder.core.headlessbuild", - "-data", - self._workspace_dir, - "-importAll", - self._project_dir, - ] - ) + subprocess.check_call([ + "eclipse", + "-application", + "-nosplash", + "org.eclipse.cdt.managedbuilder.core.headlessbuild", + "-data", + self._workspace_dir, + "-importAll", + self._project_dir, + ]) except OSError as e: # Remove the workspace directory so we re-generate it and # try to import again when the backend is invoked again. diff --git a/python/mozbuild/mozbuild/backend/fastermake.py b/python/mozbuild/mozbuild/backend/fastermake.py index c40fd29020272..7a73e9d4f8ca8 100644 --- a/python/mozbuild/mozbuild/backend/fastermake.py +++ b/python/mozbuild/mozbuild/backend/fastermake.py @@ -86,9 +86,11 @@ def consume_object(self, obj): f, ) ) - self._l10n_dependencies[dep_target].append( - (merge, f.full_path, src) - ) + self._l10n_dependencies[dep_target].append(( + merge, + f.full_path, + src, + )) src = merge else: src = f.full_path @@ -220,12 +222,10 @@ def consume_finished(self): rule = mk.create_rule([merge]).add_dependencies( [ref_file, l10n_file] + python_deps ) - rule.add_commands( - [ - "$(PYTHON3) -m moz.l10n.bin.build_file " - f"--source {ref_file} --l10n {l10n_file} --target {merge}" - ] - ) + rule.add_commands([ + "$(PYTHON3) -m moz.l10n.bin.build_file " + f"--source {ref_file} --l10n {l10n_file} --target {merge}" + ]) # Add a dummy rule for the l10n file since it might not exist. mk.create_rule([l10n_file]) diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index 2fd8b8432278a..40a3508881362 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -181,10 +181,9 @@ def close(self): self.fh.write("NONRECURSIVE_TARGETS += export\n") self.fh.write("NONRECURSIVE_TARGETS_export += xpidl\n") self.fh.write( - "NONRECURSIVE_TARGETS_export_xpidl_DIRECTORY = " - "$(DEPTH)/xpcom/xpidl\n" + "NONRECURSIVE_TARGETS_export_xpidl_DIRECTORY = $(DEPTH)/xpcom/xpidl\n" ) - self.fh.write("NONRECURSIVE_TARGETS_export_xpidl_TARGETS += " "export\n") + self.fh.write("NONRECURSIVE_TARGETS_export_xpidl_TARGETS += export\n") return self.fh.close() @@ -1617,9 +1616,12 @@ def _process_final_target_files(self, obj, files, backend_file): else: install_manifest.add_pattern_link(f.srcdir, f, dest_dir) elif isinstance(f, AbsolutePath): - if not f.full_path.lower().endswith( - (".dll", ".pdb", ".so", ".dylib") - ): + if not f.full_path.lower().endswith(( + ".dll", + ".pdb", + ".so", + ".dylib", + )): raise Exception( "Absolute paths installed to FINAL_TARGET_FILES must" " only be shared libraries or associated debug" @@ -1761,16 +1763,16 @@ def _process_chrome_manifest_entry(self, obj, backend_file): mozpath.join("$(DEPTH)", top_level), make_quote(shell_quote("manifest %s" % path)), ] - rule.add_commands( - ["$(call py_action,buildlist %s,%s)" % (path, " ".join(args))] - ) + rule.add_commands([ + "$(call py_action,buildlist %s,%s)" % (path, " ".join(args)) + ]) args = [ mozpath.join("$(DEPTH)", obj.path), make_quote(shell_quote(str(obj.entry))), ] - rule.add_commands( - ["$(call py_action,buildlist %s,%s)" % (obj.entry.path, " ".join(args))] - ) + rule.add_commands([ + "$(call py_action,buildlist %s,%s)" % (obj.entry.path, " ".join(args)) + ]) fragment.dump(backend_file.fh, removal_guard=False) self._no_skip["misc"].add(obj.relsrcdir) @@ -1853,22 +1855,17 @@ def _handle_ipdl_sources( basename = os.path.basename(source) sorted_nonstatic_ipdl_basenames.append(basename) rule = mk.create_rule([basename]) - rule.add_dependencies( - [ - source, - "backend.mk", - "Makefile", - "$(DEPTH)/config/autoconf.mk", - "$(topsrcdir)/config/config.mk", - ] - ) - rule.add_commands( - [ - "$(RM) $@", - "$(call py_action,preprocessor $@,$(DEFINES) $(ACDEFINES) " - "$< -o $@)", - ] - ) + rule.add_dependencies([ + source, + "backend.mk", + "Makefile", + "$(DEPTH)/config/autoconf.mk", + "$(topsrcdir)/config/config.mk", + ]) + rule.add_commands([ + "$(RM) $@", + "$(call py_action,preprocessor $@,$(DEFINES) $(ACDEFINES) $< -o $@)", + ]) mk.add_statement( "ALL_IPDLSRCS := %s %s" @@ -1943,25 +1940,20 @@ def _handle_webidl_build( for source in sorted(webidls.all_preprocessed_sources()): basename = os.path.basename(source) rule = mk.create_rule([basename]) - rule.add_dependencies( - [ - source, - "backend.mk", - "Makefile", - "$(DEPTH)/config/autoconf.mk", - "$(topsrcdir)/config/config.mk", - ] - ) - rule.add_commands( - [ - # Remove the file before writing so bindings that go from - # static to preprocessed don't end up writing to a symlink, - # which would modify content in the source directory. - "$(RM) $@", - "$(call py_action,preprocessor $@,$(DEFINES) $(ACDEFINES) " - "$< -o $@)", - ] - ) + rule.add_dependencies([ + source, + "backend.mk", + "Makefile", + "$(DEPTH)/config/autoconf.mk", + "$(topsrcdir)/config/config.mk", + ]) + rule.add_commands([ + # Remove the file before writing so bindings that go from + # static to preprocessed don't end up writing to a symlink, + # which would modify content in the source directory. + "$(RM) $@", + "$(call py_action,preprocessor $@,$(DEFINES) $(ACDEFINES) $< -o $@)", + ]) self._add_unified_build_rules( mk, diff --git a/python/mozbuild/mozbuild/code_analysis/mach_commands.py b/python/mozbuild/mozbuild/code_analysis/mach_commands.py index e32a1d6dca280..6360ab52e9004 100644 --- a/python/mozbuild/mozbuild/code_analysis/mach_commands.py +++ b/python/mozbuild/mozbuild/code_analysis/mach_commands.py @@ -582,8 +582,7 @@ def _get_clang_tidy_command( @StaticAnalysisSubCommand( "static-analysis", "autotest", - "Run the auto-test suite in order to determine that" - " the analysis did not regress.", + "Run the auto-test suite in order to determine that the analysis did not regress.", ) @CommandArgument( "--dump-results", diff --git a/python/mozbuild/mozbuild/codecoverage/lcov_rewriter.py b/python/mozbuild/mozbuild/codecoverage/lcov_rewriter.py index b8d5d0b2050f9..64dc824799733 100644 --- a/python/mozbuild/mozbuild/codecoverage/lcov_rewriter.py +++ b/python/mozbuild/mozbuild/codecoverage/lcov_rewriter.py @@ -74,9 +74,9 @@ def resummarize(self): for fn_name, count in self.function_exec_counts.items() if fn_name in self.functions.values() } - self.covered_function_count = len( - [c for c in self.function_exec_counts.values() if c] - ) + self.covered_function_count = len([ + c for c in self.function_exec_counts.values() if c + ]) self.line_count = len(self.lines) self.covered_line_count = len([c for c, _ in self.lines.values() if c]) self.branch_count = len(self.branches) @@ -177,9 +177,9 @@ def _rewrite_branches(self, record): def rewrite_record(self, record, pp_info): # Rewrite the lines in the given record according to preprocessor info # and split to additional records when pp_info has included file info. - self._current_pp_info = dict( - [(tuple([int(l) for l in k.split(",")]), v) for k, v in pp_info.items()] - ) + self._current_pp_info = dict([ + (tuple([int(l) for l in k.split(",")]), v) for k, v in pp_info.items() + ]) self._ranges = sorted(self._current_pp_info.keys()) self._additions = {} self._rewrite_lines(record) diff --git a/python/mozbuild/mozbuild/compilation/database.py b/python/mozbuild/mozbuild/compilation/database.py index 7594be16ae407..5cb2acf661867 100644 --- a/python/mozbuild/mozbuild/compilation/database.py +++ b/python/mozbuild/mozbuild/compilation/database.py @@ -129,13 +129,11 @@ def consume_finished(self): per_source_flags = self._per_source_flags.get(filename) if per_source_flags is not None: c.extend(per_source_flags) - db.append( - { - "directory": directory, - "command": shell_quote(*c), - "file": mozpath.join(directory, filename), - } - ) + db.append({ + "directory": directory, + "command": shell_quote(*c), + "file": mozpath.join(directory, filename), + }) import json diff --git a/python/mozbuild/mozbuild/config_status.py b/python/mozbuild/mozbuild/config_status.py index 07bc0f8d4941c..2f81ede9e6982 100644 --- a/python/mozbuild/mozbuild/config_status.py +++ b/python/mozbuild/mozbuild/config_status.py @@ -106,17 +106,15 @@ def config_status( """ if "CONFIG_FILES" in os.environ: - raise Exception( - "Using the CONFIG_FILES environment variable is not " "supported." - ) + raise Exception("Using the CONFIG_FILES environment variable is not supported.") if "CONFIG_HEADERS" in os.environ: raise Exception( - "Using the CONFIG_HEADERS environment variable is not " "supported." + "Using the CONFIG_HEADERS environment variable is not supported." ) if not os.path.isabs(topsrcdir): raise Exception( - "topsrcdir must be defined as an absolute directory: " "%s" % topsrcdir + "topsrcdir must be defined as an absolute directory: %s" % topsrcdir ) default_backends = ["RecursiveMake"] diff --git a/python/mozbuild/mozbuild/configure/__init__.py b/python/mozbuild/mozbuild/configure/__init__.py index 494e1f541840c..4d8d692efa409 100644 --- a/python/mozbuild/mozbuild/configure/__init__.py +++ b/python/mozbuild/mozbuild/configure/__init__.py @@ -330,22 +330,20 @@ class ConfigureSandbox(dict): # Expose a limited set of functions from os.path OS = ReadOnlyNamespace( - path=ReadOnlyNamespace( - **{ - k: getattr(mozpath, k, getattr(os.path, k)) - for k in ( - "abspath", - "basename", - "dirname", - "isabs", - "join", - "normcase", - "normpath", - "realpath", - "relpath", - ) - } - ) + path=ReadOnlyNamespace(**{ + k: getattr(mozpath, k, getattr(os.path, k)) + for k in ( + "abspath", + "basename", + "dirname", + "isabs", + "join", + "normcase", + "normpath", + "realpath", + "relpath", + ) + }) ) def __init__( @@ -708,7 +706,7 @@ def _dependency(self, arg, callee_name, arg_name=None): raise ConfigureError("Option must not contain an '='") if name not in self._options: raise ConfigureError( - "'%s' is not a known option. " "Maybe it's declared too late?" % arg + "'%s' is not a known option. Maybe it's declared too late?" % arg ) arg = self._options[name] self._seen.add(arg) @@ -833,8 +831,7 @@ def depends_impl(self, *args, **kwargs): for c in conditions: if c != when: raise ConfigureError( - "@depends function needs the same `when` " - "as options it depends on" + "@depends function needs the same `when` as options it depends on" ) def decorator(func): @@ -1088,7 +1085,7 @@ def _resolve_and_set(self, data, name, value, when=None): raise TypeError("Unexpected type: '%s'" % type(name).__name__) if name in data: raise ConfigureError( - "Cannot add '%s' to configuration: Key already " "exists" % name + "Cannot add '%s' to configuration: Key already exists" % name ) value = self._resolve(value) if value is not None: @@ -1108,9 +1105,10 @@ def set_config_impl(self, name, value, when=None): """ when = self._normalize_when(when, "set_config") - self._execution_queue.append( - (self._resolve_and_set, (self._config, name, value, when)) - ) + self._execution_queue.append(( + self._resolve_and_set, + (self._config, name, value, when), + )) def set_define_impl(self, name, value, when=None): """Implementation of set_define(). @@ -1123,9 +1121,10 @@ def set_define_impl(self, name, value, when=None): when = self._normalize_when(when, "set_define") defines = self._config.setdefault("DEFINES", {}) - self._execution_queue.append( - (self._resolve_and_set, (defines, name, value, when)) - ) + self._execution_queue.append(( + self._resolve_and_set, + (defines, name, value, when), + )) def imply_option_impl(self, option, value, reason=None, when=None): """Implementation of imply_option(). diff --git a/python/mozbuild/mozbuild/configure/constants.py b/python/mozbuild/mozbuild/configure/constants.py index 25f43bb9f8fe3..4e6000408051b 100644 --- a/python/mozbuild/mozbuild/configure/constants.py +++ b/python/mozbuild/mozbuild/configure/constants.py @@ -111,30 +111,28 @@ class Abi(EnumString): # The order of those checks matter -CPU_preprocessor_checks = OrderedDict( - ( - ("x86", "__i386__ || _M_IX86"), - ("x86_64", "__x86_64__ || _M_X64"), - ("arm", "__arm__ || _M_ARM"), - ("aarch64", "__aarch64__ || _M_ARM64"), - ("ia64", "__ia64__"), - ("s390x", "__s390x__"), - ("s390", "__s390__"), - ("ppc64", "__powerpc64__"), - ("ppc", "__powerpc__"), - ("Alpha", "__alpha__"), - ("hppa", "__hppa__"), - ("sparc64", "__sparc__ && __arch64__"), - ("sparc", "__sparc__"), - ("m68k", "__m68k__"), - ("mips64", "__mips64"), - ("mips32", "__mips__"), - ("riscv64", "__riscv && __riscv_xlen == 64"), - ("loongarch64", "__loongarch64"), - ("sh4", "__sh__"), - ("wasm32", "__wasm32__"), - ) -) +CPU_preprocessor_checks = OrderedDict(( + ("x86", "__i386__ || _M_IX86"), + ("x86_64", "__x86_64__ || _M_X64"), + ("arm", "__arm__ || _M_ARM"), + ("aarch64", "__aarch64__ || _M_ARM64"), + ("ia64", "__ia64__"), + ("s390x", "__s390x__"), + ("s390", "__s390__"), + ("ppc64", "__powerpc64__"), + ("ppc", "__powerpc__"), + ("Alpha", "__alpha__"), + ("hppa", "__hppa__"), + ("sparc64", "__sparc__ && __arch64__"), + ("sparc", "__sparc__"), + ("m68k", "__m68k__"), + ("mips64", "__mips64"), + ("mips32", "__mips__"), + ("riscv64", "__riscv && __riscv_xlen == 64"), + ("loongarch64", "__loongarch64"), + ("sh4", "__sh__"), + ("wasm32", "__wasm32__"), +)) assert sorted(CPU_preprocessor_checks.keys()) == sorted(CPU.POSSIBLE_VALUES) diff --git a/python/mozbuild/mozbuild/configure/lint.py b/python/mozbuild/mozbuild/configure/lint.py index 4598d83b8cb60..2f445c68ac54c 100644 --- a/python/mozbuild/mozbuild/configure/lint.py +++ b/python/mozbuild/mozbuild/configure/lint.py @@ -237,8 +237,7 @@ def _check_prefix_for_bool_option(self, *args, **kwargs): if name.startswith(f"--{prefix}-"): frame = self._pretty_current_frame() e = ConfigureError( - "{} should be used instead of " - "{} with default={}".format( + "{} should be used instead of {} with default={}".format( name.replace(f"--{prefix}-", f"--{replacement}-"), name, default, diff --git a/python/mozbuild/mozbuild/configure/options.py b/python/mozbuild/mozbuild/configure/options.py index e6627b8251d90..fdc28f78a86d2 100644 --- a/python/mozbuild/mozbuild/configure/options.py +++ b/python/mozbuild/mozbuild/configure/options.py @@ -220,8 +220,7 @@ def __init__( ): if not name and not env: raise InvalidOptionError( - "At least an option name or an environment variable name must " - "be given" + "At least an option name or an environment variable name must be given" ) if name: if not isinstance(name, str): diff --git a/python/mozbuild/mozbuild/controller/building.py b/python/mozbuild/mozbuild/controller/building.py index 844329bb203d0..f2659ccc33da1 100644 --- a/python/mozbuild/mozbuild/controller/building.py +++ b/python/mozbuild/mozbuild/controller/building.py @@ -55,10 +55,9 @@ """.strip() -INSTALL_TESTS_CLOBBER = "".join( - [ - TextWrapper().fill(line) + "\n" - for line in """ +INSTALL_TESTS_CLOBBER = "".join([ + TextWrapper().fill(line) + "\n" + for line in """ The build system was unable to install tests because the CLOBBER file has \ been updated. This means if you edited any test files, your changes may not \ be picked up until a full/clobber build is performed. @@ -73,8 +72,7 @@ $ touch {clobber_file} """.splitlines() - ] -) +]) CLOBBER_REQUESTED_MESSAGE = """ =================== @@ -1153,7 +1151,7 @@ def _build( monitor.start() if directory is not None and not what: - print("Can only use -C/--directory with an explicit target " "name.") + print("Can only use -C/--directory with an explicit target name.") return 1 if directory is not None: @@ -1259,14 +1257,12 @@ def get_substs_flag(name): status = None - if not config_rc and any( - [ - self.backend_out_of_date( - mozpath.join(self.topobjdir, "backend.%sBackend" % backend) - ) - for backend in all_backends - ] - ): + if not config_rc and any([ + self.backend_out_of_date( + mozpath.join(self.topobjdir, "backend.%sBackend" % backend) + ) + for backend in all_backends + ]): print("Build configuration changed. Regenerating backend.") args = [ config.substs["PYTHON3"], @@ -1490,7 +1486,7 @@ def get_substs_flag(name): logging.WARNING, "compiler_warning", warning, - "warning: {normpath}:{line}:{column} [{flag}] " "{message}", + "warning: {normpath}:{line}:{column} [{flag}] {message}", ) else: self.log( @@ -1532,9 +1528,9 @@ def get_substs_flag(name): self.notify("Build complete" if not status else "Build failed") if status: - if what and any( - [target for target in what if target not in ("faster", "binaries")] - ): + if what and any([ + target for target in what if target not in ("faster", "binaries") + ]): print( "Hey! Builds initiated with `mach build " "$A_SPECIFIC_TARGET` may not always work, even if the " @@ -1548,7 +1544,7 @@ def get_substs_flag(name): # if excessive: # print(EXCESSIVE_SWAP_MESSAGE) - print("To view a profile of the build, run |mach " "resource-usage|.") + print("To view a profile of the build, run |mach resource-usage|.") long_build = monitor.elapsed > 1200 @@ -1840,13 +1836,11 @@ def _check_clobber(self, mozconfig, env): return `True` if the clobber was required but not completed, and return `False` if the clobber was not required and not completed. """ - auto_clobber = any( - [ - env.get("AUTOCLOBBER", False), - (mozconfig["env"] or {}).get("added", {}).get("AUTOCLOBBER", False), - "AUTOCLOBBER=1" in (mozconfig["make_extra"] or []), - ] - ) + auto_clobber = any([ + env.get("AUTOCLOBBER", False), + (mozconfig["env"] or {}).get("added", {}).get("AUTOCLOBBER", False), + "AUTOCLOBBER=1" in (mozconfig["make_extra"] or []), + ]) from mozbuild.base import BuildEnvironmentNotFoundException substs = dict() diff --git a/python/mozbuild/mozbuild/controller/clobber.py b/python/mozbuild/mozbuild/controller/clobber.py index faa218be6cb5c..00f1c73f84f66 100644 --- a/python/mozbuild/mozbuild/controller/clobber.py +++ b/python/mozbuild/mozbuild/controller/clobber.py @@ -13,10 +13,9 @@ from mozfile.mozfile import remove as mozfileremove from mozpack import path as mozpath -CLOBBER_MESSAGE = "".join( - [ - TextWrapper().fill(line) + "\n" - for line in """ +CLOBBER_MESSAGE = "".join([ + TextWrapper().fill(line) + "\n" + for line in """ The CLOBBER file has been updated, indicating that an incremental build since \ your last build will probably not work. A full/clobber build is required. @@ -38,8 +37,7 @@ $ touch {clobber_file} """.splitlines() - ] -) +]) class Clobberer: @@ -141,9 +139,9 @@ def remove_objdir(self, full=True): """ # Determine where cargo build artifacts are stored RUST_TARGET_VARS = ("RUST_HOST_TARGET", "RUST_TARGET") - rust_targets = set( - [self.substs[x] for x in RUST_TARGET_VARS if x in self.substs] - ) + rust_targets = set([ + self.substs[x] for x in RUST_TARGET_VARS if x in self.substs + ]) rust_build_kind = "release" if self.substs.get("MOZ_DEBUG_RUST"): rust_build_kind = "debug" diff --git a/python/mozbuild/mozbuild/faster_daemon.py b/python/mozbuild/mozbuild/faster_daemon.py index 0be69af1d9d49..3bd6245c38a8b 100644 --- a/python/mozbuild/mozbuild/faster_daemon.py +++ b/python/mozbuild/mozbuild/faster_daemon.py @@ -71,11 +71,9 @@ def defines(self): defines = dict(self.config_environment.acdefines) # These additions work around warts in the build system: see # http://searchfox.org/mozilla-central/rev/ad093e98f42338effe2e2513e26c3a311dd96422/config/faster/rules.mk#92-93 - defines.update( - { - "AB_CD": "en-US", - } - ) + defines.update({ + "AB_CD": "en-US", + }) return defines @mozbuild.util.memoized_property @@ -143,14 +141,10 @@ def changed_files(self): data = self.client.getSubscription("topsrcdir") if data: for dat in data: - files |= set( - [ - mozpath.normpath( - mozpath.join(self.config_environment.topsrcdir, f) - ) - for f in dat.get("files", []) - ] - ) + files |= set([ + mozpath.normpath(mozpath.join(self.config_environment.topsrcdir, f)) + for f in dat.get("files", []) + ]) return files diff --git a/python/mozbuild/mozbuild/frontend/context.py b/python/mozbuild/mozbuild/frontend/context.py index ebfd509d1c36e..16a11c5696ffd 100644 --- a/python/mozbuild/mozbuild/frontend/context.py +++ b/python/mozbuild/mozbuild/frontend/context.py @@ -495,13 +495,11 @@ def _os_ldflags(self): # TODO: This is pretty convoluted, and isn't really a per-context thing, # configure would be a better place to aggregate these. - if all( - [ - self._context.config.substs.get("OS_ARCH") == "WINNT", - self._context.config.substs.get("CC_TYPE") == "clang-cl", - not self._context.config.substs.get("MOZ_DEBUG"), - ] - ): + if all([ + self._context.config.substs.get("OS_ARCH") == "WINNT", + self._context.config.substs.get("CC_TYPE") == "clang-cl", + not self._context.config.substs.get("MOZ_DEBUG"), + ]): if self._context.config.substs.get("MOZ_OPTIMIZE"): flags.append("-OPT:REF,ICF") @@ -540,7 +538,7 @@ def _optimize_flags(self): def __setitem__(self, key, value): if key not in self._known_keys: raise ValueError( - "Invalid value. `%s` is not a compile flags " "category." % key + "Invalid value. `%s` is not a compile flags category." % key ) if key in self and self[key] is None: raise ValueError( @@ -1190,9 +1188,12 @@ def _action(item): ("exclusive", TypedList(str, StrictOrderingOnAppendList)), ) -GeneratedFilesList = StrictOrderingOnAppendListWithFlagsFactory( - {"script": str, "inputs": list, "force": bool, "flags": list} -) +GeneratedFilesList = StrictOrderingOnAppendListWithFlagsFactory({ + "script": str, + "inputs": list, + "force": bool, + "flags": list, +}) class Files(SubContext): @@ -2359,17 +2360,15 @@ def aggregate(files): """, ), "GYP_DIRS": ( - StrictOrderingOnAppendListWithFlagsFactory( - { - "variables": dict, - "input": str, - "sandbox_vars": dict, - "no_chromium": bool, - "no_unified": bool, - "non_unified_sources": StrictOrderingOnAppendList, - "action_overrides": dict, - } - ), + StrictOrderingOnAppendListWithFlagsFactory({ + "variables": dict, + "input": str, + "sandbox_vars": dict, + "no_chromium": bool, + "no_unified": bool, + "non_unified_sources": StrictOrderingOnAppendList, + "action_overrides": dict, + }), list, """Defines a list of object directories handled by gyp configurations. diff --git a/python/mozbuild/mozbuild/frontend/data.py b/python/mozbuild/mozbuild/frontend/data.py index 8e389bab01dbc..e0fc082da3460 100644 --- a/python/mozbuild/mozbuild/frontend/data.py +++ b/python/mozbuild/mozbuild/frontend/data.py @@ -1399,20 +1399,18 @@ def __init__( self.required_during_compile = [ f for f in self.outputs - if f.endswith( - ( - ".asm", - ".c", - ".cpp", - ".inc", - ".m", - ".mm", - ".def", - ".s", - ".S", - "symverscript", - ) - ) + if f.endswith(( + ".asm", + ".c", + ".cpp", + ".inc", + ".m", + ".mm", + ".def", + ".s", + ".S", + "symverscript", + )) ] else: self.required_during_compile = required_during_compile diff --git a/python/mozbuild/mozbuild/frontend/emitter.py b/python/mozbuild/mozbuild/frontend/emitter.py index d5dbd938f9fdd..8f66a856d634f 100644 --- a/python/mozbuild/mozbuild/frontend/emitter.py +++ b/python/mozbuild/mozbuild/frontend/emitter.py @@ -670,13 +670,11 @@ def check_unique_binary(program, kind): if program: check_unique_binary(program, kind) self._binaries[program] = cls(context, program) - self._linkage.append( - ( - context, - self._binaries[program], - kind.replace("PROGRAM", "USE_LIBS"), - ) - ) + self._linkage.append(( + context, + self._binaries[program], + kind.replace("PROGRAM", "USE_LIBS"), + )) add_program(self._binaries[program], kind) all_rust_programs = [] @@ -711,13 +709,11 @@ def check_unique_binary(program, kind): check_unique_binary(program, kind) self._binaries[program] = cls(context, program, cargo_file) - self._linkage.append( - ( - context, - self._binaries[program], - kind.replace("RUST_PROGRAMS", "USE_LIBS"), - ) - ) + self._linkage.append(( + context, + self._binaries[program], + kind.replace("RUST_PROGRAMS", "USE_LIBS"), + )) add_program(self._binaries[program], kind) for kind, cls in [ @@ -736,17 +732,11 @@ def check_unique_binary(program, kind): self._binaries[program] = cls( context, program, is_unit_test=kind == "CPP_UNIT_TESTS" ) - self._linkage.append( - ( - context, - self._binaries[program], - ( - "HOST_USE_LIBS" - if kind == "HOST_SIMPLE_PROGRAMS" - else "USE_LIBS" - ), - ) - ) + self._linkage.append(( + context, + self._binaries[program], + ("HOST_USE_LIBS" if kind == "HOST_SIMPLE_PROGRAMS" else "USE_LIBS"), + )) add_program(self._binaries[program], kind) host_libname = context.get("HOST_LIBRARY_NAME") @@ -795,19 +785,17 @@ def check_unique_binary(program, kind): if final_lib: if static_lib: raise SandboxValidationError( - "FINAL_LIBRARY implies FORCE_STATIC_LIB. " - "Please remove the latter.", + "FINAL_LIBRARY implies FORCE_STATIC_LIB. Please remove the latter.", context, ) if shared_lib: raise SandboxValidationError( - "FINAL_LIBRARY conflicts with FORCE_SHARED_LIB. " - "Please remove one.", + "FINAL_LIBRARY conflicts with FORCE_SHARED_LIB. Please remove one.", context, ) if is_framework: raise SandboxValidationError( - "FINAL_LIBRARY conflicts with IS_FRAMEWORK. " "Please remove one.", + "FINAL_LIBRARY conflicts with IS_FRAMEWORK. Please remove one.", context, ) static_args["link_into"] = final_lib @@ -817,7 +805,7 @@ def check_unique_binary(program, kind): if is_framework: if soname: raise SandboxValidationError( - "IS_FRAMEWORK conflicts with SONAME. " "Please remove one.", + "IS_FRAMEWORK conflicts with SONAME. Please remove one.", context, ) shared_lib = True @@ -961,7 +949,7 @@ def check_unique_binary(program, kind): if lib_defines: if not libname: raise SandboxValidationError( - "LIBRARY_DEFINES needs a " "LIBRARY_NAME to take effect", + "LIBRARY_DEFINES needs a LIBRARY_NAME to take effect", context, ) lib.lib_defines.update(lib_defines) @@ -1040,8 +1028,7 @@ def check_unique_binary(program, kind): for f in context_srcs: if f in seen_sources: raise SandboxValidationError( - "Source file should only " - "be added to %s once: %s" % (symbol, f), + "Source file should only be added to %s once: %s" % (symbol, f), context, ) seen_sources.add(f) @@ -1058,8 +1045,7 @@ def check_unique_binary(program, kind): if isinstance(f, SourcePath) and not os.path.exists(full_path): raise SandboxValidationError( - "File listed in %s does not " - "exist: '%s'" % (symbol, full_path), + "File listed in %s does not exist: '%s'" % (symbol, full_path), context, ) @@ -1089,7 +1075,7 @@ def check_unique_binary(program, kind): if no_pgo: if no_pgo_sources: raise SandboxValidationError( - "NO_PGO and SOURCES[...].no_pgo " "cannot be set at the same time", + "NO_PGO and SOURCES[...].no_pgo cannot be set at the same time", context, ) passthru.variables["NO_PROFILE_GUIDED_OPTIMIZE"] = no_pgo @@ -1261,16 +1247,14 @@ def emit_from_context(self, context): and context["DELAYLOAD_DLLS"] ): if context.config.substs.get("CC_TYPE") != "clang": - context["LDFLAGS"].extend( - [("-DELAYLOAD:%s" % dll) for dll in context["DELAYLOAD_DLLS"]] - ) + context["LDFLAGS"].extend([ + ("-DELAYLOAD:%s" % dll) for dll in context["DELAYLOAD_DLLS"] + ]) else: - context["LDFLAGS"].extend( - [ - ("-Wl,-Xlink=-DELAYLOAD:%s" % dll) - for dll in context["DELAYLOAD_DLLS"] - ] - ) + context["LDFLAGS"].extend([ + ("-Wl,-Xlink=-DELAYLOAD:%s" % dll) + for dll in context["DELAYLOAD_DLLS"] + ]) context["OS_LIBS"].append("delayimp") for v in ["CMFLAGS", "CMMFLAGS"]: @@ -1448,12 +1432,10 @@ def emit_from_context(self, context): for obj in self._handle_linkables(context, passthru, generated_files): yield obj - generated_files.update( - [ - "%s%s" % (k, self.config.substs.get("BIN_SUFFIX", "")) - for k in self._binaries.keys() - ] - ) + generated_files.update([ + "%s%s" % (k, self.config.substs.get("BIN_SUFFIX", "")) + for k in self._binaries.keys() + ]) processed_moz_src_files = None if "MOZ_SRC_FILES" in context: @@ -1587,7 +1569,7 @@ def emit_from_context(self, context): context.get("DIST_SUBDIR") or context.get("XPI_NAME") ): raise SandboxValidationError( - "RESOURCES_FILES cannot be used with DIST_SUBDIR or " "XPI_NAME.", + "RESOURCES_FILES cannot be used with DIST_SUBDIR or XPI_NAME.", context, ) @@ -1673,14 +1655,14 @@ def _process_xpidl(self, context): if not xpidl_module: if context["XPIDL_SOURCES"]: raise SandboxValidationError( - "XPIDL_MODULE must be defined if " "XPIDL_SOURCES is defined.", + "XPIDL_MODULE must be defined if XPIDL_SOURCES is defined.", context, ) return if not context["XPIDL_SOURCES"]: raise SandboxValidationError( - "XPIDL_MODULE cannot be defined " "unless there are XPIDL_SOURCES", + "XPIDL_MODULE cannot be defined unless there are XPIDL_SOURCES", context, ) @@ -1695,7 +1677,7 @@ def _process_xpidl(self, context): for idl in context["XPIDL_SOURCES"]: if not os.path.exists(idl.full_path): raise SandboxValidationError( - "File %s from XPIDL_SOURCES " "does not exist" % idl.full_path, + "File %s from XPIDL_SOURCES does not exist" % idl.full_path, context, ) @@ -1839,15 +1821,13 @@ def process_support_files(test): obj.installs[source] = (dest, False) obj.external_installs |= install_info.external_installs for install_path in install_info.deferred_installs: - if all( - [ - "*" not in install_path, - not os.path.isfile( - mozpath.join(context.config.topsrcdir, install_path[2:]) - ), - install_path not in install_info.external_installs, - ] - ): + if all([ + "*" not in install_path, + not os.path.isfile( + mozpath.join(context.config.topsrcdir, install_path[2:]) + ), + install_path not in install_info.external_installs, + ]): raise SandboxValidationError( "Error processing test " "manifest %s: entry in support-files not present " @@ -1934,8 +1914,7 @@ def _process_jar_manifests(self, context): jar_manifests = context.get("JAR_MANIFESTS", []) if len(jar_manifests) > 1: raise SandboxValidationError( - "While JAR_MANIFESTS is a list, " - "it is currently limited to one value.", + "While JAR_MANIFESTS is a list, it is currently limited to one value.", context, ) diff --git a/python/mozbuild/mozbuild/frontend/gyp_reader.py b/python/mozbuild/mozbuild/frontend/gyp_reader.py index 361af4844eaeb..d24df2f81b0e9 100644 --- a/python/mozbuild/mozbuild/frontend/gyp_reader.py +++ b/python/mozbuild/mozbuild/frontend/gyp_reader.py @@ -431,12 +431,10 @@ def __init__( if config.substs["CC_TYPE"] == "clang-cl": # This isn't actually used anywhere in this generator, but it's needed # to override the registry detection of VC++ in gyp. - os.environ.update( - { - "GYP_MSVS_OVERRIDE_PATH": "fake_path", - "GYP_MSVS_VERSION": config.substs["MSVS_VERSION"], - } - ) + os.environ.update({ + "GYP_MSVS_OVERRIDE_PATH": "fake_path", + "GYP_MSVS_VERSION": config.substs["MSVS_VERSION"], + }) params = { "parallel": False, diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py index 93020fa92a1f3..1802fe27eb04a 100644 --- a/python/mozbuild/mozbuild/frontend/reader.py +++ b/python/mozbuild/mozbuild/frontend/reader.py @@ -875,8 +875,7 @@ def __init__(self, config, finder=default_finder): def summary(self): return ExecutionSummary( - "Finished reading {file_count:d} moz.build files in " - "{execution_time:.2f}s", + "Finished reading {file_count:d} moz.build files in {execution_time:.2f}s", file_count=self._file_count, execution_time=self._execution_time, ) @@ -1217,7 +1216,7 @@ def _read_mozbuild(self, path, config, descend, metadata): for v in ("input", "variables"): if not getattr(gyp_dir, v): raise SandboxValidationError( - "Missing value for " 'GYP_DIRS["%s"].%s' % (target_dir, v), + 'Missing value for GYP_DIRS["%s"].%s' % (target_dir, v), context, ) diff --git a/python/mozbuild/mozbuild/frontend/sandbox.py b/python/mozbuild/mozbuild/frontend/sandbox.py index 76057c367baff..00ddb979fcf39 100644 --- a/python/mozbuild/mozbuild/frontend/sandbox.py +++ b/python/mozbuild/mozbuild/frontend/sandbox.py @@ -101,20 +101,18 @@ class Sandbox(dict): """ # The default set of builtins. - BUILTINS = ReadOnlyDict( - { - # Only real Python built-ins should go here. - "None": None, - "False": False, - "True": True, - "sorted": alphabetical_sorted, - "int": int, - "len": len, - "range": range, - "set": set, - "tuple": tuple, - } - ) + BUILTINS = ReadOnlyDict({ + # Only real Python built-ins should go here. + "None": None, + "False": False, + "True": True, + "sorted": alphabetical_sorted, + "int": int, + "len": len, + "range": range, + "set": set, + "tuple": tuple, + }) def __init__(self, context, finder=default_finder): """Initialize a Sandbox ready for execution.""" diff --git a/python/mozbuild/mozbuild/lockfiles/generate_python_lockfiles.py b/python/mozbuild/mozbuild/lockfiles/generate_python_lockfiles.py index 7b40ef45c2a68..a22c01f699c0e 100644 --- a/python/mozbuild/mozbuild/lockfiles/generate_python_lockfiles.py +++ b/python/mozbuild/mozbuild/lockfiles/generate_python_lockfiles.py @@ -28,7 +28,6 @@ class MissingUVError(Exception): class GeneratePythonLockfiles(MozbuildObject): - def __init__(self, *args, **kwargs) -> None: super().__init__(*args, virtualenv_name="uv", **kwargs) diff --git a/python/mozbuild/mozbuild/lockfiles/site_dependency_extractor.py b/python/mozbuild/mozbuild/lockfiles/site_dependency_extractor.py index 5a46810e6a43a..126b6f2c6207e 100644 --- a/python/mozbuild/mozbuild/lockfiles/site_dependency_extractor.py +++ b/python/mozbuild/mozbuild/lockfiles/site_dependency_extractor.py @@ -31,7 +31,6 @@ class DependencyParseError(Exception): class SiteDependencyExtractor: - def __init__(self, site_name: str, sites_dir: Path, topsrcdir: Path) -> None: self.site_file = sites_dir / f"{site_name}.txt" if not self.site_file.is_file(): diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index b09482630c4bd..7259c406566a7 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -127,36 +127,34 @@ def starts_with_cargo(s): else: raise ValueError - return Schema( - { - # The name of the command (not checked for now, but maybe - # later) - Required("command"): All(str, starts_with_cargo), - # Whether `make` should stop immediately in case - # of error returned by the command. Default: False - "continue_on_error": Boolean, - # Whether this command requires pre_export and export build - # targets to have run. Defaults to bool(cargo_build_flags). - "requires_export": Boolean, - # Build flags to use. If this variable is not - # defined here, the build flags are generated automatically and are - # the same as for `cargo build`. See available substitutions at the - # end. - "cargo_build_flags": [str], - # Extra build flags to use. These flags are added - # after the cargo_build_flags both when they are provided or - # automatically generated. See available substitutions at the end. - "cargo_extra_flags": [str], - # Available substitutions for `cargo_*_flags`: - # * {arch}: architecture target - # * {crate}: current crate name - # * {directory}: Directory of the current crate within the source tree - # * {features}: Rust features (for `--features`) - # * {manifest}: full path of `Cargo.toml` file - # * {target}: `--lib` for library, `--bin CRATE` for executables - # * {topsrcdir}: Top directory of sources - } - ) + return Schema({ + # The name of the command (not checked for now, but maybe + # later) + Required("command"): All(str, starts_with_cargo), + # Whether `make` should stop immediately in case + # of error returned by the command. Default: False + "continue_on_error": Boolean, + # Whether this command requires pre_export and export build + # targets to have run. Defaults to bool(cargo_build_flags). + "requires_export": Boolean, + # Build flags to use. If this variable is not + # defined here, the build flags are generated automatically and are + # the same as for `cargo build`. See available substitutions at the + # end. + "cargo_build_flags": [str], + # Extra build flags to use. These flags are added + # after the cargo_build_flags both when they are provided or + # automatically generated. See available substitutions at the end. + "cargo_extra_flags": [str], + # Available substitutions for `cargo_*_flags`: + # * {arch}: architecture target + # * {crate}: current crate name + # * {directory}: Directory of the current crate within the source tree + # * {features}: Rust features (for `--features`) + # * {manifest}: full path of `Cargo.toml` file + # * {target}: `--lib` for library, `--bin CRATE` for executables + # * {topsrcdir}: Top directory of sources + }) @Command( @@ -487,8 +485,9 @@ def doctor(command_context, fix=False, verbose=False): "what", default=["objdir", "python"], nargs="*", - help="Target to clobber, must be one of {{{}}} (default " - "objdir and python).".format(", ".join(CLOBBER_CHOICES)), + help="Target to clobber, must be one of {{{}}} (default objdir and python).".format( + ", ".join(CLOBBER_CHOICES) + ), ) @CommandArgument("--full", action="store_true", help="Perform a full clobber") def clobber(command_context, what, full=False): @@ -717,16 +716,14 @@ def handle_log_file(command_context, log_file): start_time = created command_context.log_manager.terminal_handler.formatter.start_time = created if "line" in params: - record = logging.makeLogRecord( - { - "created": created, - "name": command_context._logger.name, - "levelno": logging.INFO, - "msg": "{line}", - "params": params, - "action": action, - } - ) + record = logging.makeLogRecord({ + "created": created, + "name": command_context._logger.name, + "levelno": logging.INFO, + "msg": "{line}", + "params": params, + "action": action, + }) command_context._logger.handle(record) @@ -1582,8 +1579,7 @@ def _get_android_run_parser(): "--no-wait", action="store_true", default=False, - help="Do not wait for application to start before returning " - "(default: False)", + help="Do not wait for application to start before returning (default: False)", ) group.add_argument( "--enable-fission", @@ -1736,8 +1732,7 @@ def _get_desktop_run_parser(): group.add_argument( "--temp-profile", action="store_true", - help="Run the program using a new temporary profile created inside " - "the objdir.", + help="Run the program using a new temporary profile created inside the objdir.", ) group.add_argument( "--macos-open", @@ -3097,8 +3092,7 @@ def repackage_msi( "--unsigned", default=False, action="store_true", - help="Support `Add-AppxPackage ... -AllowUnsigned` on Windows 11." - "(Default: false)", + help="Support `Add-AppxPackage ... -AllowUnsigned` on Windows 11.(Default: false)", ) def repackage_msix( command_context, @@ -3541,8 +3535,7 @@ def repackage_snap_install(command_context, snap_file, snap_name, sudo=None): logging.ERROR, "repackage-snap-install-no-sudo", {}, - "Couldn't find a command to run snap as root; please use the" - " --sudo option", + "Couldn't find a command to run snap as root; please use the --sudo option", ) if not snap_file: diff --git a/python/mozbuild/mozbuild/repackaging/flatpak.py b/python/mozbuild/mozbuild/repackaging/flatpak.py index 628e916c9bbab..98024b0824cf3 100644 --- a/python/mozbuild/mozbuild/repackaging/flatpak.py +++ b/python/mozbuild/mozbuild/repackaging/flatpak.py @@ -150,19 +150,17 @@ def repackage_flatpak( application_ini_data = application_ini_data_from_directory(str(lib_dir)) variables = get_build_variables(application_ini_data, arch, version) - variables.update( - { - "FREEDESKTOP_VERSION": FREEDESKTOP_VERSION, - "FIREFOX_BASEAPP_CHANNEL": FIREFOX_BASEAPP_CHANNEL, - "FLATPAK_BRANCH": flatpak_branch, - "DATE": variables["TIMESTAMP"].strftime("%Y-%m-%d"), - # Override PKG_NAME since we use branches for beta vs release - "PKG_NAME": product, - "DBusActivatable": "false", - # Override Icon to match the flatpak's name - "Icon": flatpak_name, - } - ) + variables.update({ + "FREEDESKTOP_VERSION": FREEDESKTOP_VERSION, + "FIREFOX_BASEAPP_CHANNEL": FIREFOX_BASEAPP_CHANNEL, + "FLATPAK_BRANCH": flatpak_branch, + "DATE": variables["TIMESTAMP"].strftime("%Y-%m-%d"), + # Override PKG_NAME since we use branches for beta vs release + "PKG_NAME": product, + "DBusActivatable": "false", + # Override Icon to match the flatpak's name + "Icon": flatpak_name, + }) _render_flatpak_templates(template_dir, build_dir, variables) from fluent.runtime.fallback import FluentLocalization, FluentResourceLoader diff --git a/python/mozbuild/mozbuild/repackaging/msix.py b/python/mozbuild/mozbuild/repackaging/msix.py index e4ddbd47dcac4..a52b72bef8fad 100644 --- a/python/mozbuild/mozbuild/repackaging/msix.py +++ b/python/mozbuild/mozbuild/repackaging/msix.py @@ -737,9 +737,7 @@ def repackage_msix( if not makeappx: makeappx = find_sdk_tool("makeappx.exe", log=log) if not makeappx: - raise ValueError( - "makeappx is required; " "set MAKEAPPX or WINDOWSSDKDIR or PATH" - ) + raise ValueError("makeappx is required; set MAKEAPPX or WINDOWSSDKDIR or PATH") # `makeappx.exe` supports both slash and hyphen style arguments; `makemsix` # supports only hyphen style. `makeappx.exe` allows to overwrite and to @@ -776,7 +774,7 @@ def repackage_msix( def _sign_msix_win(output, force, log, verbose): powershell_exe = find_sdk_tool("powershell.exe", log=log) if not powershell_exe: - raise ValueError("powershell is required; " "set POWERSHELL or PATH") + raise ValueError("powershell is required; set POWERSHELL or PATH") def powershell(argstring, check=True): "Invoke `powershell.exe`. Arguments are given as a string to allow consumer to quote." @@ -789,9 +787,7 @@ def powershell(argstring, check=True): signtool = find_sdk_tool("signtool.exe", log=log) if not signtool: - raise ValueError( - "signtool is required; " "set SIGNTOOL or WINDOWSSDKDIR or PATH" - ) + raise ValueError("signtool is required; set SIGNTOOL or WINDOWSSDKDIR or PATH") # Our first order of business is to find, or generate, a (self-signed) # certificate. @@ -979,12 +975,12 @@ def _sign_msix_posix(output, force, log, verbose): makeappx = find_sdk_tool("makeappx", log=log) if not makeappx: - raise ValueError("makeappx is required; " "set MAKEAPPX or PATH") + raise ValueError("makeappx is required; set MAKEAPPX or PATH") openssl = find_sdk_tool("openssl", log=log) if not openssl: - raise ValueError("openssl is required; " "set OPENSSL or PATH") + raise ValueError("openssl is required; set OPENSSL or PATH") if "sign" not in subprocess.run( makeappx, check=False, capture_output=True diff --git a/python/mozbuild/mozbuild/repackaging/snap.py b/python/mozbuild/mozbuild/repackaging/snap.py index 21fa4d823fb64..2df94d7fc7ed3 100644 --- a/python/mozbuild/mozbuild/repackaging/snap.py +++ b/python/mozbuild/mozbuild/repackaging/snap.py @@ -118,16 +118,14 @@ def repackage_snap( def unpack_tarball(package, destdir): os.makedirs(destdir, exist_ok=True) - subprocess.check_call( - [ - "tar", - "-C", - destdir, - "-xvf", - package, - "--strip-components=1", - ] - ) + subprocess.check_call([ + "tar", + "-C", + destdir, + "-xvf", + package, + "--strip-components=1", + ]) def missing_connections(app_name): diff --git a/python/mozbuild/mozbuild/sphinx.py b/python/mozbuild/mozbuild/sphinx.py index d496646daeed0..09d20817c4d37 100644 --- a/python/mozbuild/mozbuild/sphinx.py +++ b/python/mozbuild/mozbuild/sphinx.py @@ -17,22 +17,18 @@ def function_reference(f, attr, args, doc): lines = [] - lines.extend( - [ - f, - "-" * len(f), - "", - ] - ) + lines.extend([ + f, + "-" * len(f), + "", + ]) docstring = prepare_docstring(doc) - lines.extend( - [ - docstring[0], - "", - ] - ) + lines.extend([ + docstring[0], + "", + ]) arg_types = [] @@ -46,12 +42,10 @@ def function_reference(f, attr, args, doc): arg_s = "(%s)" % ", ".join(arg_types) - lines.extend( - [ - ":Arguments: %s" % arg_s, - "", - ] - ) + lines.extend([ + ":Arguments: %s" % arg_s, + "", + ]) lines.extend(docstring[1:]) lines.append("") @@ -68,20 +62,16 @@ def variable_reference(v, st_type, in_type, doc): docstring = prepare_docstring(doc) - lines.extend( - [ - docstring[0], - "", - ] - ) + lines.extend([ + docstring[0], + "", + ]) - lines.extend( - [ - ":Storage Type: ``%s``" % st_type.__name__, - ":Input Type: ``%s``" % in_type.__name__, - "", - ] - ) + lines.extend([ + ":Storage Type: ``%s``" % st_type.__name__, + ":Input Type: ``%s``" % in_type.__name__, + "", + ]) lines.extend(docstring[1:]) lines.append("") @@ -98,14 +88,12 @@ def special_reference(v, func, typ, doc): docstring = prepare_docstring(doc) - lines.extend( - [ - docstring[0], - "", - ":Type: ``%s``" % typ.__name__, - "", - ] - ) + lines.extend([ + docstring[0], + "", + ":Type: ``%s``" % typ.__name__, + "", + ]) lines.extend(docstring[1:]) lines.append("") @@ -116,25 +104,21 @@ def special_reference(v, func, typ, doc): def format_module(m): lines = [] - lines.extend( - [ - ".. note::", - " moz.build files' implementation includes a ``Path`` class.", - ] - ) + lines.extend([ + ".. note::", + " moz.build files' implementation includes a ``Path`` class.", + ]) path_docstring_minus_summary = prepare_docstring(m.Path.__doc__)[2:] lines.extend([" " + line for line in path_docstring_minus_summary]) for subcontext, cls in sorted(m.SUBCONTEXTS.items()): - lines.extend( - [ - ".. _mozbuild_subcontext_%s:" % subcontext, - "", - "Sub-Context: %s" % subcontext, - "=============" + "=" * len(subcontext), - "", - ] - ) + lines.extend([ + ".. _mozbuild_subcontext_%s:" % subcontext, + "", + "Sub-Context: %s" % subcontext, + "=============" + "=" * len(subcontext), + "", + ]) lines.extend(prepare_docstring(cls.__doc__)) if lines[-1]: lines.append("") @@ -142,35 +126,29 @@ def format_module(m): for k, v in sorted(cls.VARIABLES.items()): lines.extend(variable_reference(k, *v)) - lines.extend( - [ - "Variables", - "=========", - "", - ] - ) + lines.extend([ + "Variables", + "=========", + "", + ]) for v in sorted(m.VARIABLES): lines.extend(variable_reference(v, *m.VARIABLES[v])) - lines.extend( - [ - "Functions", - "=========", - "", - ] - ) + lines.extend([ + "Functions", + "=========", + "", + ]) for func in sorted(m.FUNCTIONS): lines.extend(function_reference(func, *m.FUNCTIONS[func])) - lines.extend( - [ - "Special Variables", - "=================", - "", - ] - ) + lines.extend([ + "Special Variables", + "=================", + "", + ]) for v in sorted(m.SPECIAL_VARIABLES): lines.extend(special_reference(v, *m.SPECIAL_VARIABLES[v])) diff --git a/python/mozbuild/mozbuild/test/action/test_buildlist.py b/python/mozbuild/mozbuild/test/action/test_buildlist.py index 1a59d9d7d5c13..697f14f462f07 100644 --- a/python/mozbuild/mozbuild/test/action/test_buildlist.py +++ b/python/mozbuild/mozbuild/test/action/test_buildlist.py @@ -47,7 +47,7 @@ def assertFileContains(self, filename, l): self.assertEqual(line, l.pop(0)) self.assertTrue( len(l) == 0, - f"not enough lines in file! (expected '{l}'," f" got '{lines}'", + f"not enough lines in file! (expected '{l}', got '{lines}'", ) def test_basic(self): diff --git a/python/mozbuild/mozbuild/test/backend/test_partialconfigenvironment.py b/python/mozbuild/mozbuild/test/backend/test_partialconfigenvironment.py index 13b1656981259..f29a8b0e34a36 100644 --- a/python/mozbuild/mozbuild/test/backend/test_partialconfigenvironment.py +++ b/python/mozbuild/mozbuild/test/backend/test_partialconfigenvironment.py @@ -80,12 +80,10 @@ def test_remove_subst(self): self.assertTrue(os.path.exists(path)) def _assert_deps(self, env, deps): - deps = sorted( - [ - "$(wildcard %s)" % (mozpath.join(env.topobjdir, "config.statusd", d)) - for d in deps - ] - ) + deps = sorted([ + "$(wildcard %s)" % (mozpath.join(env.topobjdir, "config.statusd", d)) + for d in deps + ]) self.assertEqual(sorted(env.get_dependencies()), deps) def test_dependencies(self): diff --git a/python/mozbuild/mozbuild/test/backend/test_test_manifest.py b/python/mozbuild/mozbuild/test/backend/test_test_manifest.py index 4e7f86277c3c6..ee872a617901a 100644 --- a/python/mozbuild/mozbuild/test/backend/test_test_manifest.py +++ b/python/mozbuild/mozbuild/test/backend/test_test_manifest.py @@ -120,14 +120,12 @@ def test_test_manifest_sources(self): self.assertEqual( sources, - set( - [ - mozpath.join(env.topsrcdir, "mochitest.toml"), - mozpath.join(env.topsrcdir, "mochitest-common.toml"), - mozpath.join(env.topsrcdir, "moz.build"), - status_path, - ] - ), + set([ + mozpath.join(env.topsrcdir, "mochitest.toml"), + mozpath.join(env.topsrcdir, "mochitest-common.toml"), + mozpath.join(env.topsrcdir, "moz.build"), + status_path, + ]), ) diff --git a/python/mozbuild/mozbuild/test/common.py b/python/mozbuild/mozbuild/test/common.py index b3d90accd5724..d5da6c993c5d8 100644 --- a/python/mozbuild/mozbuild/test/common.py +++ b/python/mozbuild/mozbuild/test/common.py @@ -50,7 +50,7 @@ def __init__( "DLL_PREFIX": "lib", "DLL_SUFFIX": ".so", }, - **extra_substs + **extra_substs, ) self.defines = self.substs diff --git a/python/mozbuild/mozbuild/test/configure/test_bootstrap.py b/python/mozbuild/mozbuild/test/configure/test_bootstrap.py index fb8a9a41b7f67..4dff978bafd7d 100644 --- a/python/mozbuild/mozbuild/test/configure/test_bootstrap.py +++ b/python/mozbuild/mozbuild/test/configure/test_bootstrap.py @@ -16,7 +16,7 @@ class IndexSearch: def should_replace_task(self, task, *args): - return f'fake-task-id-for-{task["index"][0]}' + return f"fake-task-id-for-{task['index'][0]}" class TestBootstrap(BaseConfigureTest): diff --git a/python/mozbuild/mozbuild/test/configure/test_checks_configure.py b/python/mozbuild/mozbuild/test/configure/test_checks_configure.py index 3189d315e4aac..bdfe42a154f40 100644 --- a/python/mozbuild/mozbuild/test/configure/test_checks_configure.py +++ b/python/mozbuild/mozbuild/test/configure/test_checks_configure.py @@ -535,8 +535,7 @@ def test_check_prog_with_path(self): """ DEBUG: a: Looking for known-a ERROR: Paths provided to find_program must be a list of strings, not %r - """ - % mozpath.dirname(self.OTHER_A) + """ % mozpath.dirname(self.OTHER_A) ), ) @@ -1144,9 +1143,10 @@ def test_id_and_secret_keyfile(self): }, ) - with MockedOpen( - {"default-key": "default-id default-key\n", "key": "fake-id fake-key\n"} - ): + with MockedOpen({ + "default-key": "default-id default-key\n", + "key": "fake-id fake-key\n", + }): config, output, status = self.get_result( "id_and_secret_keyfile('Bing API', default='default-key')", args=["--with-bing-api-keyfile=key"], diff --git a/python/mozbuild/mozbuild/test/configure/test_configure.py b/python/mozbuild/mozbuild/test/configure/test_configure.py index 82ea1461ad810..3b55ad16fc12d 100644 --- a/python/mozbuild/mozbuild/test/configure/test_configure.py +++ b/python/mozbuild/mozbuild/test/configure/test_configure.py @@ -40,9 +40,9 @@ def get_config( return config def moz_configure(self, source): - return MockedOpen( - {os.path.join(test_data_path, "moz.configure"): textwrap.dedent(source)} - ) + return MockedOpen({ + os.path.join(test_data_path, "moz.configure"): textwrap.dedent(source) + }) def test_defaults(self): config = self.get_config() @@ -269,16 +269,18 @@ def test_returned_default(self): def test_returned_choices(self): for val in ("a", "b", "c"): - config = self.get_config( - ["--enable-values=alpha", "--returned-choices=%s" % val] - ) + config = self.get_config([ + "--enable-values=alpha", + "--returned-choices=%s" % val, + ]) self.assertIn("CHOICES", config) self.assertEqual(PositiveOptionValue((val,)), config["CHOICES"]) for val in ("0", "1", "2"): - config = self.get_config( - ["--enable-values=numeric", "--returned-choices=%s" % val] - ) + config = self.get_config([ + "--enable-values=numeric", + "--returned-choices=%s" % val, + ]) self.assertIn("CHOICES", config) self.assertEqual(PositiveOptionValue((val,)), config["CHOICES"]) @@ -1374,7 +1376,7 @@ def test_option_when(self): self.assertEqual( str(e.exception), - "@depends function needs the same `when` as " "options it depends on", + "@depends function needs the same `when` as options it depends on", ) with self.moz_configure( @@ -1394,7 +1396,7 @@ def always2(): self.assertEqual( str(e.exception), - "@depends function needs the same `when` as " "options it depends on", + "@depends function needs the same `when` as options it depends on", ) with self.moz_configure( @@ -1485,10 +1487,9 @@ def test_include_failures(self): self.assertEqual(str(e.exception), "Unexpected type: 'int'") def test_include_when(self): - with MockedOpen( - { - os.path.join(test_data_path, "moz.configure"): textwrap.dedent( - """ + with MockedOpen({ + os.path.join(test_data_path, "moz.configure"): textwrap.dedent( + """ option('--with-foo', help='Foo') include('always.configure', when=True) @@ -1499,27 +1500,27 @@ def test_include_when(self): set_config('BAR', bar) set_config('QUX', qux) """ - ), - os.path.join(test_data_path, "always.configure"): textwrap.dedent( - """ + ), + os.path.join(test_data_path, "always.configure"): textwrap.dedent( + """ option('--with-bar', help='Bar') @depends('--with-bar') def bar(x): if x: return 'bar' """ - ), - os.path.join(test_data_path, "never.configure"): textwrap.dedent( - """ + ), + os.path.join(test_data_path, "never.configure"): textwrap.dedent( + """ option('--with-qux', help='Qux') @depends('--with-qux') def qux(x): if x: return 'qux' """ - ), - os.path.join(test_data_path, "foo.configure"): textwrap.dedent( - """ + ), + os.path.join(test_data_path, "foo.configure"): textwrap.dedent( + """ option('--with-foo-really', help='Really foo') @depends('--with-foo-really') def foo(x): @@ -1528,14 +1529,13 @@ def foo(x): include('foo2.configure', when='--with-foo-really') """ - ), - os.path.join(test_data_path, "foo2.configure"): textwrap.dedent( - """ + ), + os.path.join(test_data_path, "foo2.configure"): textwrap.dedent( + """ set_config('FOO2', True) """ - ), - } - ): + ), + }): config = self.get_config() self.assertEqual(config, {}) @@ -1586,7 +1586,7 @@ def test_sandbox_failures(self): self.get_config() self.assertIn( - "Cannot assign `foo` because it is neither a @depends nor a " "@template", + "Cannot assign `foo` because it is neither a @depends nor a @template", str(e.exception), ) @@ -1615,7 +1615,7 @@ def foo(value): self.assertEqual( str(e.exception), - "'--with-foo' is not a known option. Maybe it's " "declared too late?", + "'--with-foo' is not a known option. Maybe it's declared too late?", ) with self.assertRaises(ConfigureError) as e: @@ -1642,7 +1642,7 @@ def foo(value): self.assertEqual( str(e.exception), - "Cannot use object of type 'int' as argument " "to @depends", + "Cannot use object of type 'int' as argument to @depends", ) with self.assertRaises(ConfigureError) as e: @@ -1947,7 +1947,7 @@ def foo(value): self.assertEqual( str(e.exception), - "@depends function needs the same `when` as " "options it depends on", + "@depends function needs the same `when` as options it depends on", ) with self.moz_configure( @@ -2084,9 +2084,9 @@ def baz(value): ("", NegativeOptionValue()), ("--baz=baz", PositiveOptionValue(("baz",))), ): - config = self.get_config( - [x for x in (foo_opt, bar_opt, baz_opt) if x] - ) + config = self.get_config([ + x for x in (foo_opt, bar_opt, baz_opt) if x + ]) self.assertEqual( config, { diff --git a/python/mozbuild/mozbuild/test/configure/test_lint.py b/python/mozbuild/mozbuild/test/configure/test_lint.py index 2a4b8a2f06eec..34278f7f1531f 100644 --- a/python/mozbuild/mozbuild/test/configure/test_lint.py +++ b/python/mozbuild/mozbuild/test/configure/test_lint.py @@ -47,9 +47,9 @@ def lint_test(self, options=[], env={}): sandbox.run(mozpath.join(test_data_path, "moz.configure")) def moz_configure(self, source): - return MockedOpen( - {os.path.join(test_data_path, "moz.configure"): textwrap.dedent(source)} - ) + return MockedOpen({ + os.path.join(test_data_path, "moz.configure"): textwrap.dedent(source) + }) def assertRaisesFromLine(self, exc_type, line): return AssertRaisesFromLine( @@ -341,7 +341,7 @@ def test_default_enable(self): self.lint_test() self.assertEqual( str(e.exception), - "--disable-foo should be used instead of " "--enable-foo with default=True", + "--disable-foo should be used instead of --enable-foo with default=True", ) def test_default_disable(self): @@ -361,8 +361,7 @@ def test_default_disable(self): self.lint_test() self.assertEqual( str(e.exception), - "--enable-foo should be used instead of " - "--disable-foo with default=False", + "--enable-foo should be used instead of --disable-foo with default=False", ) def test_default_with(self): @@ -382,7 +381,7 @@ def test_default_with(self): self.lint_test() self.assertEqual( str(e.exception), - "--without-foo should be used instead of " "--with-foo with default=True", + "--without-foo should be used instead of --with-foo with default=True", ) def test_default_without(self): @@ -402,7 +401,7 @@ def test_default_without(self): self.lint_test() self.assertEqual( str(e.exception), - "--with-foo should be used instead of " "--without-foo with default=False", + "--with-foo should be used instead of --without-foo with default=False", ) def test_default_func(self): @@ -427,8 +426,7 @@ def test_default_func(self): self.lint_test() self.assertEqual( str(e.exception), - '`help` should contain "{Enable|Disable}" because of ' - "non-constant default", + '`help` should contain "{Enable|Disable}" because of non-constant default', ) def test_dual_help(self): diff --git a/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py b/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py index 393bc85961e57..3fc45f3e40dfd 100644 --- a/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py +++ b/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py @@ -62,14 +62,12 @@ @memoize def GCC_BASE(version): version = Version(version) - return FakeCompiler( - { - "__GNUC__": version.major, - "__GNUC_MINOR__": version.minor, - "__GNUC_PATCHLEVEL__": version.patch, - "__STDC__": 1, - } - ) + return FakeCompiler({ + "__GNUC__": version.major, + "__GNUC_MINOR__": version.minor, + "__GNUC_PATCHLEVEL__": version.patch, + "__STDC__": 1, + }) @memoize @@ -145,14 +143,12 @@ def GXX(version): @memoize def CLANG_BASE(version): version = Version(version) - return FakeCompiler( - { - "__clang__": 1, - "__clang_major__": version.major, - "__clang_minor__": version.minor, - "__clang_patchlevel__": version.patch, - } - ) + return FakeCompiler({ + "__clang__": 1, + "__clang_major__": version.major, + "__clang_minor__": version.minor, + "__clang_patchlevel__": version.patch, + }) @memoize @@ -239,17 +235,15 @@ def CLANG_PLATFORM(gcc_platform): @memoize def VS(version): version = Version(version) - return FakeCompiler( - { - None: { - "_MSC_VER": "%02d%02d" % (version.major, version.minor), - "_MSC_FULL_VER": "%02d%02d%05d" - % (version.major, version.minor, version.patch), - "_MT": "1", - }, - "*.cpp": DEFAULT_CXX_97, - } - ) + return FakeCompiler({ + None: { + "_MSC_VER": "%02d%02d" % (version.major, version.minor), + "_MSC_FULL_VER": "%02d%02d%05d" + % (version.major, version.minor, version.patch), + "_MT": "1", + }, + "*.cpp": DEFAULT_CXX_97, + }) VS_2017u8 = VS("19.15.26726") @@ -703,12 +697,10 @@ def test_no_supported_compiler(self): def test_absolute_path(self): paths = dict(self.PATHS) - paths.update( - { - "/opt/clang/bin/clang": paths["/usr/bin/clang"], - "/opt/clang/bin/clang++": paths["/usr/bin/clang++"], - } - ) + paths.update({ + "/opt/clang/bin/clang": paths["/usr/bin/clang"], + "/opt/clang/bin/clang++": paths["/usr/bin/clang++"], + }) result = { "c_compiler": self.DEFAULT_CLANG_RESULT + {"compiler": "/opt/clang/bin/clang"}, @@ -725,12 +717,10 @@ def test_absolute_path(self): def test_atypical_name(self): paths = dict(self.PATHS) - paths.update( - { - "/usr/bin/afl-clang-fast": paths["/usr/bin/clang"], - "/usr/bin/afl-clang-fast++": paths["/usr/bin/clang++"], - } - ) + paths.update({ + "/usr/bin/afl-clang-fast": paths["/usr/bin/clang"], + "/usr/bin/afl-clang-fast++": paths["/usr/bin/clang++"], + }) self.do_toolchain_test( paths, { @@ -884,13 +874,11 @@ class OSXToolchainTest(BaseToolchainTest): GCC_10_RESULT = LinuxToolchainTest.GCC_10_RESULT GXX_10_RESULT = LinuxToolchainTest.GXX_10_RESULT SYSROOT_FLAGS = { - "flags": PrependFlags( - [ - "-isysroot", - xcrun("", ("--show-sdk-path",))[1], - "-mmacosx-version-min=10.15", - ] - ) + "flags": PrependFlags([ + "-isysroot", + xcrun("", ("--show-sdk-path",))[1], + "-mmacosx-version-min=10.15", + ]) } def test_clang(self): @@ -1362,12 +1350,10 @@ def do_test_cross_gcc(self, host, target): }, ) - paths.update( - { - "%s-gcc" % toolchain_prefix: DEFAULT_GCC + self.PLATFORMS[target], - "%s-g++" % toolchain_prefix: DEFAULT_GXX + self.PLATFORMS[target], - } - ) + paths.update({ + "%s-gcc" % toolchain_prefix: DEFAULT_GCC + self.PLATFORMS[target], + "%s-g++" % toolchain_prefix: DEFAULT_GXX + self.PLATFORMS[target], + }) self.do_toolchain_test( paths, { @@ -1494,12 +1480,10 @@ def test_cross_clang(self): def test_cross_atypical_clang(self): paths = dict(self.PATHS) - paths.update( - { - "/usr/bin/afl-clang-fast": paths["/usr/bin/clang"], - "/usr/bin/afl-clang-fast++": paths["/usr/bin/clang++"], - } - ) + paths.update({ + "/usr/bin/afl-clang-fast": paths["/usr/bin/clang"], + "/usr/bin/afl-clang-fast++": paths["/usr/bin/clang++"], + }) afl_clang_result = self.DEFAULT_CLANG_RESULT + { "compiler": "/usr/bin/afl-clang-fast" } @@ -1522,12 +1506,10 @@ def test_cross_atypical_clang(self): class OSXCrossToolchainTest(BaseToolchainTest): TARGET = "i686-apple-darwin11.2.0" PATHS = dict(LinuxToolchainTest.PATHS) - PATHS.update( - { - "/usr/bin/clang": CLANG_17 + CLANG_PLATFORM_X86_64_LINUX, - "/usr/bin/clang++": CLANGXX_17 + CLANG_PLATFORM_X86_64_LINUX, - } - ) + PATHS.update({ + "/usr/bin/clang": CLANG_17 + CLANG_PLATFORM_X86_64_LINUX, + "/usr/bin/clang++": CLANGXX_17 + CLANG_PLATFORM_X86_64_LINUX, + }) DEFAULT_CLANG_RESULT = CompilerResult( flags=[], version="17.0.0", diff --git a/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py b/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py index 1751ac4a5cd74..3e4055c51ba25 100644 --- a/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py +++ b/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py @@ -98,9 +98,10 @@ def test_expansion(self): self.assertEqual(pp.out.getvalue(), '1 . 2 . c "D"') def test_normalization(self): - pp = CompilerPreprocessor( - {"__has_attribute(bar)": 1, '__has_warning("-Wc++98-foo")': 1} - ) + pp = CompilerPreprocessor({ + "__has_attribute(bar)": 1, + '__has_warning("-Wc++98-foo")': 1, + }) pp.out = StringIO() input = StringIO( dedent( @@ -268,15 +269,13 @@ def test_fake_compiler(self): compiler = FakeCompiler({"A": "1", "B": "2"}) self.assertEqual(compiler(None, ["-E", "file"]), (0, "1 2 C", "")) - compiler = FakeCompiler( - { - None: {"A": "1", "B": "2"}, - "-foo": {"C": "foo"}, - "-bar": {"B": "bar", "C": "bar"}, - "-qux": {"B": False}, - "*.c": {"B": "42"}, - } - ) + compiler = FakeCompiler({ + None: {"A": "1", "B": "2"}, + "-foo": {"C": "foo"}, + "-bar": {"B": "bar", "C": "bar"}, + "-qux": {"B": False}, + "*.c": {"B": "42"}, + }) self.assertEqual(compiler(None, ["-E", "file"]), (0, "1 2 C", "")) self.assertEqual(compiler(None, ["-E", "-foo", "file"]), (0, "1 2 foo", "")) self.assertEqual( diff --git a/python/mozbuild/mozbuild/test/configure/test_toolkit_moz_configure.py b/python/mozbuild/mozbuild/test/configure/test_toolkit_moz_configure.py index 7d20c0a49e0a0..6540c04255059 100644 --- a/python/mozbuild/mozbuild/test/configure/test_toolkit_moz_configure.py +++ b/python/mozbuild/mozbuild/test/configure/test_toolkit_moz_configure.py @@ -38,7 +38,7 @@ def get_value_for(args=[], environ={}, mozconfig=""): ) self.assertEqual( - "--enable-application=browser " "MOZ_VTUNE=1", + "--enable-application=browser MOZ_VTUNE=1", get_value_for(["--enable-application=browser", "MOZ_VTUNE=1"]), ) diff --git a/python/mozbuild/mozbuild/test/configure/test_util.py b/python/mozbuild/mozbuild/test/configure/test_util.py index bf2d1a9bb8c5e..172f63227441f 100644 --- a/python/mozbuild/mozbuild/test/configure/test_util.py +++ b/python/mozbuild/mozbuild/test/configure/test_util.py @@ -59,7 +59,7 @@ def test_format(self): logger.debug("qux") self.assertEqual(out.getvalue(), "baz\n") - self.assertEqual(err.getvalue(), "ERROR:foo\n" "WARNING:bar\n") + self.assertEqual(err.getvalue(), "ERROR:foo\nWARNING:bar\n") def test_continuation(self): out = StringIO() @@ -75,7 +75,7 @@ def test_continuation(self): logger.info("yes") logger.info("qux") - self.assertEqual(out.getvalue(), "foo\n" "checking bar... yes\n" "qux\n") + self.assertEqual(out.getvalue(), "foo\nchecking bar... yes\nqux\n") out.seek(0) out.truncate() @@ -88,7 +88,7 @@ def test_continuation(self): self.assertEqual( out.getvalue(), - "foo\n" "checking bar... \n" "WARNING:hoge\n" " ... no\n" "qux\n", + "foo\nchecking bar... \nWARNING:hoge\n ... no\nqux\n", ) out.seek(0) @@ -103,12 +103,7 @@ def test_continuation(self): self.assertEqual( out.getvalue(), - "foo\n" - "checking bar... \n" - "WARNING:hoge\n" - "WARNING:fuga\n" - " ... no\n" - "qux\n", + "foo\nchecking bar... \nWARNING:hoge\nWARNING:fuga\n ... no\nqux\n", ) out.seek(0) @@ -127,9 +122,9 @@ def test_continuation(self): logger.info("no") logger.info("qux") - self.assertEqual(out.getvalue(), "foo\n" "checking bar... no\n" "qux\n") + self.assertEqual(out.getvalue(), "foo\nchecking bar... no\nqux\n") - self.assertEqual(err.getvalue(), "WARNING:hoge\n" "WARNING:fuga\n") + self.assertEqual(err.getvalue(), "WARNING:hoge\nWARNING:fuga\n") def test_queue_debug(self): out = StringIO() @@ -146,7 +141,7 @@ def test_queue_debug(self): logger.info("yes") logger.info("qux") - self.assertEqual(out.getvalue(), "checking bar... yes\n" "qux\n") + self.assertEqual(out.getvalue(), "checking bar... yes\nqux\n") out.seek(0) out.truncate() @@ -158,7 +153,7 @@ def test_queue_debug(self): logger.error("fail") self.assertEqual( - out.getvalue(), "checking bar... no\n" "DEBUG:do foo\n" "ERROR:fail\n" + out.getvalue(), "checking bar... no\nDEBUG:do foo\nERROR:fail\n" ) out.seek(0) @@ -219,7 +214,7 @@ def test_queue_debug(self): self.assertIs(caught, e) self.assertEqual( - out.getvalue(), "checking bar... no\n" "DEBUG:do foo\n" "DEBUG:do bar\n" + out.getvalue(), "checking bar... no\nDEBUG:do foo\nDEBUG:do bar\n" ) def test_queue_debug_reentrant(self): @@ -245,7 +240,7 @@ def test_queue_debug_reentrant(self): self.assertEqual( out.getvalue(), - "outer info\n" "inner info\n" "DEBUG| outer debug\n" "DEBUG| inner debug\n", + "outer info\ninner info\nDEBUG| outer debug\nDEBUG| inner debug\n", ) out.seek(0) @@ -265,7 +260,7 @@ def test_queue_debug_reentrant(self): self.assertEqual( out.getvalue(), - "outer info\n" "inner info\n" "DEBUG| outer debug\n" "DEBUG| inner debug\n", + "outer info\ninner info\nDEBUG| outer debug\nDEBUG| inner debug\n", ) out.seek(0) @@ -406,7 +401,7 @@ def test_non_ascii_subprocess_output(self): self.assertEqual(status, 0) quote_char = "'" if getpreferredencoding().lower() == "utf-8": - quote_char = "\u00B4" + quote_char = "\u00b4" self.assertEqual(out.getvalue().strip(), quote_char) diff --git a/python/mozbuild/mozbuild/test/frontend/test_context.py b/python/mozbuild/mozbuild/test/frontend/test_context.py index 307b6afe3deb2..4f9d487ef504b 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_context.py +++ b/python/mozbuild/mozbuild/test/frontend/test_context.py @@ -29,13 +29,11 @@ class TestContext(unittest.TestCase): def test_defaults(self): - test = Context( - { - "foo": (int, int, ""), - "bar": (bool, bool, ""), - "baz": (dict, dict, ""), - } - ) + test = Context({ + "foo": (int, int, ""), + "bar": (bool, bool, ""), + "baz": (dict, dict, ""), + }) self.assertEqual(list(test), []) @@ -57,12 +55,10 @@ def test_defaults(self): self.assertEqual(set(test.keys()), {"foo", "bar", "baz"}) def test_type_check(self): - test = Context( - { - "foo": (int, int, ""), - "baz": (dict, list, ""), - } - ) + test = Context({ + "foo": (int, int, ""), + "baz": (dict, list, ""), + }) test["foo"] = 5 @@ -81,13 +77,11 @@ def test_type_check(self): self.assertEqual(test["baz"], {"a": 1, "b": 2}) def test_update(self): - test = Context( - { - "foo": (int, int, ""), - "bar": (bool, bool, ""), - "baz": (dict, list, ""), - } - ) + test = Context({ + "foo": (int, int, ""), + "bar": (bool, bool, ""), + "baz": (dict, list, ""), + }) self.assertEqual(list(test), []) @@ -548,11 +542,9 @@ def test_path_typed_list(self): MyListWithFlags = ContextDerivedTypedListWithItems( Path, - StrictOrderingOnAppendListWithFlagsFactory( - { - "foo": bool, - } - ), + StrictOrderingOnAppendListWithFlagsFactory({ + "foo": bool, + }), ) l = MyListWithFlags(ctxt1) l += paths diff --git a/python/mozbuild/mozbuild/test/frontend/test_emitter.py b/python/mozbuild/mozbuild/test/frontend/test_emitter.py index a660754f54367..6facc90f1d125 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py +++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py @@ -1008,7 +1008,7 @@ def test_test_manifest_unmatched_generated(self): with self.assertRaisesRegex( SandboxValidationError, "entry in generated-files not present elsewhere" ): - self.read_topsrcdir(reader), + (self.read_topsrcdir(reader),) def test_test_manifest_parent_support_files_dir(self): """support-files referencing a file in a parent directory works.""" @@ -1058,9 +1058,12 @@ def test_ipdl_sources(self): mozpath.relpath(p, ipdl_collection.topsrcdir) for p in ipdl_collection.all_regular_sources() ) - expected = set( - ["bar/bar.ipdl", "bar/bar2.ipdlh", "foo/foo.ipdl", "foo/foo2.ipdlh"] - ) + expected = set([ + "bar/bar.ipdl", + "bar/bar2.ipdlh", + "foo/foo.ipdl", + "foo/foo2.ipdlh", + ]) self.assertEqual(ipdls, expected) diff --git a/python/mozbuild/mozbuild/test/frontend/test_namespaces.py b/python/mozbuild/mozbuild/test/frontend/test_namespaces.py index 3c360c5c5c386..394493fb6cd72 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_namespaces.py +++ b/python/mozbuild/mozbuild/test/frontend/test_namespaces.py @@ -64,11 +64,9 @@ def __hash__(self): "HOGEHOGE": ( ContextDerivedTypedListWithItems( Piyo, - StrictOrderingOnAppendListWithFlagsFactory( - { - "foo": bool, - } - ), + StrictOrderingOnAppendListWithFlagsFactory({ + "foo": bool, + }), ), list, None, diff --git a/python/mozbuild/mozbuild/test/frontend/test_reader.py b/python/mozbuild/mozbuild/test/frontend/test_reader.py index 07383a022ad94..63c1160cfa2e5 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_reader.py +++ b/python/mozbuild/mozbuild/test/frontend/test_reader.py @@ -289,9 +289,9 @@ def test_find_relevant_mozbuilds(self): ) # Missing moz.build from missing intermediate directory. - paths = reader._find_relevant_mozbuilds( - ["d1/no-intermediate-moz-build/child/file"] - ) + paths = reader._find_relevant_mozbuilds([ + "d1/no-intermediate-moz-build/child/file" + ]) self.assertEqual( paths, { @@ -304,9 +304,9 @@ def test_find_relevant_mozbuilds(self): ) # Lots of empty directories. - paths = reader._find_relevant_mozbuilds( - ["d1/parent-is-far/dir1/dir2/dir3/file"] - ) + paths = reader._find_relevant_mozbuilds([ + "d1/parent-is-far/dir1/dir2/dir3/file" + ]) self.assertEqual( paths, { @@ -319,9 +319,10 @@ def test_find_relevant_mozbuilds(self): ) # Lots of levels. - paths = reader._find_relevant_mozbuilds( - ["d1/every-level/a/file", "d1/every-level/b/file"] - ) + paths = reader._find_relevant_mozbuilds([ + "d1/every-level/a/file", + "d1/every-level/b/file", + ]) self.assertEqual( paths, { @@ -354,9 +355,11 @@ def test_find_relevant_mozbuilds(self): def test_read_relevant_mozbuilds(self): reader = self.reader("reader-relevant-mozbuild") - paths, contexts = reader.read_relevant_mozbuilds( - ["d1/every-level/a/file", "d1/every-level/b/file", "d2/file"] - ) + paths, contexts = reader.read_relevant_mozbuilds([ + "d1/every-level/a/file", + "d1/every-level/b/file", + "d2/file", + ]) self.assertEqual(len(paths), 3) self.assertEqual(len(contexts), 6) @@ -387,13 +390,11 @@ def test_files_bad_bug_component(self): def test_files_bug_component_static(self): reader = self.reader("files-info") - v = reader.files_info( - [ - "bug_component/static/foo", - "bug_component/static/bar", - "bug_component/static/foo/baz", - ] - ) + v = reader.files_info([ + "bug_component/static/foo", + "bug_component/static/bar", + "bug_component/static/foo/baz", + ]) self.assertEqual(len(v), 3) self.assertEqual( v["bug_component/static/foo"]["BUG_COMPONENT"], @@ -420,13 +421,11 @@ def test_files_bug_component_simple(self): def test_files_bug_component_different_matchers(self): reader = self.reader("files-info") - v = reader.files_info( - [ - "bug_component/different-matchers/foo.jsm", - "bug_component/different-matchers/bar.cpp", - "bug_component/different-matchers/baz.misc", - ] - ) + v = reader.files_info([ + "bug_component/different-matchers/foo.jsm", + "bug_component/different-matchers/bar.cpp", + "bug_component/different-matchers/baz.misc", + ]) self.assertEqual(len(v), 3) js_flags = v["bug_component/different-matchers/foo.jsm"] @@ -445,14 +444,12 @@ def test_files_bug_component_different_matchers(self): def test_files_bug_component_final(self): reader = self.reader("files-info") - v = reader.files_info( - [ - "bug_component/final/foo", - "bug_component/final/Makefile.in", - "bug_component/final/subcomponent/Makefile.in", - "bug_component/final/subcomponent/bar", - ] - ) + v = reader.files_info([ + "bug_component/final/foo", + "bug_component/final/Makefile.in", + "bug_component/final/subcomponent/Makefile.in", + "bug_component/final/subcomponent/bar", + ]) self.assertEqual( v["bug_component/final/foo"]["BUG_COMPONENT"], @@ -479,17 +476,15 @@ def test_invalid_flavor(self): def test_schedules(self): reader = self.reader("schedules") - info = reader.files_info( - [ - "win.and.osx", - "somefile", - "foo.win", - "foo.osx", - "subd/aa.py", - "subd/yaml.py", - "subd/win.js", - ] - ) + info = reader.files_info([ + "win.and.osx", + "somefile", + "foo.win", + "foo.osx", + "subd/aa.py", + "subd/yaml.py", + "subd/win.js", + ]) # default: all exclusive, no inclusive self.assertEqual(info["somefile"]["SCHEDULES"].inclusive, []) self.assertEqual( diff --git a/python/mozbuild/mozbuild/test/frontend/test_sandbox.py b/python/mozbuild/mozbuild/test/frontend/test_sandbox.py index 6bddcf85df2ee..6f22b1e795224 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_sandbox.py +++ b/python/mozbuild/mozbuild/test/frontend/test_sandbox.py @@ -25,11 +25,9 @@ class TestSandbox(unittest.TestCase): def sandbox(self): return Sandbox( - Context( - { - "DIRS": (list, list, None), - } - ) + Context({ + "DIRS": (list, list, None), + }) ) def test_exec_source_success(self): @@ -498,11 +496,9 @@ class Foo(int): def foo(a, b): return type(a), type(b) - FUNCTIONS.update( - { - "foo": (lambda self: foo, (Foo, int), ""), - } - ) + FUNCTIONS.update({ + "foo": (lambda self: foo, (Foo, int), ""), + }) try: sandbox = self.sandbox() diff --git a/python/mozbuild/mozbuild/test/test_confvars.py b/python/mozbuild/mozbuild/test/test_confvars.py index 401e44e66647f..191252bc71fbc 100644 --- a/python/mozbuild/mozbuild/test/test_confvars.py +++ b/python/mozbuild/mozbuild/test/test_confvars.py @@ -13,7 +13,6 @@ def TemporaryConfVars(): class TestContext(unittest.TestCase): - def loads(self, *lines): with NamedTemporaryFile("wt", delete=False) as ntf: ntf.writelines(lines) @@ -64,7 +63,7 @@ def test_parse_invalid_assign_in_trailing_comment(self): ) def test_parse_quoted_assignment(self): - confvars = self.loads("a='b'\n" "b=' c'\n" 'c=" \'c"\n') + confvars = self.loads("a='b'\nb=' c'\nc=\" 'c\"\n") self.assertEqual(confvars, {"a": "b", "b": " c", "c": " 'c"}) def test_parse_invalid_assignment(self): diff --git a/python/mozbuild/mozbuild/test/test_legacy_test.py b/python/mozbuild/mozbuild/test/test_legacy_test.py index 9a75599d1a90e..41fdfaaa6841c 100644 --- a/python/mozbuild/mozbuild/test/test_legacy_test.py +++ b/python/mozbuild/mozbuild/test/test_legacy_test.py @@ -22,9 +22,9 @@ def test_extra_legacy_tests(): for src, fd in repo.get_tracked_files_finder().find("**/moz.build"): if src in allowlist: continue - assert ( - b"LegacyTest(" not in fd.read() - ), f"LegacyTest used in {src}, please refrain and use another test kind." + assert b"LegacyTest(" not in fd.read(), ( + f"LegacyTest used in {src}, please refrain and use another test kind." + ) if __name__ == "__main__": diff --git a/python/mozbuild/mozbuild/test/test_line_endings.py b/python/mozbuild/mozbuild/test/test_line_endings.py index 4162eb3e2f025..f218497618392 100644 --- a/python/mozbuild/mozbuild/test/test_line_endings.py +++ b/python/mozbuild/mozbuild/test/test_line_endings.py @@ -26,17 +26,17 @@ def createFile(self, lineendings): self.f.flush() def testMac(self): - self.createFile([b"\x0D"] * 3) + self.createFile([b"\x0d"] * 3) self.pp.do_include(self.f.name) self.assertEqual(self.pp.out.getvalue(), "a\nb\nc\n") def testUnix(self): - self.createFile([b"\x0A"] * 3) + self.createFile([b"\x0a"] * 3) self.pp.do_include(self.f.name) self.assertEqual(self.pp.out.getvalue(), "a\nb\nc\n") def testWindows(self): - self.createFile([b"\x0D\x0A"] * 3) + self.createFile([b"\x0d\x0a"] * 3) self.pp.do_include(self.f.name) self.assertEqual(self.pp.out.getvalue(), "a\nb\nc\n") diff --git a/python/mozbuild/mozbuild/test/test_manifest.py b/python/mozbuild/mozbuild/test/test_manifest.py index abe4b67602bb8..d56c2a0aba5e8 100644 --- a/python/mozbuild/mozbuild/test/test_manifest.py +++ b/python/mozbuild/mozbuild/test/test_manifest.py @@ -43,11 +43,10 @@ def test_simple(self): "bugzilla": {"component": "Graphics", "product": "Core"}, } - self.process_test_vectors( - [ - ( - simple_dict, - b""" + self.process_test_vectors([ + ( + simple_dict, + b""" --- schema: 1 origin: @@ -63,10 +62,10 @@ def test_simple(self): product: Core component: Graphics """.strip(), - ), - ( - simple_dict, - b""" + ), + ( + simple_dict, + b""" schema: 1 origin: name: cairo @@ -81,32 +80,30 @@ def test_simple(self): product: Core component: Graphics """.strip(), - ), - ] - ) + ), + ]) # =========================================================================================== def test_updatebot(self): - self.process_test_vectors( - [ - ( - { - "schema": "1", - "origin": { - "description": "2D Graphics Library", - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "release": "version 1.6.4", - "revision": "AA001122334455", - "url": "https://www.cairographics.org/", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "updatebot": { - "maintainer-phab": "tjr", - "maintainer-bz": "a@example.com", - }, + self.process_test_vectors([ + ( + { + "schema": "1", + "origin": { + "description": "2D Graphics Library", + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "release": "version 1.6.4", + "revision": "AA001122334455", + "url": "https://www.cairographics.org/", }, - b""" + "bugzilla": {"component": "Graphics", "product": "Core"}, + "updatebot": { + "maintainer-phab": "tjr", + "maintainer-bz": "a@example.com", + }, + }, + b""" --- schema: 1 origin: @@ -125,26 +122,26 @@ def test_updatebot(self): maintainer-phab: tjr maintainer-bz: a@example.com """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "description": "2D Graphics Library", - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "release": "version 1.6.4", - "revision": "001122334455", - "url": "https://www.cairographics.org/", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "updatebot": { - "maintainer-phab": "tjr", - "maintainer-bz": "a@example.com", - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "description": "2D Graphics Library", + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "release": "version 1.6.4", + "revision": "001122334455", + "url": "https://www.cairographics.org/", + }, + "bugzilla": {"component": "Graphics", "product": "Core"}, + "updatebot": { + "maintainer-phab": "tjr", + "maintainer-bz": "a@example.com", }, - b""" + }, + b""" --- schema: 1 origin: @@ -163,27 +160,27 @@ def test_updatebot(self): maintainer-phab: tjr maintainer-bz: a@example.com """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "description": "2D Graphics Library", - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "release": "version 1.6.4", - "revision": "001122334455", - "url": "https://www.cairographics.org/", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "updatebot": { - "try-preset": "foo", - "maintainer-phab": "tjr", - "maintainer-bz": "a@example.com", - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "description": "2D Graphics Library", + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "release": "version 1.6.4", + "revision": "001122334455", + "url": "https://www.cairographics.org/", + }, + "bugzilla": {"component": "Graphics", "product": "Core"}, + "updatebot": { + "try-preset": "foo", + "maintainer-phab": "tjr", + "maintainer-bz": "a@example.com", }, - b""" + }, + b""" --- schema: 1 origin: @@ -203,32 +200,32 @@ def test_updatebot(self): maintainer-phab: tjr maintainer-bz: a@example.com """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "description": "2D Graphics Library", - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "release": "version 1.6.4", - "revision": "AA001122334455", - "url": "https://www.cairographics.org/", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "vendoring": { - "url": "https://example.com", - "source-hosting": "gitlab", - }, - "updatebot": { - "maintainer-phab": "tjr", - "maintainer-bz": "a@example.com", - "fuzzy-query": "!linux64", - "tasks": [{"type": "commit-alert"}], - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "description": "2D Graphics Library", + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "release": "version 1.6.4", + "revision": "AA001122334455", + "url": "https://www.cairographics.org/", }, - b""" + "bugzilla": {"component": "Graphics", "product": "Core"}, + "vendoring": { + "url": "https://example.com", + "source-hosting": "gitlab", + }, + "updatebot": { + "maintainer-phab": "tjr", + "maintainer-bz": "a@example.com", + "fuzzy-query": "!linux64", + "tasks": [{"type": "commit-alert"}], + }, + }, + b""" --- schema: 1 origin: @@ -253,11 +250,11 @@ def test_updatebot(self): tasks: - type: commit-alert """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -283,32 +280,32 @@ def test_updatebot(self): tasks: - type: commit-alert """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "description": "2D Graphics Library", - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "release": "version 1.6.4", - "revision": "AA001122334455", - "url": "https://www.cairographics.org/", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "vendoring": { - "url": "https://example.com", - "source-hosting": "gitlab", - }, - "updatebot": { - "maintainer-phab": "tjr", - "maintainer-bz": "a@example.com", - "fuzzy-paths": ["dir1/", "dir2"], - "tasks": [{"type": "commit-alert"}], - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "description": "2D Graphics Library", + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "release": "version 1.6.4", + "revision": "AA001122334455", + "url": "https://www.cairographics.org/", + }, + "bugzilla": {"component": "Graphics", "product": "Core"}, + "vendoring": { + "url": "https://example.com", + "source-hosting": "gitlab", }, - b""" + "updatebot": { + "maintainer-phab": "tjr", + "maintainer-bz": "a@example.com", + "fuzzy-paths": ["dir1/", "dir2"], + "tasks": [{"type": "commit-alert"}], + }, + }, + b""" --- schema: 1 origin: @@ -335,32 +332,32 @@ def test_updatebot(self): tasks: - type: commit-alert """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "description": "2D Graphics Library", - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "release": "version 1.6.4", - "revision": "AA001122334455", - "url": "https://www.cairographics.org/", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "vendoring": { - "url": "https://example.com", - "source-hosting": "gitlab", - }, - "updatebot": { - "maintainer-phab": "tjr", - "maintainer-bz": "a@example.com", - "fuzzy-paths": ["dir1/"], - "tasks": [{"type": "commit-alert"}], - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "description": "2D Graphics Library", + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "release": "version 1.6.4", + "revision": "AA001122334455", + "url": "https://www.cairographics.org/", + }, + "bugzilla": {"component": "Graphics", "product": "Core"}, + "vendoring": { + "url": "https://example.com", + "source-hosting": "gitlab", + }, + "updatebot": { + "maintainer-phab": "tjr", + "maintainer-bz": "a@example.com", + "fuzzy-paths": ["dir1/"], + "tasks": [{"type": "commit-alert"}], }, - b""" + }, + b""" --- schema: 1 origin: @@ -385,43 +382,43 @@ def test_updatebot(self): tasks: - type: commit-alert """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "description": "2D Graphics Library", - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "release": "version 1.6.4", - "revision": "AA001122334455", - "url": "https://www.cairographics.org/", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "vendoring": { - "url": "https://example.com", - "source-hosting": "gitlab", - "tracking": "commit", - "flavor": "rust", - }, - "updatebot": { - "maintainer-phab": "tjr", - "maintainer-bz": "a@example.com", - "tasks": [ - {"type": "commit-alert", "frequency": "release"}, - { - "type": "vendoring", - "enabled": False, - "cc": ["b@example.com"], - "needinfo": ["c@example.com"], - "frequency": "1 weeks", - "platform": "windows", - }, - ], - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "description": "2D Graphics Library", + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "release": "version 1.6.4", + "revision": "AA001122334455", + "url": "https://www.cairographics.org/", + }, + "bugzilla": {"component": "Graphics", "product": "Core"}, + "vendoring": { + "url": "https://example.com", + "source-hosting": "gitlab", + "tracking": "commit", + "flavor": "rust", + }, + "updatebot": { + "maintainer-phab": "tjr", + "maintainer-bz": "a@example.com", + "tasks": [ + {"type": "commit-alert", "frequency": "release"}, + { + "type": "vendoring", + "enabled": False, + "cc": ["b@example.com"], + "needinfo": ["c@example.com"], + "frequency": "1 weeks", + "platform": "windows", + }, + ], }, - b""" + }, + b""" --- schema: 1 origin: @@ -454,43 +451,43 @@ def test_updatebot(self): frequency: 1 weeks platform: windows """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "description": "2D Graphics Library", - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "release": "version 1.6.4", - "revision": "AA001122334455", - "url": "https://www.cairographics.org/", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "vendoring": { - "url": "https://example.com", - "source-hosting": "gitlab", - "tracking": "tag", - "flavor": "rust", - }, - "updatebot": { - "maintainer-phab": "tjr", - "maintainer-bz": "a@example.com", - "tasks": [ - {"type": "commit-alert", "frequency": "release"}, - { - "type": "vendoring", - "enabled": False, - "cc": ["b@example.com"], - "needinfo": ["c@example.com"], - "frequency": "1 weeks, 4 commits", - "platform": "windows", - }, - ], - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "description": "2D Graphics Library", + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "release": "version 1.6.4", + "revision": "AA001122334455", + "url": "https://www.cairographics.org/", }, - b""" + "bugzilla": {"component": "Graphics", "product": "Core"}, + "vendoring": { + "url": "https://example.com", + "source-hosting": "gitlab", + "tracking": "tag", + "flavor": "rust", + }, + "updatebot": { + "maintainer-phab": "tjr", + "maintainer-bz": "a@example.com", + "tasks": [ + {"type": "commit-alert", "frequency": "release"}, + { + "type": "vendoring", + "enabled": False, + "cc": ["b@example.com"], + "needinfo": ["c@example.com"], + "frequency": "1 weeks, 4 commits", + "platform": "windows", + }, + ], + }, + }, + b""" --- schema: 1 origin: @@ -523,11 +520,11 @@ def test_updatebot(self): frequency: 1 weeks, 4 commits platform: windows """.strip(), - ), - # ------------------------------------------------- - ( - "exception", # rust flavor cannot use update-actions - b""" + ), + # ------------------------------------------------- + ( + "exception", # rust flavor cannot use update-actions + b""" --- schema: 1 origin: @@ -564,46 +561,46 @@ def test_updatebot(self): frequency: 1 weeks, 4 commits platform: windows """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "description": "2D Graphics Library", - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "release": "version 1.6.4", - "revision": "AA001122334455", - "url": "https://www.cairographics.org/", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "vendoring": { - "url": "https://example.com", - "source-hosting": "gitlab", - }, - "updatebot": { - "maintainer-phab": "tjr", - "maintainer-bz": "a@example.com", - "tasks": [ - { - "type": "vendoring", - "enabled": False, - "cc": ["b@example.com", "c@example.com"], - "needinfo": ["d@example.com", "e@example.com"], - "frequency": "every", - }, - { - "type": "commit-alert", - "filter": "none", - "source-extensions": [".c", ".cpp"], - "frequency": "2 weeks", - "platform": "linux", - }, - ], - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "description": "2D Graphics Library", + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "release": "version 1.6.4", + "revision": "AA001122334455", + "url": "https://www.cairographics.org/", + }, + "bugzilla": {"component": "Graphics", "product": "Core"}, + "vendoring": { + "url": "https://example.com", + "source-hosting": "gitlab", }, - b""" + "updatebot": { + "maintainer-phab": "tjr", + "maintainer-bz": "a@example.com", + "tasks": [ + { + "type": "vendoring", + "enabled": False, + "cc": ["b@example.com", "c@example.com"], + "needinfo": ["d@example.com", "e@example.com"], + "frequency": "every", + }, + { + "type": "commit-alert", + "filter": "none", + "source-extensions": [".c", ".cpp"], + "frequency": "2 weeks", + "platform": "linux", + }, + ], + }, + }, + b""" --- schema: 1 origin: @@ -642,46 +639,46 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "description": "2D Graphics Library", - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "release": "version 1.6.4", - "revision": "AA001122334455", - "url": "https://www.cairographics.org/", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "vendoring": { - "url": "https://example.com", - "source-hosting": "gitlab", - }, - "updatebot": { - "maintainer-phab": "tjr", - "maintainer-bz": "a@example.com", - "tasks": [ - { - "type": "vendoring", - "enabled": False, - "cc": ["b@example.com", "c@example.com"], - "needinfo": ["d@example.com", "e@example.com"], - "frequency": "every", - }, - { - "type": "commit-alert", - "filter": "none", - "source-extensions": [".c", ".cpp"], - "frequency": "2 commits", - "platform": "linux", - }, - ], - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "description": "2D Graphics Library", + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "release": "version 1.6.4", + "revision": "AA001122334455", + "url": "https://www.cairographics.org/", + }, + "bugzilla": {"component": "Graphics", "product": "Core"}, + "vendoring": { + "url": "https://example.com", + "source-hosting": "gitlab", + }, + "updatebot": { + "maintainer-phab": "tjr", + "maintainer-bz": "a@example.com", + "tasks": [ + { + "type": "vendoring", + "enabled": False, + "cc": ["b@example.com", "c@example.com"], + "needinfo": ["d@example.com", "e@example.com"], + "frequency": "every", + }, + { + "type": "commit-alert", + "filter": "none", + "source-extensions": [".c", ".cpp"], + "frequency": "2 commits", + "platform": "linux", + }, + ], }, - b""" + }, + b""" --- schema: 1 origin: @@ -720,47 +717,47 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "description": "2D Graphics Library", - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "release": "version 1.6.4", - "revision": "AA001122334455", - "url": "https://www.cairographics.org/", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "vendoring": { - "url": "https://example.com", - "source-hosting": "gitlab", - }, - "updatebot": { - "maintainer-phab": "tjr", - "maintainer-bz": "a@example.com", - "tasks": [ - { - "type": "vendoring", - "enabled": False, - "cc": ["b@example.com", "c@example.com"], - "needinfo": ["d@example.com", "e@example.com"], - "frequency": "every", - "blocking": "1234", - }, - { - "type": "commit-alert", - "filter": "none", - "source-extensions": [".c", ".cpp"], - "frequency": "2 commits", - "platform": "linux", - }, - ], - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "description": "2D Graphics Library", + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "release": "version 1.6.4", + "revision": "AA001122334455", + "url": "https://www.cairographics.org/", }, - b""" + "bugzilla": {"component": "Graphics", "product": "Core"}, + "vendoring": { + "url": "https://example.com", + "source-hosting": "gitlab", + }, + "updatebot": { + "maintainer-phab": "tjr", + "maintainer-bz": "a@example.com", + "tasks": [ + { + "type": "vendoring", + "enabled": False, + "cc": ["b@example.com", "c@example.com"], + "needinfo": ["d@example.com", "e@example.com"], + "frequency": "every", + "blocking": "1234", + }, + { + "type": "commit-alert", + "filter": "none", + "source-extensions": [".c", ".cpp"], + "frequency": "2 commits", + "platform": "linux", + }, + ], + }, + }, + b""" --- schema: 1 origin: @@ -800,11 +797,11 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -845,30 +842,28 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "description": "2D Graphics Library", - "url": "https://www.cairographics.org/", - "release": "version 1.6.4", - "revision": "AA001122334455", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "vendoring": { - "url": "https://example.com", - "source-hosting": "gitlab", - "flavor": "individual-files", - "individual-files": [ - {"upstream": "foo", "destination": "bar"} - ], - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "description": "2D Graphics Library", + "url": "https://www.cairographics.org/", + "release": "version 1.6.4", + "revision": "AA001122334455", + }, + "bugzilla": {"component": "Graphics", "product": "Core"}, + "vendoring": { + "url": "https://example.com", + "source-hosting": "gitlab", + "flavor": "individual-files", + "individual-files": [{"upstream": "foo", "destination": "bar"}], }, - b""" + }, + b""" --- schema: 1 origin: @@ -891,33 +886,31 @@ def test_updatebot(self): product: Core component: Graphics """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "description": "2D Graphics Library", - "url": "https://www.cairographics.org/", - "release": "version 1.6.4", - "revision": "AA001122334455", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "vendoring": { - "url": "https://example.com", - "source-hosting": "gitlab", - "flavor": "individual-files", - "individual-files": [ - {"upstream": "foo", "destination": "bar"} - ], - "update-actions": [ - {"action": "move-file", "from": "foo", "to": "bar"} - ], - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "description": "2D Graphics Library", + "url": "https://www.cairographics.org/", + "release": "version 1.6.4", + "revision": "AA001122334455", }, - b""" + "bugzilla": {"component": "Graphics", "product": "Core"}, + "vendoring": { + "url": "https://example.com", + "source-hosting": "gitlab", + "flavor": "individual-files", + "individual-files": [{"upstream": "foo", "destination": "bar"}], + "update-actions": [ + {"action": "move-file", "from": "foo", "to": "bar"} + ], + }, + }, + b""" --- schema: 1 origin: @@ -944,33 +937,33 @@ def test_updatebot(self): product: Core component: Graphics """.strip(), - ), - # ------------------------------------------------- - ( - { - "schema": "1", - "origin": { - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "description": "2D Graphics Library", - "url": "https://www.cairographics.org/", - "release": "version 1.6.4", - "revision": "AA001122334455", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, - "vendoring": { - "url": "https://example.com", - "source-hosting": "gitlab", - "flavor": "individual-files", - "individual-files-default-destination": "bar", - "individual-files-default-upstream": "foo", - "individual-files-list": ["bar", "foo"], - "update-actions": [ - {"action": "move-file", "from": "foo", "to": "bar"} - ], - }, + ), + # ------------------------------------------------- + ( + { + "schema": "1", + "origin": { + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "description": "2D Graphics Library", + "url": "https://www.cairographics.org/", + "release": "version 1.6.4", + "revision": "AA001122334455", + }, + "bugzilla": {"component": "Graphics", "product": "Core"}, + "vendoring": { + "url": "https://example.com", + "source-hosting": "gitlab", + "flavor": "individual-files", + "individual-files-default-destination": "bar", + "individual-files-default-upstream": "foo", + "individual-files-list": ["bar", "foo"], + "update-actions": [ + {"action": "move-file", "from": "foo", "to": "bar"} + ], }, - b""" + }, + b""" --- schema: 1 origin: @@ -999,11 +992,11 @@ def test_updatebot(self): product: Core component: Graphics """.strip(), - ), - # ------------------------------------------------- - ( - "exception", # can't have both types of indidivudal-files list - b""" + ), + # ------------------------------------------------- + ( + "exception", # can't have both types of indidivudal-files list + b""" --- schema: 1 origin: @@ -1032,11 +1025,11 @@ def test_updatebot(self): product: Core component: Graphics """.strip(), - ), - # ------------------------------------------------- - ( - "exception", # can't have indidivudal-files-default-upstream - b""" + ), + # ------------------------------------------------- + ( + "exception", # can't have indidivudal-files-default-upstream + b""" --- schema: 1 origin: @@ -1064,11 +1057,11 @@ def test_updatebot(self): product: Core component: Graphics """.strip(), - ), - # ------------------------------------------------- - ( - "exception", # must have indidivudal-files-default-upstream - b""" + ), + # ------------------------------------------------- + ( + "exception", # must have indidivudal-files-default-upstream + b""" --- schema: 1 origin: @@ -1096,11 +1089,11 @@ def test_updatebot(self): product: Core component: Graphics """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1124,11 +1117,11 @@ def test_updatebot(self): product: Core component: Graphics """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1148,11 +1141,11 @@ def test_updatebot(self): product: Core component: Graphics """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1175,11 +1168,11 @@ def test_updatebot(self): product: Core component: Graphics """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1201,11 +1194,11 @@ def test_updatebot(self): product: Core component: Graphics """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1245,11 +1238,11 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1289,11 +1282,11 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1332,11 +1325,11 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1375,11 +1368,11 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1419,11 +1412,11 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1463,11 +1456,11 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1506,11 +1499,11 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1540,11 +1533,11 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1576,11 +1569,11 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1605,11 +1598,11 @@ def test_updatebot(self): - type: vendoring filter: none """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1633,11 +1626,11 @@ def test_updatebot(self): tasks: - type: foo """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1664,11 +1657,11 @@ def test_updatebot(self): - .c - .cpp """.strip(), - ), - # ------------------------------------------------- - ( - "exception", - b""" + ), + # ------------------------------------------------- + ( + "exception", + b""" --- schema: 1 origin: @@ -1693,10 +1686,10 @@ def test_updatebot(self): - type: commit-alert filter: hogwash """.strip(), - ), - ( - "exception", - b""" + ), + ( + "exception", + b""" --- schema: 1 origin: @@ -1729,10 +1722,10 @@ def test_updatebot(self): source-extensions: - .c - .cpp""".strip(), - ), - ( - "exception", - b""" + ), + ( + "exception", + b""" --- schema: 1 origin: @@ -1765,10 +1758,10 @@ def test_updatebot(self): source-extensions: - .c - .cpp""".strip(), - ), - ( - "exception", - b""" + ), + ( + "exception", + b""" --- schema: 1 origin: @@ -1801,10 +1794,10 @@ def test_updatebot(self): source-extensions: - .c - .cpp""".strip(), - ), - ( - "exception", - b""" + ), + ( + "exception", + b""" --- schema: 1 origin: @@ -1837,10 +1830,10 @@ def test_updatebot(self): source-extensions: - .c - .cpp""".strip(), - ), - ( - "exception", - b""" + ), + ( + "exception", + b""" --- schema: 1 origin: @@ -1865,9 +1858,8 @@ def test_updatebot(self): - type: commit-alert frequency: 0 weeks """.strip(), - ), - ] - ) + ), + ]) # =========================================================================================== def test_malformed(self): @@ -1898,22 +1890,21 @@ def test_json(self): load_moz_yaml(tf.name, require_license_file=False) def test_revision(self): - self.process_test_vectors( - [ - ( - { - "schema": "1", - "origin": { - "description": "2D Graphics Library", - "license": ["MPL-1.1", "LGPL-2.1"], - "name": "cairo", - "release": "version 1.6.4", - "revision": "v1.6.37", - "url": "https://www.cairographics.org/", - }, - "bugzilla": {"component": "Graphics", "product": "Core"}, + self.process_test_vectors([ + ( + { + "schema": "1", + "origin": { + "description": "2D Graphics Library", + "license": ["MPL-1.1", "LGPL-2.1"], + "name": "cairo", + "release": "version 1.6.4", + "revision": "v1.6.37", + "url": "https://www.cairographics.org/", }, - b""" + "bugzilla": {"component": "Graphics", "product": "Core"}, + }, + b""" --- schema: 1 origin: @@ -1928,10 +1919,10 @@ def test_revision(self): bugzilla: product: Core component: Graphics""".strip(), - ), - ( - "exception", - b""" + ), + ( + "exception", + b""" --- schema: 1 origin: @@ -1946,10 +1937,10 @@ def test_revision(self): bugzilla: product: Core component: Graphics""".strip(), - ), - ( - "exception", - b""" + ), + ( + "exception", + b""" --- schema: 1 origin: @@ -1964,10 +1955,10 @@ def test_revision(self): bugzilla: product: Core component: Graphics""".strip(), - ), - ( - "exception", - b""" + ), + ( + "exception", + b""" --- schema: 1 origin: @@ -1982,10 +1973,10 @@ def test_revision(self): bugzilla: product: Core component: Graphics""".strip(), - ), - ( - "exception", - b""" + ), + ( + "exception", + b""" --- schema: 1 origin: @@ -2000,10 +1991,10 @@ def test_revision(self): bugzilla: product: Core component: Graphics""".strip(), - ), - ( - "exception", - b""" + ), + ( + "exception", + b""" --- schema: 1 origin: @@ -2018,10 +2009,10 @@ def test_revision(self): bugzilla: product: Core component: Graphics""".strip(), - ), - ( - "exception", - b""" + ), + ( + "exception", + b""" --- schema: 1 origin: @@ -2046,10 +2037,10 @@ def test_revision(self): args: ['hi'] pattern: 'hi' """.strip(), - ), - ( - "exception", - b""" + ), + ( + "exception", + b""" --- schema: 1 origin: @@ -2072,9 +2063,8 @@ def test_revision(self): cwd: '{cwd}' args: ['hi'] """.strip(), - ), - ] - ) + ), + ]) if __name__ == "__main__": diff --git a/python/mozbuild/mozbuild/test/test_preprocessor.py b/python/mozbuild/mozbuild/test/test_preprocessor.py index edc546e8578ae..88ce026ae1b8b 100644 --- a/python/mozbuild/mozbuild/test/test_preprocessor.py +++ b/python/mozbuild/mozbuild/test/test_preprocessor.py @@ -34,15 +34,13 @@ def do_include_pass(self, content_lines): self.do_include_compare(content_lines, ["PASS"]) def test_conditional_if_0(self): - self.do_include_pass( - [ - "#if 0", - "FAIL", - "#else", - "PASS", - "#endif", - ] - ) + self.do_include_pass([ + "#if 0", + "FAIL", + "#else", + "PASS", + "#endif", + ]) def test_no_marker(self): lines = [ @@ -80,99 +78,83 @@ def test_number_value(self): ) def test_conditional_if_0_elif_1(self): - self.do_include_pass( - [ - "#if 0", - "#elif 1", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#if 0", + "#elif 1", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_conditional_if_1(self): - self.do_include_pass( - [ - "#if 1", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#if 1", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_conditional_if_0_or_1(self): - self.do_include_pass( - [ - "#if 0 || 1", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#if 0 || 1", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_conditional_if_1_elif_1_else(self): - self.do_include_pass( - [ - "#if 1", - "PASS", - "#elif 1", - "FAIL", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#if 1", + "PASS", + "#elif 1", + "FAIL", + "#else", + "FAIL", + "#endif", + ]) def test_conditional_if_1_if_1(self): - self.do_include_pass( - [ - "#if 1", - "#if 1", - "PASS", - "#else", - "FAIL", - "#endif", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#if 1", + "#if 1", + "PASS", + "#else", + "FAIL", + "#endif", + "#else", + "FAIL", + "#endif", + ]) def test_conditional_not_0(self): - self.do_include_pass( - [ - "#if !0", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#if !0", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_conditional_not_0_and_1(self): - self.do_include_pass( - [ - "#if !0 && !1", - "FAIL", - "#else", - "PASS", - "#endif", - ] - ) + self.do_include_pass([ + "#if !0 && !1", + "FAIL", + "#else", + "PASS", + "#endif", + ]) def test_conditional_not_1(self): - self.do_include_pass( - [ - "#if !1", - "FAIL", - "#else", - "PASS", - "#endif", - ] - ) + self.do_include_pass([ + "#if !1", + "FAIL", + "#else", + "PASS", + "#endif", + ]) def test_conditional_not_emptyval(self): self.do_include_compare( @@ -193,36 +175,30 @@ def test_conditional_not_emptyval(self): ) def test_conditional_not_nullval(self): - self.do_include_pass( - [ - "#define NULLVAL 0", - "#if !NULLVAL", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#define NULLVAL 0", + "#if !NULLVAL", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_indentation(self): - self.do_include_pass( - [ - " #define NULLVAL 0", - " #if !NULLVAL", - "PASS", - " #else", - "FAIL", - " #endif", - ] - ) + self.do_include_pass([ + " #define NULLVAL 0", + " #if !NULLVAL", + "PASS", + " #else", + "FAIL", + " #endif", + ]) def test_expand(self): - self.do_include_pass( - [ - "#define ASVAR AS", - "#expand P__ASVAR__S", - ] - ) + self.do_include_pass([ + "#define ASVAR AS", + "#expand P__ASVAR__S", + ]) def test_undef_defined(self): self.do_include_compare( @@ -330,14 +306,12 @@ def test_filter_dumbComments_and_emptyLines(self): ) def test_filter_substitution(self): - self.do_include_pass( - [ - "#define VAR ASS", - "#filter substitution", - "P@VAR@", - "#unfilter substitution", - ] - ) + self.do_include_pass([ + "#define VAR ASS", + "#filter substitution", + "P@VAR@", + "#unfilter substitution", + ]) def test_error(self): with MockedOpen({"f": "#error spit this message out\n"}): @@ -361,265 +335,223 @@ def test_javascript_line(self): full = os.path.join(tmpdir, "javascript_line.js.in") with open(full, "w") as fh: fh.write( - "\n".join( - [ - "// Line 1", - "#if 0", - "// line 3", - "#endif", - "// line 5", - "# comment", - "// line 7", - "// line 8", - "// line 9", - "# another comment", - "// line 11", - "#define LINE 1", - "// line 13, given line number overwritten with 2", - "", - ] - ) + "\n".join([ + "// Line 1", + "#if 0", + "// line 3", + "#endif", + "// line 5", + "# comment", + "// line 7", + "// line 8", + "// line 9", + "# another comment", + "// line 11", + "#define LINE 1", + "// line 13, given line number overwritten with 2", + "", + ]) ) self.pp.do_include(full) - out = "\n".join( - [ - "// Line 1", - '//@line 5 "CWDjavascript_line.js.in"', - "// line 5", - '//@line 7 "CWDjavascript_line.js.in"', - "// line 7", - "// line 8", - "// line 9", - '//@line 11 "CWDjavascript_line.js.in"', - "// line 11", - '//@line 2 "CWDjavascript_line.js.in"', - "// line 13, given line number overwritten with 2", - "", - ] - ) + out = "\n".join([ + "// Line 1", + '//@line 5 "CWDjavascript_line.js.in"', + "// line 5", + '//@line 7 "CWDjavascript_line.js.in"', + "// line 7", + "// line 8", + "// line 9", + '//@line 11 "CWDjavascript_line.js.in"', + "// line 11", + '//@line 2 "CWDjavascript_line.js.in"', + "// line 13, given line number overwritten with 2", + "", + ]) out = out.replace("CWD", tmpdir + os.path.sep) self.assertEqual(self.pp.out.getvalue(), out) finally: shutil.rmtree(tmpdir) def test_literal(self): - self.do_include_pass( - [ - "#literal PASS", - ] - ) + self.do_include_pass([ + "#literal PASS", + ]) def test_var_directory(self): - self.do_include_pass( - [ - "#ifdef DIRECTORY", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#ifdef DIRECTORY", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_var_file(self): - self.do_include_pass( - [ - "#ifdef FILE", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#ifdef FILE", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_var_if_0(self): - self.do_include_pass( - [ - "#define VAR 0", - "#if VAR", - "FAIL", - "#else", - "PASS", - "#endif", - ] - ) + self.do_include_pass([ + "#define VAR 0", + "#if VAR", + "FAIL", + "#else", + "PASS", + "#endif", + ]) def test_var_if_0_elifdef(self): - self.do_include_pass( - [ - "#if 0", - "#elifdef FILE", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#if 0", + "#elifdef FILE", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_var_if_0_elifndef(self): - self.do_include_pass( - [ - "#if 0", - "#elifndef VAR", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#if 0", + "#elifndef VAR", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_var_ifdef_0(self): - self.do_include_pass( - [ - "#define VAR 0", - "#ifdef VAR", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#define VAR 0", + "#ifdef VAR", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_var_ifdef_1_or_undef(self): - self.do_include_pass( - [ - "#define FOO 1", - "#if defined(FOO) || defined(BAR)", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#define FOO 1", + "#if defined(FOO) || defined(BAR)", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_var_ifdef_undef(self): - self.do_include_pass( - [ - "#define VAR 0", - "#undef VAR", - "#ifdef VAR", - "FAIL", - "#else", - "PASS", - "#endif", - ] - ) + self.do_include_pass([ + "#define VAR 0", + "#undef VAR", + "#ifdef VAR", + "FAIL", + "#else", + "PASS", + "#endif", + ]) def test_var_ifndef_0(self): - self.do_include_pass( - [ - "#define VAR 0", - "#ifndef VAR", - "FAIL", - "#else", - "PASS", - "#endif", - ] - ) + self.do_include_pass([ + "#define VAR 0", + "#ifndef VAR", + "FAIL", + "#else", + "PASS", + "#endif", + ]) def test_var_ifndef_0_and_undef(self): - self.do_include_pass( - [ - "#define FOO 0", - "#if !defined(FOO) && !defined(BAR)", - "FAIL", - "#else", - "PASS", - "#endif", - ] - ) + self.do_include_pass([ + "#define FOO 0", + "#if !defined(FOO) && !defined(BAR)", + "FAIL", + "#else", + "PASS", + "#endif", + ]) def test_var_ifndef_undef(self): - self.do_include_pass( - [ - "#define VAR 0", - "#undef VAR", - "#ifndef VAR", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#define VAR 0", + "#undef VAR", + "#ifndef VAR", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_var_line(self): - self.do_include_pass( - [ - "#ifdef LINE", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#ifdef LINE", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_filterDefine(self): - self.do_include_pass( - [ - "#filter substitution", - "#define VAR AS", - "#define VAR2 P@VAR@", - "@VAR2@S", - ] - ) + self.do_include_pass([ + "#filter substitution", + "#define VAR AS", + "#define VAR2 P@VAR@", + "@VAR2@S", + ]) def test_number_value_equals(self): - self.do_include_pass( - [ - "#define FOO 1000", - "#if FOO == 1000", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#define FOO 1000", + "#if FOO == 1000", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_default_defines(self): self.pp.handleCommandLine(["-DFOO"]) - self.do_include_pass( - [ - "#if FOO == 1", - "PASS", - "#else", - "FAIL", - ] - ) + self.do_include_pass([ + "#if FOO == 1", + "PASS", + "#else", + "FAIL", + ]) def test_number_value_equals_defines(self): self.pp.handleCommandLine(["-DFOO=1000"]) - self.do_include_pass( - [ - "#if FOO == 1000", - "PASS", - "#else", - "FAIL", - ] - ) + self.do_include_pass([ + "#if FOO == 1000", + "PASS", + "#else", + "FAIL", + ]) def test_octal_value_equals(self): - self.do_include_pass( - [ - "#define FOO 0100", - "#if FOO == 0100", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#define FOO 0100", + "#if FOO == 0100", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_octal_value_equals_defines(self): self.pp.handleCommandLine(["-DFOO=0100"]) - self.do_include_pass( - [ - "#if FOO == 0100", - "PASS", - "#else", - "FAIL", - "#endif", - ] - ) + self.do_include_pass([ + "#if FOO == 0100", + "PASS", + "#else", + "FAIL", + "#endif", + ]) def test_value_quoted_expansion(self): """ @@ -647,27 +579,23 @@ def test_octal_value_quoted_expansion(self): def test_number_value_not_equals_quoted_defines(self): self.pp.handleCommandLine(['-DFOO="1000"']) - self.do_include_pass( - [ - "#if FOO == 1000", - "FAIL", - "#else", - "PASS", - "#endif", - ] - ) + self.do_include_pass([ + "#if FOO == 1000", + "FAIL", + "#else", + "PASS", + "#endif", + ]) def test_octal_value_not_equals_quoted_defines(self): self.pp.handleCommandLine(['-DFOO="0100"']) - self.do_include_pass( - [ - "#if FOO == 0100", - "FAIL", - "#else", - "PASS", - "#endif", - ] - ) + self.do_include_pass([ + "#if FOO == 0100", + "FAIL", + "#else", + "PASS", + "#endif", + ]) def test_undefined_variable(self): with MockedOpen({"f": "#filter substitution\n@foo@"}): @@ -677,29 +605,23 @@ def test_undefined_variable(self): def test_include(self): files = { - "foo/test": "\n".join( - [ - "#define foo foobarbaz", - "#include @inc@", - "@bar@", - "", - ] - ), - "bar": "\n".join( - [ - "#define bar barfoobaz", - "@foo@", - "", - ] - ), - "f": "\n".join( - [ - "#filter substitution", - "#define inc ../bar", - "#include foo/test", - "", - ] - ), + "foo/test": "\n".join([ + "#define foo foobarbaz", + "#include @inc@", + "@bar@", + "", + ]), + "bar": "\n".join([ + "#define bar barfoobaz", + "@foo@", + "", + ]), + "f": "\n".join([ + "#filter substitution", + "#define inc ../bar", + "#include foo/test", + "", + ]), } with MockedOpen(files): @@ -708,41 +630,33 @@ def test_include(self): def test_include_line(self): files = { - "srcdir/test.js": "\n".join( - [ - "#define foo foobarbaz", - "#include @inc@", - "@bar@", - "", - ] - ), - "srcdir/bar.js": "\n".join( - [ - "#define bar barfoobaz", - "@foo@", - "", - ] - ), - "srcdir/foo.js": "\n".join( - [ - "bazfoobar", - "#include bar.js", - "bazbarfoo", - "", - ] - ), + "srcdir/test.js": "\n".join([ + "#define foo foobarbaz", + "#include @inc@", + "@bar@", + "", + ]), + "srcdir/bar.js": "\n".join([ + "#define bar barfoobaz", + "@foo@", + "", + ]), + "srcdir/foo.js": "\n".join([ + "bazfoobar", + "#include bar.js", + "bazbarfoo", + "", + ]), "objdir/baz.js": "baz\n", - "srcdir/f.js": "\n".join( - [ - "#include foo.js", - "#filter substitution", - "#define inc bar.js", - "#include test.js", - "#include ../objdir/baz.js", - "fin", - "", - ] - ), + "srcdir/f.js": "\n".join([ + "#include foo.js", + "#filter substitution", + "#define inc bar.js", + "#include test.js", + "#include ../objdir/baz.js", + "fin", + "", + ]), } preprocessed = ( @@ -819,13 +733,11 @@ def test_invalid_ifdef(self): self.assertEqual(e.exception.key, "INVALID_VAR") # Trailing whitespaces, while not nice, shouldn't be an error. - self.do_include_pass( - [ - "#ifndef FOO ", - "PASS", - "#endif", - ] - ) + self.do_include_pass([ + "#ifndef FOO ", + "PASS", + "#endif", + ]) if __name__ == "__main__": diff --git a/python/mozbuild/mozbuild/test/test_pythonutil.py b/python/mozbuild/mozbuild/test/test_pythonutil.py index 6ebb5cc46e275..02438dd8f8235 100644 --- a/python/mozbuild/mozbuild/test/test_pythonutil.py +++ b/python/mozbuild/mozbuild/test/test_pythonutil.py @@ -12,12 +12,10 @@ def test_iter_modules_in_path(): tests_path = os.path.normcase(os.path.dirname(__file__)) paths = list(iter_modules_in_path(tests_path)) - assert set(paths) == set( - [ - os.path.join(os.path.abspath(tests_path), "__init__.py"), - os.path.join(os.path.abspath(tests_path), "test_pythonutil.py"), - ] - ) + assert set(paths) == set([ + os.path.join(os.path.abspath(tests_path), "__init__.py"), + os.path.join(os.path.abspath(tests_path), "test_pythonutil.py"), + ]) if __name__ == "__main__": diff --git a/python/mozbuild/mozbuild/test/test_telemetry_settings.py b/python/mozbuild/mozbuild/test/test_telemetry_settings.py index c170e56de1bc9..92e52292d0810 100644 --- a/python/mozbuild/mozbuild/test/test_telemetry_settings.py +++ b/python/mozbuild/mozbuild/test/test_telemetry_settings.py @@ -101,9 +101,7 @@ def _initialize_telemetry(settings, is_employee, contributor_prompt_response=Non return_value=contributor_prompt_response, ) as prompt_mock, mock.patch( "subprocess.run", return_value=Mock(returncode=0) - ), mock.patch( - "mach.config.ConfigSettings" - ), mock.patch( + ), mock.patch("mach.config.ConfigSettings"), mock.patch( "mach.telemetry.record_is_employee_telemetry_setting" ): initialize_telemetry_setting(settings, "", "") @@ -170,9 +168,7 @@ def mock_and_run(is_employee_bugzilla, is_employee_vcs): return_value=is_employee_bugzilla, ), mock.patch( "mach.telemetry.resolve_is_employee_by_vcs", return_value=is_employee_vcs - ), mock.patch( - "mach.telemetry.record_is_employee_telemetry_setting" - ): + ), mock.patch("mach.telemetry.record_is_employee_telemetry_setting"): fake_settings = Mock() fake_settings.mach_telemetry.is_employee = None return resolve_is_employee(None, str(tmpdir), fake_settings) @@ -196,9 +192,7 @@ def test_resolve_is_employee_no_cache_when_unknown(tmpdir, monkeypatch): return_value=None, ), mock.patch( "mach.telemetry.resolve_is_employee_by_vcs", return_value=None - ), mock.patch( - "mach.telemetry.record_is_employee_telemetry_setting" - ) as record_mock: + ), mock.patch("mach.telemetry.record_is_employee_telemetry_setting") as record_mock: fake_settings = Mock() fake_settings.mach_telemetry.is_employee = None result = resolve_is_employee(None, str(tmpdir), fake_settings) diff --git a/python/mozbuild/mozbuild/test/test_util.py b/python/mozbuild/mozbuild/test/test_util.py index 0467ff1cf9bd8..659f0a5a6fa5f 100644 --- a/python/mozbuild/mozbuild/test/test_util.py +++ b/python/mozbuild/mozbuild/test/test_util.py @@ -447,12 +447,10 @@ def test_iadd(self): class TestStrictOrderingOnAppendListWithFlagsFactory(unittest.TestCase): def test_strict_ordering_on_append_list_with_flags_factory(self): - cls = StrictOrderingOnAppendListWithFlagsFactory( - { - "foo": bool, - "bar": int, - } - ) + cls = StrictOrderingOnAppendListWithFlagsFactory({ + "foo": bool, + "bar": int, + }) l = cls() l += ["a", "b"] diff --git a/python/mozbuild/mozbuild/test/test_vendor.py b/python/mozbuild/mozbuild/test/test_vendor.py index 07ba0883377bc..d329d784d8357 100644 --- a/python/mozbuild/mozbuild/test/test_vendor.py +++ b/python/mozbuild/mozbuild/test/test_vendor.py @@ -32,16 +32,14 @@ def test_up_to_date_vendor(): # Verify that re-vendoring did not cause file changes. # Note that we don't want hg-ignored generated files # to bust the diff, so we exclude them (pycache, egg-info). - subprocess.check_call( - [ - "diff", - "-r", - os.path.join(topsrcdir, os.path.join("third_party", "python")), - os.path.join(work_dir, os.path.join("third_party", "python")), - "--exclude=__pycache__", - "--strip-trailing-cr", - ] - ) + subprocess.check_call([ + "diff", + "-r", + os.path.join(topsrcdir, os.path.join("third_party", "python")), + os.path.join(work_dir, os.path.join("third_party", "python")), + "--exclude=__pycache__", + "--strip-trailing-cr", + ]) if __name__ == "__main__": diff --git a/python/mozbuild/mozbuild/testing.py b/python/mozbuild/mozbuild/testing.py index 2b0cdb2684930..dbab68472bfc8 100644 --- a/python/mozbuild/mozbuild/testing.py +++ b/python/mozbuild/mozbuild/testing.py @@ -152,9 +152,10 @@ def convert_support_files(self, test, install_root, manifest_dir, out_dir): full = mozpath.normpath( mozpath.join(manifest_dir, mozpath.basename(pattern)) ) - info.installs.append( - (full, mozpath.join(install_root, pattern[1:])) - ) + info.installs.append(( + full, + mozpath.join(install_root, pattern[1:]), + )) else: full = mozpath.normpath(mozpath.join(manifest_dir, pattern)) dest_path = mozpath.join(out_dir, pattern) diff --git a/python/mozbuild/mozbuild/util.py b/python/mozbuild/mozbuild/util.py index 31e536bcb08b2..6943394cbc8f9 100644 --- a/python/mozbuild/mozbuild/util.py +++ b/python/mozbuild/mozbuild/util.py @@ -411,11 +411,9 @@ def extend(self, l): def __setitem__(self, key, val): if isinstance(key, slice): if not isinstance(val, list): - raise ValueError( - "List can only be sliced with other list " "instances." - ) + raise ValueError("List can only be sliced with other list instances.") if key.step: - raise ValueError("List cannot be sliced with a nonzero step " "value") + raise ValueError("List cannot be sliced with a nonzero step value") return super().__setitem__(key, val) return super().__setitem__(key, val) diff --git a/python/mozbuild/mozbuild/vendor/host_git.py b/python/mozbuild/mozbuild/vendor/host_git.py index 90f5125422b16..bcf09e5a680bb 100644 --- a/python/mozbuild/mozbuild/vendor/host_git.py +++ b/python/mozbuild/mozbuild/vendor/host_git.py @@ -13,9 +13,12 @@ class GitHost(BaseHost): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.workdir = tempfile.TemporaryDirectory(suffix="." + self.repo_url.netloc) - subprocess.check_call( - ["git", "clone", self.repo_url.geturl(), self.workdir.name] - ) + subprocess.check_call([ + "git", + "clone", + self.repo_url.geturl(), + self.workdir.name, + ]) def upstream_commit(self, revision): sha = subprocess.check_output( diff --git a/python/mozbuild/mozbuild/vendor/host_github.py b/python/mozbuild/mozbuild/vendor/host_github.py index 9300585f7107d..e99eef64a84ba 100644 --- a/python/mozbuild/mozbuild/vendor/host_github.py +++ b/python/mozbuild/mozbuild/vendor/host_github.py @@ -22,9 +22,11 @@ def upstream_commit(self, revision): return (info["sha"], info["commit"]["committer"]["date"]) def upstream_snapshot(self, revision): - return "/".join( - [self.manifest["vendoring"]["url"], "archive", revision + ".tar.gz"] - ) + return "/".join([ + self.manifest["vendoring"]["url"], + "archive", + revision + ".tar.gz", + ]) def upstream_path_to_file(self, revision, filepath): repo = self.repo_url.path[1:] diff --git a/python/mozbuild/mozbuild/vendor/host_gitlab.py b/python/mozbuild/mozbuild/vendor/host_gitlab.py index 8bfc3ddc79810..33b1d8c3d6fe7 100644 --- a/python/mozbuild/mozbuild/vendor/host_gitlab.py +++ b/python/mozbuild/mozbuild/vendor/host_gitlab.py @@ -21,6 +21,9 @@ def upstream_commit(self, revision): return (info["id"], info["committed_date"]) def upstream_snapshot(self, revision): - return "/".join( - [self.manifest["vendoring"]["url"], "-", "archive", revision + ".tar.gz"] - ) + return "/".join([ + self.manifest["vendoring"]["url"], + "-", + "archive", + revision + ".tar.gz", + ]) diff --git a/python/mozbuild/mozbuild/vendor/host_googlesource.py b/python/mozbuild/mozbuild/vendor/host_googlesource.py index 9b5e91579fdf5..0fe3b88f60ae6 100644 --- a/python/mozbuild/mozbuild/vendor/host_googlesource.py +++ b/python/mozbuild/mozbuild/vendor/host_googlesource.py @@ -12,9 +12,11 @@ class GoogleSourceHost(BaseHost): def upstream_commit(self, revision): """Query for a git commit and timestamp.""" - url = "/".join( - [self.manifest["vendoring"]["url"], "+", revision + "?format=JSON"] - ) + url = "/".join([ + self.manifest["vendoring"]["url"], + "+", + revision + "?format=JSON", + ]) req = requests.get(url) req.raise_for_status() try: @@ -29,9 +31,11 @@ def upstream_commit(self, revision): return (info["commit"], info["committer"]["time"]) def upstream_snapshot(self, revision): - return "/".join( - [self.manifest["vendoring"]["url"], "+archive", revision + ".tar.gz"] - ) + return "/".join([ + self.manifest["vendoring"]["url"], + "+archive", + revision + ".tar.gz", + ]) def upstream_path_to_file(self, revision, filepath): return ( diff --git a/python/mozbuild/mozbuild/vendor/mach_commands.py b/python/mozbuild/mozbuild/vendor/mach_commands.py index 4cae04c1fa8e4..5c91582b96927 100644 --- a/python/mozbuild/mozbuild/vendor/mach_commands.py +++ b/python/mozbuild/mozbuild/vendor/mach_commands.py @@ -178,9 +178,7 @@ def check_modified_files(command_context): {files} Please commit or stash these changes before vendoring, or re-run with `--ignore-modified`. -""".format( - files="\n".join(sorted(modified)) - ), +""".format(files="\n".join(sorted(modified))), ) sys.exit(1) diff --git a/python/mozbuild/mozbuild/vendor/moz_yaml.py b/python/mozbuild/mozbuild/vendor/moz_yaml.py index 38119c4005353..2a9f19846277f 100644 --- a/python/mozbuild/mozbuild/vendor/moz_yaml.py +++ b/python/mozbuild/mozbuild/vendor/moz_yaml.py @@ -152,93 +152,91 @@ def _schema_1(): ], ) - return Schema( - { - Required("schema"): "1", - Required("bugzilla"): { - Required("product"): All(str, Length(min=1)), - Required("component"): All(str, Length(min=1)), - }, - "origin": { - Required("name"): All(str, Length(min=1)), - Required("description"): All(str, Length(min=1)), - "notes": All(str, Length(min=1)), - Required("url"): FqdnUrl(), - Required("license"): Msg(License(), msg="Unsupported License"), - "license-file": All(str, Length(min=1)), - Required("release"): All(str, Length(min=1)), - # The following regex defines a valid git reference - # The first group [^ ~^:?*[\]] matches 0 or more times anything - # that isn't a Space, ~, ^, :, ?, *, or ] - # The second group [^ ~^:?*[\]\.]+ matches 1 or more times - # anything that isn't a Space, ~, ^, :, ?, *, [, ], or . - "revision": Match(r"^[^ ~^:?*[\]]*[^ ~^:?*[\]\.]+$"), - }, - "updatebot": { - Required("maintainer-phab"): All(str, Length(min=1)), - Required("maintainer-bz"): All(str, Length(min=1)), - "try-preset": All(str, Length(min=1)), - "fuzzy-query": All(str, Length(min=1)), - "fuzzy-paths": All([str], Length(min=1)), - "tasks": All( - UpdatebotTasks(), - [ - { - Required("type"): In( - ["vendoring", "commit-alert"], - msg="Invalid type specified in tasks", - ), - "branch": All(str, Length(min=1)), - "enabled": Boolean(), - "cc": Unique([str]), - "needinfo": Unique([str]), - "filter": In( - ["none", "security", "source-extensions"], - msg="Invalid filter value specified in tasks", - ), - "source-extensions": Unique([str]), - "blocking": Match(r"^[0-9]+$"), - "frequency": Match( - r"^(every|release|[1-9][0-9]* weeks?|[1-9][0-9]* commits?|" - + r"[1-9][0-9]* weeks?, ?[1-9][0-9]* commits?)$" - ), - "platform": Match(r"^(windows|linux)$"), - } - ], - ), - }, - "vendoring": { - Required("url"): FqdnUrl(), - Required("source-hosting"): All( - str, - Length(min=1), - In(VALID_SOURCE_HOSTS, msg="Unsupported Source Hosting"), - ), - "source-host-path": str, - "tracking": Match(r"^(commit|tag)$"), - "release-artifact": All(str, Length(min=1)), - "flavor": Match(r"^(regular|rust|individual-files)$"), - "skip-vendoring-steps": Unique([str]), - "vendor-directory": All(str, Length(min=1)), - "patches": Unique([str]), - "keep": Unique([str]), - "exclude": Unique([str]), - "include": Unique([str]), - "generated": Unique([str]), - "individual-files": [ + return Schema({ + Required("schema"): "1", + Required("bugzilla"): { + Required("product"): All(str, Length(min=1)), + Required("component"): All(str, Length(min=1)), + }, + "origin": { + Required("name"): All(str, Length(min=1)), + Required("description"): All(str, Length(min=1)), + "notes": All(str, Length(min=1)), + Required("url"): FqdnUrl(), + Required("license"): Msg(License(), msg="Unsupported License"), + "license-file": All(str, Length(min=1)), + Required("release"): All(str, Length(min=1)), + # The following regex defines a valid git reference + # The first group [^ ~^:?*[\]] matches 0 or more times anything + # that isn't a Space, ~, ^, :, ?, *, or ] + # The second group [^ ~^:?*[\]\.]+ matches 1 or more times + # anything that isn't a Space, ~, ^, :, ?, *, [, ], or . + "revision": Match(r"^[^ ~^:?*[\]]*[^ ~^:?*[\]\.]+$"), + }, + "updatebot": { + Required("maintainer-phab"): All(str, Length(min=1)), + Required("maintainer-bz"): All(str, Length(min=1)), + "try-preset": All(str, Length(min=1)), + "fuzzy-query": All(str, Length(min=1)), + "fuzzy-paths": All([str], Length(min=1)), + "tasks": All( + UpdatebotTasks(), + [ { - Required("upstream"): All(str, Length(min=1)), - Required("destination"): All(str, Length(min=1)), + Required("type"): In( + ["vendoring", "commit-alert"], + msg="Invalid type specified in tasks", + ), + "branch": All(str, Length(min=1)), + "enabled": Boolean(), + "cc": Unique([str]), + "needinfo": Unique([str]), + "filter": In( + ["none", "security", "source-extensions"], + msg="Invalid filter value specified in tasks", + ), + "source-extensions": Unique([str]), + "blocking": Match(r"^[0-9]+$"), + "frequency": Match( + r"^(every|release|[1-9][0-9]* weeks?|[1-9][0-9]* commits?|" + + r"[1-9][0-9]* weeks?, ?[1-9][0-9]* commits?)$" + ), + "platform": Match(r"^(windows|linux)$"), } ], - "individual-files-default-upstream": str, - "individual-files-default-destination": All(str, Length(min=1)), - "individual-files-list": Unique([str]), - "update-actions": actions_schema, - "post-patch-actions": actions_schema, - }, - } - ) + ), + }, + "vendoring": { + Required("url"): FqdnUrl(), + Required("source-hosting"): All( + str, + Length(min=1), + In(VALID_SOURCE_HOSTS, msg="Unsupported Source Hosting"), + ), + "source-host-path": str, + "tracking": Match(r"^(commit|tag)$"), + "release-artifact": All(str, Length(min=1)), + "flavor": Match(r"^(regular|rust|individual-files)$"), + "skip-vendoring-steps": Unique([str]), + "vendor-directory": All(str, Length(min=1)), + "patches": Unique([str]), + "keep": Unique([str]), + "exclude": Unique([str]), + "include": Unique([str]), + "generated": Unique([str]), + "individual-files": [ + { + Required("upstream"): All(str, Length(min=1)), + Required("destination"): All(str, Length(min=1)), + } + ], + "individual-files-default-upstream": str, + "individual-files-default-destination": All(str, Length(min=1)), + "individual-files-list": Unique([str]), + "update-actions": actions_schema, + "post-patch-actions": actions_schema, + }, + }) def _schema_1_additional(filename, manifest, require_license_file=True): diff --git a/python/mozbuild/mozbuild/vendor/rewrite_mozbuild.py b/python/mozbuild/mozbuild/vendor/rewrite_mozbuild.py index 85308c87f92c2..58b86b681e6be 100644 --- a/python/mozbuild/mozbuild/vendor/rewrite_mozbuild.py +++ b/python/mozbuild/mozbuild/vendor/rewrite_mozbuild.py @@ -434,23 +434,21 @@ def mozbuild_file_to_source_assignments(normalized_mozbuild_filename, assignment ) ) ] - source_assignment_nodes.extend( - [ - node - for node in ast.walk(root) - if isinstance(node, ast.Assign) - and ( - ( - isinstance(node.targets[0], ast.Name) - and node.targets[0].id == "EXPORTS" - ) - or ( - isinstance(node.targets[0], ast.Attribute) - and get_attribute_label(node.targets[0]).startswith("EXPORTS") - ) + source_assignment_nodes.extend([ + node + for node in ast.walk(root) + if isinstance(node, ast.Assign) + and ( + ( + isinstance(node.targets[0], ast.Name) + and node.targets[0].id == "EXPORTS" ) - ] - ) + or ( + isinstance(node.targets[0], ast.Attribute) + and get_attribute_label(node.targets[0]).startswith("EXPORTS") + ) + ) + ]) # Get the source-assignment-location for the node: assignment_index = 1 @@ -473,11 +471,12 @@ def mozbuild_file_to_source_assignments(normalized_mozbuild_filename, assignment if source_assignment_location in source_assignments: source_assignment_location = node_to_readable_file_location(code, a) - assert ( - source_assignment_location not in source_assignments - ), "In %s, two assignments have the same key ('%s')" % ( - normalized_mozbuild_filename, - source_assignment_location, + assert source_assignment_location not in source_assignments, ( + "In %s, two assignments have the same key ('%s')" + % ( + normalized_mozbuild_filename, + source_assignment_location, + ) ) source_assignments[source_assignment_location] = normalized_source_filename_list assignment_index += 1 @@ -562,9 +561,9 @@ def get_mozbuild_file_search_order( ordered_list = [] if all_mozbuild_filenames_normalized is None: - assert os.path.isfile( - ".arcconfig" - ), "We do not seem to be running from the gecko root" + assert os.path.isfile(".arcconfig"), ( + "We do not seem to be running from the gecko root" + ) # The first time around, this variable name is incorrect. # It's actually the full path+filename, not a directory. @@ -642,12 +641,10 @@ def filenames_directory_is_in_filename_list( f("foo/bar/a.c", ["foo/b.c", "foo/bar/c.c"]) -> true f("foo/bar/a.c", ["foo/b.c", "foo/bar/baz/d.c"]) -> false """ - path_list = set( - [ - os.path.dirname(f).replace(os.path.sep, "/") - for f in list_of_normalized_filenames - ] - ) + path_list = set([ + os.path.dirname(f).replace(os.path.sep, "/") + for f in list_of_normalized_filenames + ]) return os.path.dirname(filename_normalized).replace(os.path.sep, "/") in path_list @@ -1050,9 +1047,9 @@ def add_file_to_moz_build_file( normalized_filename_to_add = original_normalized_filename_to_add continue - assert ( - len(possible_assignments) > 0 - ), "Could not find a single possible source assignment" + assert len(possible_assignments) > 0, ( + "Could not find a single possible source assignment" + ) if len(possible_assignments) > 1: best_guess, _ = guess_best_assignment( possible_assignments, normalized_filename_to_add diff --git a/python/mozbuild/mozbuild/vendor/vendor_manifest.py b/python/mozbuild/mozbuild/vendor/vendor_manifest.py index 79cfa16ccb3e5..a5b04a163ec1b 100644 --- a/python/mozbuild/mozbuild/vendor/vendor_manifest.py +++ b/python/mozbuild/mozbuild/vendor/vendor_manifest.py @@ -663,18 +663,13 @@ def update_yaml(self, revision, timestamp): f.write(("".join(yaml)).encode("utf-8")) def spurious_check(self, revision, ignore_modified): - changed_files = set( - [ - os.path.abspath(f) - for f in self.repository.get_changed_files(mode="staged") - ] - ) - generated_files = set( - [ - self.get_full_path(f) - for f in self.manifest["vendoring"].get("generated", []) - ] - ) + changed_files = set([ + os.path.abspath(f) for f in self.repository.get_changed_files(mode="staged") + ]) + generated_files = set([ + self.get_full_path(f) + for f in self.manifest["vendoring"].get("generated", []) + ]) changed_files = set(changed_files) - generated_files if not changed_files: self.logInfo({"r": revision}, "Upstream {r} hasn't modified files locally.") @@ -855,12 +850,15 @@ def update_moz_build(self, vendoring_dir, moz_yaml_dir, add_to_exports): self.log( logging.WARNING, "header_files_warning", - {}, + { + "num_headers": len(header_files_to_add), + "headers": header_files_to_add, + }, ( - "We found %s header files in the update, pass --add-to-exports if you want" - + " to attempt to include them in EXPORTS blocks: %s" - ) - % (len(header_files_to_add), header_files_to_add), + "We found {num_headers} header files in the update, pass " + + "--add-to-exports if you want to attempt to include them " + + "in EXPORTS blocks: {header_files_to_add}" + ), ) self.logInfo( @@ -876,8 +874,8 @@ def update_moz_build(self, vendoring_dir, moz_yaml_dir, add_to_exports): self.log( logging.ERROR, "vendor", - {}, - "Could not add %s to the appropriate moz.build file" % f, + {"f": f}, + "Could not add {f} to the appropriate moz.build file", ) should_abort = True @@ -888,8 +886,8 @@ def update_moz_build(self, vendoring_dir, moz_yaml_dir, add_to_exports): self.log( logging.ERROR, "vendor", - {}, - "Could not remove %s from the appropriate moz.build file" % f, + {"f": f}, + "Could not remove {f} from the appropriate moz.build file", ) should_abort = True @@ -925,10 +923,12 @@ def import_local_patches(self, patches, yaml_dir, vendor_dir): "--input", os.path.abspath(patch), "--no-backup-if-mismatch", + "--batch", ] self.run_process( args=script, log_name=script, + ensure_exit_code=0, ) except Exception as e: # Check if reject file has content (patch failed) @@ -938,8 +938,8 @@ def import_local_patches(self, patches, yaml_dir, vendor_dir): self.log( logging.ERROR, "vendor", - {}, - f"Patch rejection details:\n{reject_content}", + {"reject_content": reject_content}, + "Patch rejection details:\n{reject_content}", ) raise e finally: @@ -956,5 +956,5 @@ def import_local_patches(self, patches, yaml_dir, vendor_dir): ) msgs.append("I am going to re-throw the exception now.") for m in msgs: - self.log(logging.WARN, "vendor", {}, m) + self.log(logging.WARN, "vendor", {"m": m}, "{m}") raise e diff --git a/python/mozbuild/mozbuild/vendor/vendor_python.py b/python/mozbuild/mozbuild/vendor/vendor_python.py index 72f351b3d7af0..f7aab83062201 100644 --- a/python/mozbuild/mozbuild/vendor/vendor_python.py +++ b/python/mozbuild/mozbuild/vendor/vendor_python.py @@ -146,23 +146,21 @@ def vendor( with TemporaryDirectory() as tmp: # use requirements.txt to download archived source distributions of all # packages - subprocess.check_call( - [ - sys.executable, - "-m", - "pip", - "download", - "-r", - str(requirements_path), - "--no-deps", - "--dest", - tmp, - "--abi", - "none", - "--platform", - "any", - ] - ) + subprocess.check_call([ + sys.executable, + "-m", + "pip", + "download", + "-r", + str(requirements_path), + "--no-deps", + "--dest", + tmp, + "--abi", + "none", + "--platform", + "any", + ]) _purge_vendor_dir(vendor_dir) self._extract(tmp, vendor_dir, keep_extra_files) diff --git a/python/mozbuild/mozbuild/vendor/vendor_rust.py b/python/mozbuild/mozbuild/vendor/vendor_rust.py index 6ba4ffe1123db..1f27984dd1ecd 100644 --- a/python/mozbuild/mozbuild/vendor/vendor_rust.py +++ b/python/mozbuild/mozbuild/vendor/vendor_rust.py @@ -163,20 +163,18 @@ def __init__(self, *args, **kwargs): self._issues = [] def serialize_issues_json(self): - return json.dumps( - { - "Cargo.lock": [ - { - "path": "Cargo.lock", - "column": None, - "line": None, - "level": "error" if level == logging.ERROR else "warning", - "message": msg, - } - for (level, msg) in self._issues - ] - } - ) + return json.dumps({ + "Cargo.lock": [ + { + "path": "Cargo.lock", + "column": None, + "line": None, + "level": "error" if level == logging.ERROR else "warning", + "message": msg, + } + for (level, msg) in self._issues + ] + }) def generate_diff_stream(self): return self.repository.diff_stream() @@ -268,9 +266,7 @@ def has_modified_files(self): {files} Please commit or stash these changes before vendoring, or re-run with `--ignore-modified`. -""".format( - files="\n".join(sorted(modified)) - ), +""".format(files="\n".join(sorted(modified))), ) return modified @@ -706,13 +702,11 @@ def vendor(self, ignore_modified=False, force=False): for name, packages in grouped.items(): # Allow to have crates of the same name when one depends on the other. - num = len( - [ - p - for p in packages - if all(d.split()[0] != name for d in p.get("dependencies", [])) - ] - ) + num = len([ + p + for p in packages + if all(d.split()[0] != name for d in p.get("dependencies", [])) + ]) if num > 1: self.log( logging.ERROR, @@ -728,12 +722,10 @@ def vendor(self, ignore_modified=False, force=False): # Only emit warnings for cargo-vet for now. env = os.environ.copy() - env["PATH"] = os.pathsep.join( - ( - str(Path(cargo).parent), - os.environ["PATH"], - ) - ) + env["PATH"] = os.pathsep.join(( + str(Path(cargo).parent), + os.environ["PATH"], + )) flags = ["--output-format=json"] if "MOZ_AUTOMATION" in os.environ: flags.append("--locked") diff --git a/python/mozbuild/mozpack/archive.py b/python/mozbuild/mozpack/archive.py index 308d5c61f2add..70502519e475b 100644 --- a/python/mozbuild/mozpack/archive.py +++ b/python/mozbuild/mozpack/archive.py @@ -79,7 +79,7 @@ def create_tar_from_files(fp, files): # would be a glaring security hole if the archive were # uncompressed as root. if ti.mode & (stat.S_ISUID | stat.S_ISGID): - raise ValueError("cannot add file with setuid or setgid set: " "%s" % f) + raise ValueError("cannot add file with setuid or setgid set: %s" % f) # Set uid, gid, username, and group as deterministic values. ti.uid = 0 diff --git a/python/mozbuild/mozpack/chrome/manifest.py b/python/mozbuild/mozpack/chrome/manifest.py index a69b93ebc839b..ef1675cc27141 100644 --- a/python/mozbuild/mozpack/chrome/manifest.py +++ b/python/mozbuild/mozpack/chrome/manifest.py @@ -339,16 +339,14 @@ def __str__(self): # All manifest classes by their type name. -MANIFESTS_TYPES = dict( - [ - (c.type, c) - for c in globals().values() - if type(c) is type - and issubclass(c, ManifestEntry) - and hasattr(c, "type") - and c.type - ] -) +MANIFESTS_TYPES = dict([ + (c.type, c) + for c in globals().values() + if type(c) is type + and issubclass(c, ManifestEntry) + and hasattr(c, "type") + and c.type +]) MANIFEST_RE = re.compile(r"^#.*$") diff --git a/python/mozbuild/mozpack/dmg.py b/python/mozbuild/mozpack/dmg.py index 0794fb5742cc8..e959624b54486 100644 --- a/python/mozbuild/mozpack/dmg.py +++ b/python/mozbuild/mozpack/dmg.py @@ -52,15 +52,13 @@ def generate_hfs_file( size = int(output.split()[0]) / 1000 # Get in MB size = int(size * 1.02) # Bump the used size slightly larger. # Setup a proper file sized out with zero's - subprocess.check_call( - [ - "dd", - "if=/dev/zero", - f"of={hfs}", - "bs=1M", - f"count={size}", - ] - ) + subprocess.check_call([ + "dd", + "if=/dev/zero", + f"of={hfs}", + "bs=1M", + f"count={size}", + ]) subprocess.check_call([mkfshfs_tool, "-v", volume_name, hfs]) @@ -106,23 +104,26 @@ def create_dmg_from_staged( if attribution_sentinel: while len(attribution_sentinel) < 1024: attribution_sentinel += "\t" - subprocess.check_call( - [ - hfs_tool, - hfs, - "setattr", - f"{volume_name}.app", - "com.apple.application-instance", - attribution_sentinel, - ] - ) + subprocess.check_call([ + hfs_tool, + hfs, + "setattr", + f"{volume_name}.app", + "com.apple.application-instance", + attribution_sentinel, + ]) subprocess.check_call(["cp", hfs, str(Path(output_dmg).parent)]) dmg_cmd.append(attribution_sentinel) if compression == "lzma": - dmg_cmd.extend( - ["--compression", "lzma", "--level", "5", "--run-sectors", "2048"] - ) + dmg_cmd.extend([ + "--compression", + "lzma", + "--level", + "5", + "--run-sectors", + "2048", + ]) subprocess.check_call( dmg_cmd, @@ -135,35 +136,31 @@ def create_dmg_from_staged( format = "ULMO" hybrid = tmpdir / "hybrid.dmg" - subprocess.check_call( - [ - "hdiutil", - "makehybrid", - "-hfs", - "-hfs-volume-name", - volume_name, - "-hfs-openfolder", - stagedir, - "-ov", - stagedir, - "-o", - hybrid, - ] - ) - subprocess.check_call( - [ - "hdiutil", - "convert", - "-format", - format, - "-imagekey", - "bzip2-level=9", - "-ov", - hybrid, - "-o", - output_dmg, - ] - ) + subprocess.check_call([ + "hdiutil", + "makehybrid", + "-hfs", + "-hfs-volume-name", + volume_name, + "-hfs-openfolder", + stagedir, + "-ov", + stagedir, + "-o", + hybrid, + ]) + subprocess.check_call([ + "hdiutil", + "convert", + "-format", + format, + "-imagekey", + "bzip2-level=9", + "-ov", + hybrid, + "-o", + output_dmg, + ]) def create_dmg( diff --git a/python/mozbuild/mozpack/files.py b/python/mozbuild/mozpack/files.py index f03b375d26bdb..ac31ed129f42c 100644 --- a/python/mozbuild/mozpack/files.py +++ b/python/mozbuild/mozpack/files.py @@ -1253,13 +1253,11 @@ def __init__(self, repo, rev=".", recognize_repo_paths=False, **kwargs): # Immediately populate the list of files in the repo since nearly every # operation requires this list. - out = self._client.rawcommand( - [ - b"files", - b"--rev", - self._rev.encode(), - ] - ) + out = self._client.rawcommand([ + b"files", + b"--rev", + self._rev.encode(), + ]) for relpath in out.splitlines(): # Mercurial may use \ as path separator on Windows. So use # normpath(). diff --git a/python/mozbuild/mozpack/mozjar.py b/python/mozbuild/mozpack/mozjar.py index fd53cea4a4fc3..55e2ddd250981 100644 --- a/python/mozbuild/mozpack/mozjar.py +++ b/python/mozbuild/mozpack/mozjar.py @@ -197,18 +197,16 @@ class JarCdirEnd(JarStruct): """ MAGIC = 0x06054B50 - STRUCT = OrderedDict( - [ - ("disk_num", "uint16"), - ("cdir_disk", "uint16"), - ("disk_entries", "uint16"), - ("cdir_entries", "uint16"), - ("cdir_size", "uint32"), - ("cdir_offset", "uint32"), - ("comment_size", "uint16"), - ("comment", "comment_size"), - ] - ) + STRUCT = OrderedDict([ + ("disk_num", "uint16"), + ("cdir_disk", "uint16"), + ("disk_entries", "uint16"), + ("cdir_entries", "uint16"), + ("cdir_size", "uint32"), + ("cdir_offset", "uint32"), + ("comment_size", "uint16"), + ("comment", "comment_size"), + ]) CDIR_END_SIZE = JarCdirEnd().size @@ -220,29 +218,27 @@ class JarCdirEntry(JarStruct): """ MAGIC = 0x02014B50 - STRUCT = OrderedDict( - [ - ("creator_version", "uint16"), - ("min_version", "uint16"), - ("general_flag", "uint16"), - ("compression", "uint16"), - ("lastmod_time", "uint16"), - ("lastmod_date", "uint16"), - ("crc32", "uint32"), - ("compressed_size", "uint32"), - ("uncompressed_size", "uint32"), - ("filename_size", "uint16"), - ("extrafield_size", "uint16"), - ("filecomment_size", "uint16"), - ("disknum", "uint16"), - ("internal_attr", "uint16"), - ("external_attr", "uint32"), - ("offset", "uint32"), - ("filename", "filename_size"), - ("extrafield", "extrafield_size"), - ("filecomment", "filecomment_size"), - ] - ) + STRUCT = OrderedDict([ + ("creator_version", "uint16"), + ("min_version", "uint16"), + ("general_flag", "uint16"), + ("compression", "uint16"), + ("lastmod_time", "uint16"), + ("lastmod_date", "uint16"), + ("crc32", "uint32"), + ("compressed_size", "uint32"), + ("uncompressed_size", "uint32"), + ("filename_size", "uint16"), + ("extrafield_size", "uint16"), + ("filecomment_size", "uint16"), + ("disknum", "uint16"), + ("internal_attr", "uint16"), + ("external_attr", "uint32"), + ("offset", "uint32"), + ("filename", "filename_size"), + ("extrafield", "extrafield_size"), + ("filecomment", "filecomment_size"), + ]) class JarLocalFileHeader(JarStruct): @@ -251,22 +247,20 @@ class JarLocalFileHeader(JarStruct): """ MAGIC = 0x04034B50 - STRUCT = OrderedDict( - [ - ("min_version", "uint16"), - ("general_flag", "uint16"), - ("compression", "uint16"), - ("lastmod_time", "uint16"), - ("lastmod_date", "uint16"), - ("crc32", "uint32"), - ("compressed_size", "uint32"), - ("uncompressed_size", "uint32"), - ("filename_size", "uint16"), - ("extra_field_size", "uint16"), - ("filename", "filename_size"), - ("extra_field", "extra_field_size"), - ] - ) + STRUCT = OrderedDict([ + ("min_version", "uint16"), + ("general_flag", "uint16"), + ("compression", "uint16"), + ("lastmod_time", "uint16"), + ("lastmod_date", "uint16"), + ("crc32", "uint32"), + ("compressed_size", "uint32"), + ("uncompressed_size", "uint32"), + ("filename_size", "uint16"), + ("extra_field_size", "uint16"), + ("filename", "filename_size"), + ("extra_field", "extra_field_size"), + ]) class JarFileReader: diff --git a/python/mozbuild/mozpack/pkg.py b/python/mozbuild/mozpack/pkg.py index bfb5b0211d343..5ef950a57ae7a 100644 --- a/python/mozbuild/mozpack/pkg.py +++ b/python/mozbuild/mozpack/pkg.py @@ -165,17 +165,15 @@ def create_bom(bom_path: Path, root_path: Path, mkbom_tool: Path): mkbom_tool: Path, mkbom tool Path """ print(f"Creating BOM file from {root_path} to {bom_path}") - subprocess.check_call( - [ - mkbom_tool, - "-u", - "0", - "-g", - "80", - str(root_path), - str(bom_path), - ] - ) + subprocess.check_call([ + mkbom_tool, + "-u", + "0", + "-g", + "80", + str(root_path), + str(bom_path), + ]) print(f"Created BOM File size: {bom_path.stat().st_size // 1024}kb") @@ -256,14 +254,12 @@ def create_pkg( root_path.mkdir(parents=True, exist_ok=True) # Copy files over - subprocess.check_call( - [ - "cp", - "-R", - str(source_app), - str(root_path), - ] - ) + subprocess.check_call([ + "cp", + "-R", + str(source_app), + str(root_path), + ]) # Count all files (innards + itself) file_count = len(list(source_app.glob("**/*"))) + 1 diff --git a/python/mozbuild/mozpack/test/test_chrome_manifest.py b/python/mozbuild/mozpack/test/test_chrome_manifest.py index c1d5826bbc87e..936af1b7c1394 100644 --- a/python/mozbuild/mozpack/test/test_chrome_manifest.py +++ b/python/mozbuild/mozpack/test/test_chrome_manifest.py @@ -108,12 +108,10 @@ def test_parse_manifest(self): "chrome://browser/content/viewSourceOverlay.xul", ), ] - with mozunit.MockedOpen( - { - "manifest": "\n".join(manifest), - "other/manifest": "\n".join(other_manifest), - } - ): + with mozunit.MockedOpen({ + "manifest": "\n".join(manifest), + "other/manifest": "\n".join(other_manifest), + }): # Ensure we have tests for all types of manifests. self.assertEqual( set(type(e) for e in expected_result), set(MANIFESTS_TYPES.values()) diff --git a/python/mozbuild/mozpack/test/test_files.py b/python/mozbuild/mozpack/test/test_files.py index e09d31acedcf5..af80aa036b6bb 100644 --- a/python/mozbuild/mozpack/test/test_files.py +++ b/python/mozbuild/mozpack/test/test_files.py @@ -829,12 +829,12 @@ def test_manifest_file(self): # because they will be automatically included by virtue of being an # argument to a method of |bar|. bar_xpt = GeneratedFile( - b"\x58\x50\x43\x4F\x4D\x0A\x54\x79\x70\x65\x4C\x69\x62\x0D\x0A\x1A" - + b"\x01\x02\x00\x02\x00\x00\x00\x7B\x00\x00\x00\x24\x00\x00\x00\x5C" + b"\x58\x50\x43\x4f\x4d\x0a\x54\x79\x70\x65\x4c\x69\x62\x0d\x0a\x1a" + + b"\x01\x02\x00\x02\x00\x00\x00\x7b\x00\x00\x00\x24\x00\x00\x00\x5c" + b"\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - + b"\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x5F" - + b"\x70\xDA\x76\x51\x9C\x48\x58\xB7\x1E\xE3\xC9\x23\x33\xE2\xD6\x00" - + b"\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x0D\x00\x66\x6F\x6F\x00" + + b"\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x5f" + + b"\x70\xda\x76\x51\x9c\x48\x58\xb7\x1e\xe3\xc9\x23\x33\xe2\xd6\x00" + + b"\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x66\x6f\x6f\x00" + b"\x62\x61\x72\x00\x62\x61\x72\x00\x00\x00\x00\x01\x00\x00\x00\x00" + b"\x09\x01\x80\x92\x00\x01\x80\x06\x00\x00\x80" ) @@ -845,11 +845,11 @@ def test_manifest_file(self): # void foo(); # }; foo_xpt = GeneratedFile( - b"\x58\x50\x43\x4F\x4D\x0A\x54\x79\x70\x65\x4C\x69\x62\x0D\x0A\x1A" + b"\x58\x50\x43\x4f\x4d\x0a\x54\x79\x70\x65\x4c\x69\x62\x0d\x0a\x1a" + b"\x01\x02\x00\x01\x00\x00\x00\x57\x00\x00\x00\x24\x00\x00\x00\x40" - + b"\x80\x00\x00\x32\x71\xBE\xBC\x92\x7E\x4B\xEF\x93\x5E\x44\xE0\xAA" - + b"\xF3\xC1\xE5\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x09\x00" - + b"\x66\x6F\x6F\x00\x66\x6F\x6F\x00\x00\x00\x00\x01\x00\x00\x00\x00" + + b"\x80\x00\x00\x32\x71\xbe\xbc\x92\x7e\x4b\xef\x93\x5e\x44\xe0\xaa" + + b"\xf3\xc1\xe5\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x09\x00" + + b"\x66\x6f\x6f\x00\x66\x6f\x6f\x00\x00\x00\x00\x01\x00\x00\x00\x00" + b"\x05\x00\x80\x06\x00\x00\x00" ) @@ -859,11 +859,11 @@ def test_manifest_file(self): # void foo(); # }; foo2_xpt = GeneratedFile( - b"\x58\x50\x43\x4F\x4D\x0A\x54\x79\x70\x65\x4C\x69\x62\x0D\x0A\x1A" + b"\x58\x50\x43\x4f\x4d\x0a\x54\x79\x70\x65\x4c\x69\x62\x0d\x0a\x1a" + b"\x01\x02\x00\x01\x00\x00\x00\x57\x00\x00\x00\x24\x00\x00\x00\x40" - + b"\x80\x00\x00\x70\x57\xF2\xAA\xFD\xC2\x45\x59\xAB\xDE\x08\xD9\x39" - + b"\xF7\xE8\x0D\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x09\x00" - + b"\x66\x6F\x6F\x00\x66\x6F\x6F\x00\x00\x00\x00\x01\x00\x00\x00\x00" + + b"\x80\x00\x00\x70\x57\xf2\xaa\xfd\xc2\x45\x59\xab\xde\x08\xd9\x39" + + b"\xf7\xe8\x0d\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x09\x00" + + b"\x66\x6f\x6f\x00\x66\x6f\x6f\x00\x00\x00\x00\x01\x00\x00\x00\x00" + b"\x05\x00\x80\x06\x00\x00\x00" ) @@ -1282,12 +1282,10 @@ def test_composed_finder(self): ensureParentDir(self.tmppath("a/foo/qux/hoge")) open(self.tmppath("a/foo/qux/hoge"), "wb").write(b"hoge") open(self.tmppath("a/foo/qux/bar"), "wb").write(b"not the right content") - self.finder = ComposedFinder( - { - "": FileFinder(self.tmppath("a")), - "foo/qux": FileFinder(self.tmppath("b")), - } - ) + self.finder = ComposedFinder({ + "": FileFinder(self.tmppath("a")), + "foo/qux": FileFinder(self.tmppath("b")), + }) self.do_match_test() self.assertIsNone(self.finder.get("does-not-exist")) diff --git a/python/mozbuild/mozpack/test/test_mozjar.py b/python/mozbuild/mozpack/test/test_mozjar.py index fb5c7dbf4b354..9a935ccdb8f22 100644 --- a/python/mozbuild/mozpack/test/test_mozjar.py +++ b/python/mozbuild/mozpack/test/test_mozjar.py @@ -29,17 +29,15 @@ class TestJarStruct(unittest.TestCase): class Foo(JarStruct): MAGIC = 0x01020304 - STRUCT = OrderedDict( - [ - ("foo", "uint32"), - ("bar", "uint16"), - ("qux", "uint16"), - ("length", "uint16"), - ("length2", "uint16"), - ("string", "length"), - ("string2", "length2"), - ] - ) + STRUCT = OrderedDict([ + ("foo", "uint32"), + ("bar", "uint16"), + ("qux", "uint16"), + ("length", "uint16"), + ("length2", "uint16"), + ("string", "length"), + ("string2", "length2"), + ]) def test_jar_struct(self): foo = TestJarStruct.Foo() @@ -304,30 +302,26 @@ def test_preload(self): class TestJarLog(unittest.TestCase): def test_jarlog(self): s = StringIO( - "\n".join( - [ - "bar/baz.jar first", - "bar/baz.jar second", - "bar/baz.jar third", - "bar/baz.jar second", - "bar/baz.jar second", - "omni.ja stuff", - "bar/baz.jar first", - "omni.ja other/stuff", - "omni.ja stuff", - "bar/baz.jar third", - ] - ) + "\n".join([ + "bar/baz.jar first", + "bar/baz.jar second", + "bar/baz.jar third", + "bar/baz.jar second", + "bar/baz.jar second", + "omni.ja stuff", + "bar/baz.jar first", + "omni.ja other/stuff", + "omni.ja stuff", + "bar/baz.jar third", + ]) ) log = JarLog(fileobj=s) self.assertEqual( set(log.keys()), - set( - [ - "bar/baz.jar", - "omni.ja", - ] - ), + set([ + "bar/baz.jar", + "omni.ja", + ]), ) self.assertEqual( log["bar/baz.jar"], diff --git a/python/mozbuild/mozpack/test/test_packager.py b/python/mozbuild/mozpack/test/test_packager.py index 3b4c3d2d6e88c..2df4608ae2ff5 100644 --- a/python/mozbuild/mozpack/test/test_packager.py +++ b/python/mozbuild/mozpack/test/test_packager.py @@ -160,12 +160,10 @@ def __init__(self, path, content): packager.add( "qux/qux.manifest", GeneratedFile( - b"".join( - [ - b"resource qux qux/\n", - b"binary-component qux.so\n", - ] - ) + b"".join([ + b"resource qux qux/\n", + b"binary-component qux.so\n", + ]) ), ) bar_xpt = GeneratedFile(b"bar.xpt") @@ -180,12 +178,10 @@ def __init__(self, path, content): file = GeneratedFileWithPath( os.path.join(curdir, "foo.manifest"), - b"".join( - [ - b"manifest foo/bar.manifest\n", - b"manifest bar/baz.manifest\n", - ] - ), + b"".join([ + b"manifest foo/bar.manifest\n", + b"manifest bar/baz.manifest\n", + ]), ) with errors.context("manifest", 6): packager.add("foo.manifest", file) @@ -399,26 +395,24 @@ def __init__(self, path, content): self.assertEqual( packager.get_bases(), - set( - [ - "", - "addon", - "addon2", - "addon3", - "addon4", - "addon5", - "addon6", - "addon7", - "addon8", - "addon9", - "addon10", - "addon11", - "qux", - "hybrid", - "hybrid2", - "webextension", - ] - ), + set([ + "", + "addon", + "addon2", + "addon3", + "addon4", + "addon5", + "addon6", + "addon7", + "addon8", + "addon9", + "addon10", + "addon11", + "qux", + "hybrid", + "hybrid2", + "webextension", + ]), ) self.assertEqual(packager.get_bases(addons=False), set(["", "qux"])) @@ -429,9 +423,7 @@ def test_simple_packager_manifest_consistency(self): packager = SimplePackager(formatter) packager.add( "base.manifest", - GeneratedFile( - b"manifest foo/bar.manifest\n" b"manifest bar/baz.manifest\n" - ), + GeneratedFile(b"manifest foo/bar.manifest\nmanifest bar/baz.manifest\n"), ) packager.add("foo/bar.manifest", GeneratedFile(b"resource bar bar")) packager.add("bar/baz.manifest", GeneratedFile(b"resource baz baz")) @@ -452,9 +444,7 @@ def test_simple_packager_manifest_consistency(self): packager = SimplePackager(formatter) packager.add( "base.manifest", - GeneratedFile( - b"manifest foo/bar.manifest\n" b"manifest bar/baz.manifest\n" - ), + GeneratedFile(b"manifest foo/bar.manifest\nmanifest bar/baz.manifest\n"), ) packager.add("foo/bar.manifest", GeneratedFile(b"resource bar bar")) packager.add("bar/baz.manifest", GeneratedFile(b"resource baz baz")) @@ -487,16 +477,14 @@ def test_simple_manifest_parser(self): foobaz = GeneratedFile(b"foobaz") fooqux = GeneratedFile(b"fooqux") foozot = GeneratedFile(b"foozot") - finder = MockFinder( - { - "bin/foo/bar": foobar, - "bin/foo/baz": foobaz, - "bin/foo/qux": fooqux, - "bin/foo/zot": foozot, - "bin/foo/chrome.manifest": GeneratedFile(b"resource foo foo/"), - "bin/chrome.manifest": GeneratedFile(b"manifest foo/chrome.manifest"), - } - ) + finder = MockFinder({ + "bin/foo/bar": foobar, + "bin/foo/baz": foobaz, + "bin/foo/qux": fooqux, + "bin/foo/zot": foozot, + "bin/foo/chrome.manifest": GeneratedFile(b"resource foo foo/"), + "bin/chrome.manifest": GeneratedFile(b"manifest foo/chrome.manifest"), + }) parser = SimpleManifestSink(finder, formatter) component0 = Component("component0") component1 = Component("component1") diff --git a/python/mozbuild/mozpack/test/test_packager_formats.py b/python/mozbuild/mozpack/test/test_packager_formats.py index 734e12d60be58..9b7ba8aa9a992 100644 --- a/python/mozbuild/mozpack/test/test_packager_formats.py +++ b/python/mozbuild/mozpack/test/test_packager_formats.py @@ -107,27 +107,23 @@ } for addon in ("addon0", "addon1", "app/chrome/addons/addon2"): - RESULT_FLAT.update( - { - mozpath.join(addon, p): f - for p, f in { - "chrome.manifest": [ - "manifest chrome/chrome.manifest", - "manifest components/components.manifest", - ], - "chrome/chrome.manifest": [ - "content %s foo/bar/" % mozpath.basename(addon) - ], - "chrome/foo/bar/baz": FILES[mozpath.join(addon, "chrome/foo/bar/baz")], - "components/components.manifest": [ - "interfaces bar.xpt", - "interfaces foo.xpt", - ], - "components/bar.xpt": bar_xpt, - "components/foo.xpt": foo2_xpt, - }.items() - } - ) + RESULT_FLAT.update({ + mozpath.join(addon, p): f + for p, f in { + "chrome.manifest": [ + "manifest chrome/chrome.manifest", + "manifest components/components.manifest", + ], + "chrome/chrome.manifest": ["content %s foo/bar/" % mozpath.basename(addon)], + "chrome/foo/bar/baz": FILES[mozpath.join(addon, "chrome/foo/bar/baz")], + "components/components.manifest": [ + "interfaces bar.xpt", + "interfaces foo.xpt", + ], + "components/bar.xpt": bar_xpt, + "components/foo.xpt": foo2_xpt, + }.items() + }) RESULT_JAR = { p: RESULT_FLAT[p] @@ -149,42 +145,40 @@ ) } -RESULT_JAR.update( - { - "chrome/f/f.manifest": [ - "content oo jar:oo.jar!/", - "content bar jar:oo.jar!/bar/", - "resource foo resource://bar/", - ], - "chrome/f/oo.jar": { - "bar/baz": FILES["chrome/f/oo/bar/baz"], - "baz": FILES["chrome/f/oo/baz"], - "qux": FILES["chrome/f/oo/qux"], - }, - "app/chrome/chrome.manifest": [ - "content content jar:foo.jar!/", - ], - "app/chrome/foo.jar": { - "foo": FILES["app/chrome/foo/foo"], - }, - "addon0/chrome/chrome.manifest": [ - "content addon0 jar:foo.jar!/bar/", - ], - "addon0/chrome/foo.jar": { - "bar/baz": FILES["addon0/chrome/foo/bar/baz"], - }, - "addon1.xpi": { - mozpath.relpath(p, "addon1"): f - for p, f in RESULT_FLAT.items() - if p.startswith("addon1/") - }, - "app/chrome/addons/addon2.xpi": { - mozpath.relpath(p, "app/chrome/addons/addon2"): f - for p, f in RESULT_FLAT.items() - if p.startswith("app/chrome/addons/addon2/") - }, - } -) +RESULT_JAR.update({ + "chrome/f/f.manifest": [ + "content oo jar:oo.jar!/", + "content bar jar:oo.jar!/bar/", + "resource foo resource://bar/", + ], + "chrome/f/oo.jar": { + "bar/baz": FILES["chrome/f/oo/bar/baz"], + "baz": FILES["chrome/f/oo/baz"], + "qux": FILES["chrome/f/oo/qux"], + }, + "app/chrome/chrome.manifest": [ + "content content jar:foo.jar!/", + ], + "app/chrome/foo.jar": { + "foo": FILES["app/chrome/foo/foo"], + }, + "addon0/chrome/chrome.manifest": [ + "content addon0 jar:foo.jar!/bar/", + ], + "addon0/chrome/foo.jar": { + "bar/baz": FILES["addon0/chrome/foo/bar/baz"], + }, + "addon1.xpi": { + mozpath.relpath(p, "addon1"): f + for p, f in RESULT_FLAT.items() + if p.startswith("addon1/") + }, + "app/chrome/addons/addon2.xpi": { + mozpath.relpath(p, "app/chrome/addons/addon2"): f + for p, f in RESULT_FLAT.items() + if p.startswith("app/chrome/addons/addon2/") + }, +}) RESULT_OMNIJAR = { p: RESULT_FLAT[p] @@ -196,55 +190,51 @@ RESULT_OMNIJAR.update({p: RESULT_JAR[p] for p in RESULT_JAR if p.startswith("addon")}) -RESULT_OMNIJAR.update( - { - "omni.foo": { - "components/components.manifest": [ - "interfaces bar.xpt", - "interfaces foo.xpt", - ], - }, - "chrome.manifest": [ - "manifest components/components.manifest", - ], +RESULT_OMNIJAR.update({ + "omni.foo": { "components/components.manifest": [ - "binary-component foo.so", + "interfaces bar.xpt", + "interfaces foo.xpt", ], - "app/omni.foo": { - p: RESULT_FLAT["app/" + p] - for p in chain( - ( - "chrome.manifest", - "chrome/chrome.manifest", - "chrome/foo/foo", - "components/components.manifest", - "components/foo.js", - ), - ( - mozpath.relpath(p, "app") - for p in RESULT_FLAT.keys() - if p.startswith("app/chrome/addons/addon2/") - ), - ) - }, - } -) - -RESULT_OMNIJAR["omni.foo"].update( - { - p: RESULT_FLAT[p] - for p in ( - "chrome.manifest", - "chrome/chrome.manifest", - "chrome/f/f.manifest", - "chrome/f/oo/bar/baz", - "chrome/f/oo/baz", - "chrome/f/oo/qux", - "components/foo.xpt", - "components/bar.xpt", + }, + "chrome.manifest": [ + "manifest components/components.manifest", + ], + "components/components.manifest": [ + "binary-component foo.so", + ], + "app/omni.foo": { + p: RESULT_FLAT["app/" + p] + for p in chain( + ( + "chrome.manifest", + "chrome/chrome.manifest", + "chrome/foo/foo", + "components/components.manifest", + "components/foo.js", + ), + ( + mozpath.relpath(p, "app") + for p in RESULT_FLAT.keys() + if p.startswith("app/chrome/addons/addon2/") + ), ) - } -) + }, +}) + +RESULT_OMNIJAR["omni.foo"].update({ + p: RESULT_FLAT[p] + for p in ( + "chrome.manifest", + "chrome/chrome.manifest", + "chrome/f/f.manifest", + "chrome/f/oo/bar/baz", + "chrome/f/oo/baz", + "chrome/f/oo/qux", + "components/foo.xpt", + "components/bar.xpt", + ) +}) RESULT_OMNIJAR_WITH_SUBPATH = { k.replace("omni.foo", "bar/omni.foo"): v for k, v in RESULT_OMNIJAR.items() @@ -453,7 +443,7 @@ def test_chrome_override(self): self.assertEqual( str(e.exception), - 'error: "content foo foo/" overrides ' '"content foo foo/unix"', + 'error: "content foo foo/" overrides "content foo foo/unix"', ) # Chrome with the same name and same flags overrides the previous @@ -475,7 +465,7 @@ def test_chrome_override(self): self.assertEqual( str(e.exception), - 'error: "content bar bar/unix" overrides ' '"content bar bar/win os=WINNT"', + 'error: "content bar bar/unix" overrides "content bar bar/win os=WINNT"', ) # Adding something more specific still works. diff --git a/python/mozbuild/mozpack/test/test_packager_l10n.py b/python/mozbuild/mozpack/test/test_packager_l10n.py index c437b09d17b84..6126bc5fa009c 100644 --- a/python/mozbuild/mozpack/test/test_packager_l10n.py +++ b/python/mozbuild/mozpack/test/test_packager_l10n.py @@ -25,41 +25,39 @@ def test_l10n_repack(self): dict_cc = GeneratedFile(b"dict_cc") barbaz = GeneratedFile(b"barbaz") lst = GeneratedFile(b"foo\nbar") - app_finder = MockFinder( - { - "bar/foo": foo, - "chrome/foo/foobar": foobar, - "chrome/qux/qux.properties": qux, - "chrome/qux/baz/baz.properties": baz, - "chrome/chrome.manifest": ManifestFile( - "chrome", - [ - ManifestContent("chrome", "foo", "foo/"), - ManifestLocale("chrome", "qux", "en-US", "qux/"), - ], - ), - "chrome.manifest": ManifestFile( - "", [Manifest("", "chrome/chrome.manifest")] - ), - "dict/aa": dict_aa, - "app/chrome/bar/barbaz.dtd": barbaz, - "app/chrome/chrome.manifest": ManifestFile( - "app/chrome", [ManifestLocale("app/chrome", "bar", "en-US", "bar/")] - ), - "app/chrome.manifest": ManifestFile( - "app", [Manifest("app", "chrome/chrome.manifest")] - ), - "app/dict/bb": dict_bb, - "app/dict/cc": dict_cc, - "app/chrome/bar/search/foo.xml": foo, - "app/chrome/bar/search/bar.xml": bar, - "app/chrome/bar/search/lst.txt": lst, - "META-INF/foo": foo, # Stripped. - "inner/META-INF/foo": foo, # Not stripped. - "app/META-INF/foo": foo, # Stripped. - "app/inner/META-INF/foo": foo, # Not stripped. - } - ) + app_finder = MockFinder({ + "bar/foo": foo, + "chrome/foo/foobar": foobar, + "chrome/qux/qux.properties": qux, + "chrome/qux/baz/baz.properties": baz, + "chrome/chrome.manifest": ManifestFile( + "chrome", + [ + ManifestContent("chrome", "foo", "foo/"), + ManifestLocale("chrome", "qux", "en-US", "qux/"), + ], + ), + "chrome.manifest": ManifestFile( + "", [Manifest("", "chrome/chrome.manifest")] + ), + "dict/aa": dict_aa, + "app/chrome/bar/barbaz.dtd": barbaz, + "app/chrome/chrome.manifest": ManifestFile( + "app/chrome", [ManifestLocale("app/chrome", "bar", "en-US", "bar/")] + ), + "app/chrome.manifest": ManifestFile( + "app", [Manifest("app", "chrome/chrome.manifest")] + ), + "app/dict/bb": dict_bb, + "app/dict/cc": dict_cc, + "app/chrome/bar/search/foo.xml": foo, + "app/chrome/bar/search/bar.xml": bar, + "app/chrome/bar/search/lst.txt": lst, + "META-INF/foo": foo, # Stripped. + "inner/META-INF/foo": foo, # Not stripped. + "app/META-INF/foo": foo, # Stripped. + "app/inner/META-INF/foo": foo, # Not stripped. + }) app_finder.jarlogs = {} app_finder.base = "app" foo_l10n = GeneratedFile(b"foo_l10n") @@ -67,35 +65,33 @@ def test_l10n_repack(self): baz_l10n = GeneratedFile(b"baz_l10n") barbaz_l10n = GeneratedFile(b"barbaz_l10n") lst_l10n = GeneratedFile(b"foo\nqux") - l10n_finder = MockFinder( - { - "chrome/qux-l10n/qux.properties": qux_l10n, - "chrome/qux-l10n/baz/baz.properties": baz_l10n, - "chrome/chrome.manifest": ManifestFile( - "chrome", - [ - ManifestLocale("chrome", "qux", "x-test", "qux-l10n/"), - ], - ), - "chrome.manifest": ManifestFile( - "", [Manifest("", "chrome/chrome.manifest")] - ), - "dict/bb": dict_bb, - "dict/cc": dict_cc, - "app/chrome/bar-l10n/barbaz.dtd": barbaz_l10n, - "app/chrome/chrome.manifest": ManifestFile( - "app/chrome", - [ManifestLocale("app/chrome", "bar", "x-test", "bar-l10n/")], - ), - "app/chrome.manifest": ManifestFile( - "app", [Manifest("app", "chrome/chrome.manifest")] - ), - "app/dict/aa": dict_aa, - "app/chrome/bar-l10n/search/foo.xml": foo_l10n, - "app/chrome/bar-l10n/search/qux.xml": qux_l10n, - "app/chrome/bar-l10n/search/lst.txt": lst_l10n, - } - ) + l10n_finder = MockFinder({ + "chrome/qux-l10n/qux.properties": qux_l10n, + "chrome/qux-l10n/baz/baz.properties": baz_l10n, + "chrome/chrome.manifest": ManifestFile( + "chrome", + [ + ManifestLocale("chrome", "qux", "x-test", "qux-l10n/"), + ], + ), + "chrome.manifest": ManifestFile( + "", [Manifest("", "chrome/chrome.manifest")] + ), + "dict/bb": dict_bb, + "dict/cc": dict_cc, + "app/chrome/bar-l10n/barbaz.dtd": barbaz_l10n, + "app/chrome/chrome.manifest": ManifestFile( + "app/chrome", + [ManifestLocale("app/chrome", "bar", "x-test", "bar-l10n/")], + ), + "app/chrome.manifest": ManifestFile( + "app", [Manifest("app", "chrome/chrome.manifest")] + ), + "app/dict/aa": dict_aa, + "app/chrome/bar-l10n/search/foo.xml": foo_l10n, + "app/chrome/bar-l10n/search/qux.xml": qux_l10n, + "app/chrome/bar-l10n/search/lst.txt": lst_l10n, + }) l10n_finder.base = "l10n" copier = FileRegistry() formatter = l10n.FlatFormatter(copier) diff --git a/python/mozbuild/mozpack/test/test_path.py b/python/mozbuild/mozpack/test/test_path.py index 6c7aeb5400dbb..06b10e75aeafd 100644 --- a/python/mozbuild/mozpack/test/test_path.py +++ b/python/mozbuild/mozpack/test/test_path.py @@ -56,9 +56,11 @@ def test_dirname(self): def test_commonprefix(self): self.assertEqual( - commonprefix( - [self.SEP.join(("foo", "bar", "baz")), "foo/qux", "foo/baz/qux"] - ), + commonprefix([ + self.SEP.join(("foo", "bar", "baz")), + "foo/qux", + "foo/baz/qux", + ]), "foo/", ) self.assertEqual( diff --git a/python/mozbuild/mozpack/test/test_unify.py b/python/mozbuild/mozpack/test/test_unify.py index a3c79ec47c5cb..5504e7c69361f 100644 --- a/python/mozbuild/mozpack/test/test_unify.py +++ b/python/mozbuild/mozpack/test/test_unify.py @@ -47,18 +47,18 @@ def test_unified_finder(self): sorted=["test"], ) self.assertEqual( - sorted( - [(f, c.open().read().decode("utf-8")) for f, c in finder.find("foo")] - ), + sorted([ + (f, c.open().read().decode("utf-8")) for f, c in finder.find("foo") + ]), [("foo/bar", "foobar"), ("foo/baz", "foobaz")], ) self.assertRaises(ErrorMessage, any, finder.find("bar")) self.assertRaises(ErrorMessage, any, finder.find("baz")) self.assertRaises(ErrorMessage, any, finder.find("qux")) self.assertEqual( - sorted( - [(f, c.open().read().decode("utf-8")) for f, c in finder.find("test")] - ), + sorted([ + (f, c.open().read().decode("utf-8")) for f, c in finder.find("test") + ]), [("test/bar", "a\nb\nc\n"), ("test/foo", "a\nb\nc\n")], ) @@ -74,12 +74,10 @@ def test_unified_build_finder(self): self.create_one("a", "chrome/chrome.manifest", "a\nb\nc\n") self.create_one("b", "chrome/chrome.manifest", "b\nc\na\n") self.assertEqual( - sorted( - [ - (f, c.open().read().decode("utf-8")) - for f, c in finder.find("**/chrome.manifest") - ] - ), + sorted([ + (f, c.open().read().decode("utf-8")) + for f, c in finder.find("**/chrome.manifest") + ]), [("chrome.manifest", "a\nb\nc\n"), ("chrome/chrome.manifest", "a\nb\nc\n")], ) @@ -87,59 +85,51 @@ def test_unified_build_finder(self): self.create_one( "a", "chrome/browser/foo/buildconfig.html", - "\n".join( - [ - "", - " ", - "
", - "

Build Configuration

", - "
foo
", - "
", - " ", - "", - ] - ), + "\n".join([ + "", + " ", + "
", + "

Build Configuration

", + "
foo
", + "
", + " ", + "", + ]), ) self.create_one( "b", "chrome/browser/foo/buildconfig.html", - "\n".join( - [ - "", - " ", - "
", - "

Build Configuration

", - "
bar
", - "
", - " ", - "", - ] - ), + "\n".join([ + "", + " ", + "
", + "

Build Configuration

", + "
bar
", + "
", + " ", + "", + ]), ) self.assertEqual( - sorted( - [ - (f, c.open().read().decode("utf-8")) - for f, c in finder.find("**/buildconfig.html") - ] - ), + sorted([ + (f, c.open().read().decode("utf-8")) + for f, c in finder.find("**/buildconfig.html") + ]), [ ( "chrome/browser/foo/buildconfig.html", - "\n".join( - [ - "", - " ", - "
", - "

Build Configuration

", - "
foo
", - "
", - "
bar
", - "
", - " ", - "", - ] - ), + "\n".join([ + "", + " ", + "
", + "

Build Configuration

", + "
foo
", + "
", + "
bar
", + "
", + " ", + "", + ]), ) ], ) @@ -171,23 +161,19 @@ def test_unified_build_finder(self): target_tag = "<{em}targetPlatform>{platform}" target_attr = '{em}targetPlatform="{platform}" ' - rdf_tag = "".join( - [ - '<{RDF}Description {em}bar="bar" {em}qux="qux">', - "<{em}foo>foo", - "{targets}", - "<{em}baz>baz", - "", - ] - ) - rdf_attr = "".join( - [ - '<{RDF}Description {em}bar="bar" {attr}{em}qux="qux">', - "{targets}", - "<{em}foo>foo<{em}baz>baz", - "", - ] - ) + rdf_tag = "".join([ + '<{RDF}Description {em}bar="bar" {em}qux="qux">', + "<{em}foo>foo", + "{targets}", + "<{em}baz>baz", + "", + ]) + rdf_attr = "".join([ + '<{RDF}Description {em}bar="bar" {attr}{em}qux="qux">', + "{targets}", + "<{em}foo>foo<{em}baz>baz", + "", + ]) for descr_ns, target_ns in (("RDF:", ""), ("", "em:"), ("RDF:", "em:")): # First we need to infuse the above strings with our namespaces and @@ -236,12 +222,10 @@ def test_unified_build_finder(self): results.append((filename, result)) self.assertEqual( - sorted( - [ - (f, c.open().read().decode("utf-8")) - for f, c in finder.find("**/install.rdf") - ] - ), + sorted([ + (f, c.open().read().decode("utf-8")) + for f, c in finder.find("**/install.rdf") + ]), results, ) diff --git a/python/mozlint/mozlint/errors.py b/python/mozlint/mozlint/errors.py index cb4ed587140a7..7f597b7d66f44 100644 --- a/python/mozlint/mozlint/errors.py +++ b/python/mozlint/mozlint/errors.py @@ -29,5 +29,5 @@ class LintersNotConfigured(LintException): def __init__(self): LintException.__init__( self, - "No linters registered! Use `LintRoller.read` " "to register a linter.", + "No linters registered! Use `LintRoller.read` to register a linter.", ) diff --git a/python/mozlint/mozlint/formatters/stylish.py b/python/mozlint/mozlint/formatters/stylish.py index c2ebce9804ba2..23fb4b8625bfb 100644 --- a/python/mozlint/mozlint/formatters/stylish.py +++ b/python/mozlint/mozlint/formatters/stylish.py @@ -29,9 +29,7 @@ class StylishFormatter: fmt = """ {c1}{lineno}{column} {c2}{level}{normal} {message} {c1}{rule}({linter}){source}{normal} -{diff}""".lstrip( - "\n" - ) +{diff}""".lstrip("\n") fmt_summary = ( "{t.bold}{c}\u2716 {problem} ({error}, {warning}{failure}, {fixed}){t.normal}" ) diff --git a/python/mozlint/mozlint/formatters/summary.py b/python/mozlint/mozlint/formatters/summary.py index c71d6105467b1..fb28ba4a137c2 100644 --- a/python/mozlint/mozlint/formatters/summary.py +++ b/python/mozlint/mozlint/formatters/summary.py @@ -33,12 +33,12 @@ def __call__(self, result): ] abspath = mozpath.join(commonprefix, *parts) - summary[abspath][0] += len( - [r for r in result.issues[path] if r.level == "error"] - ) - summary[abspath][1] += len( - [r for r in result.issues[path] if r.level == "warning"] - ) + summary[abspath][0] += len([ + r for r in result.issues[path] if r.level == "error" + ]) + summary[abspath][1] += len([ + r for r in result.issues[path] if r.level == "warning" + ]) summary[abspath][1] += result.suppressed_warnings[path] msg = [] diff --git a/python/mozlint/mozlint/parser.py b/python/mozlint/mozlint/parser.py index b215440374d50..49ebcc7802c5c 100644 --- a/python/mozlint/mozlint/parser.py +++ b/python/mozlint/mozlint/parser.py @@ -39,7 +39,7 @@ def _validate(self, linter): if missing_attrs: raise LinterParseError( relpath, - "Missing required attribute(s): " "{}".format(",".join(missing_attrs)), + "Missing required attribute(s): {}".format(",".join(missing_attrs)), ) if linter["type"] not in supported_types: @@ -54,7 +54,7 @@ def _validate(self, linter): ): raise LinterParseError( relpath, - f"The {attr} directive must be a " "list of strings!", + f"The {attr} directive must be a list of strings!", ) invalid_paths = set() for path in linter[attr]: diff --git a/python/mozlint/mozlint/pathutils.py b/python/mozlint/mozlint/pathutils.py index 86cc002eed573..b6ac22d177591 100644 --- a/python/mozlint/mozlint/pathutils.py +++ b/python/mozlint/mozlint/pathutils.py @@ -196,7 +196,6 @@ def normalize(path): # First handle include/exclude directives # that exist (i.e don't have globs) for inc in include: - # If the include directive is a file and we're specifically linting # it, keep it. if inc.isfile and path.path == inc.path: diff --git a/python/mozlint/mozlint/roller.py b/python/mozlint/mozlint/roller.py index a7ffb0febfa5e..aeafa93cc525d 100644 --- a/python/mozlint/mozlint/roller.py +++ b/python/mozlint/mozlint/roller.py @@ -399,12 +399,9 @@ def roll(self, paths=None, outgoing=None, workdir=None, rev=None, num_procs=None # Make sure all paths are absolute. Join `paths` to cwd and `vcs_paths` to root. paths = set(map(os.path.abspath, paths)) - vcs_paths = set( - [ - os.path.join(self.root, p) if not os.path.isabs(p) else p - for p in vcs_paths - ] - ) + vcs_paths = set([ + os.path.join(self.root, p) if not os.path.isabs(p) else p for p in vcs_paths + ]) num_procs = num_procs or cpu_count() jobs = list(self._generate_jobs(paths, vcs_paths, num_procs)) diff --git a/python/mozlint/test/test_cli.py b/python/mozlint/test/test_cli.py index 13abaf441f927..8a2253468d4b7 100644 --- a/python/mozlint/test/test_cli.py +++ b/python/mozlint/test/test_cli.py @@ -156,15 +156,13 @@ def test_cli_run_with_stdin_filename(run, filedir, capfd, monkeypatch, tmp_path) monkeypatch.setattr("sys.stdin", io.TextIOWrapper(io.BytesIO(b"foobar\n"))) tmpfile = tmp_path / "temp" - run( - [ - "-l", - "string", - f"--stdin-filename={filedir}/foobar.py", - "--dump-stdin-file", - str(tmpfile), - ] - ) + run([ + "-l", + "string", + f"--stdin-filename={filedir}/foobar.py", + "--dump-stdin-file", + str(tmpfile), + ]) out, err = capfd.readouterr() assert out == "" assert tmpfile.read_text() == "foobar\n" diff --git a/python/mozlint/test/test_editor.py b/python/mozlint/test/test_editor.py index 7a15a613a6efc..875e7e85ee406 100644 --- a/python/mozlint/test/test_editor.py +++ b/python/mozlint/test/test_editor.py @@ -28,23 +28,21 @@ def fake_subprocess_call(*args, **kwargs): @pytest.fixture def result(): result = ResultSummary("/fake/root") - result.issues["foo.py"].extend( - [ - Issue( - linter="no-foobar", - path="foo.py", - lineno=1, - message="Oh no!", - ), - Issue( - linter="no-foobar", - path="foo.py", - lineno=3, - column=10, - message="To Yuma!", - ), - ] - ) + result.issues["foo.py"].extend([ + Issue( + linter="no-foobar", + path="foo.py", + lineno=1, + message="Oh no!", + ), + Issue( + linter="no-foobar", + path="foo.py", + lineno=3, + column=10, + message="To Yuma!", + ), + ]) return result diff --git a/python/mozlint/test/test_formatters.py b/python/mozlint/test/test_formatters.py index feded05e82153..b1c3521ef3fa0 100644 --- a/python/mozlint/test/test_formatters.py +++ b/python/mozlint/test/test_formatters.py @@ -67,18 +67,14 @@ {abc}:4:10: baz error: oh no baz {abc}:5: foo-diff error: oh no foo-diff {def}:4:2: bar-not-allowed warning: oh no bar -""".format( - **NORMALISED_PATHS - ).strip(), +""".format(**NORMALISED_PATHS).strip(), }, "summary": { "kwargs": {}, "format": """ {root}/a: 3 errors {root}/d: 0 errors, 1 warning -""".format( - **NORMALISED_PATHS - ).strip(), +""".format(**NORMALISED_PATHS).strip(), }, } diff --git a/python/mozperftest/mozperftest/environment.py b/python/mozperftest/mozperftest/environment.py index 7be0f1f639fdd..35273af7af1f5 100644 --- a/python/mozperftest/mozperftest/environment.py +++ b/python/mozperftest/mozperftest/environment.py @@ -19,9 +19,9 @@ class MachEnvironment(MachLogger): def __init__(self, mach_cmd, flavor="desktop-browser", hooks=None, **kwargs): MachLogger.__init__(self, mach_cmd) self._mach_cmd = mach_cmd - self._mach_args = dict( - [(self._normalize(key), value) for key, value in kwargs.items()] - ) + self._mach_args = dict([ + (self._normalize(key), value) for key, value in kwargs.items() + ]) self.layers = [] if flavor not in FLAVORS: raise NotImplementedError(flavor) diff --git a/python/mozperftest/mozperftest/metrics/common.py b/python/mozperftest/mozperftest/metrics/common.py index 1b510e5c94ec8..780acd8cae0f7 100644 --- a/python/mozperftest/mozperftest/metrics/common.py +++ b/python/mozperftest/mozperftest/metrics/common.py @@ -245,9 +245,9 @@ def filtered_metrics( for data_type, data_info in results.items(): newresults = [] for res in data_info: - if any([met["name"] in res["subtest"] for met in metrics]) and not any( - [met in res["subtest"] for met in exclude] - ): + if any([met["name"] in res["subtest"] for met in metrics]) and not any([ + met in res["subtest"] for met in exclude + ]): res["transformer"] = self.ptnb_config[data_type][ "custom_transformer" ] diff --git a/python/mozperftest/mozperftest/metrics/notebookupload.py b/python/mozperftest/mozperftest/metrics/notebookupload.py index ec53af2b7fdce..e8730439e31cc 100644 --- a/python/mozperftest/mozperftest/metrics/notebookupload.py +++ b/python/mozperftest/mozperftest/metrics/notebookupload.py @@ -16,42 +16,40 @@ class Notebook(Layer): activated = False arguments = COMMON_ARGS - arguments.update( - { - "analysis": { - "nargs": "*", - "default": [], - "help": "List of analyses to run in Iodide.", - }, - "analyze-strings": { - "action": "store_true", - "default": False, - "help": ( - "If set, strings won't be filtered out of the results to analyze in Iodide." - ), - }, - "no-server": { - "action": "store_true", - "default": False, - "help": "If set, the data won't be opened in Iodide.", - }, - "compare-to": { - "nargs": "*", - "default": [], - "help": ( - "Compare the results from this test to the historical data in the folder(s) " - "specified through this option. Only JSON data can be processed for the " - "moment. Each folder containing those JSONs is considered as a distinct " - "data point to compare with the newest run." - ), - }, - "stats": { - "action": "store_true", - "default": False, - "help": "If set, browsertime statistics will be reported.", - }, - } - ) + arguments.update({ + "analysis": { + "nargs": "*", + "default": [], + "help": "List of analyses to run in Iodide.", + }, + "analyze-strings": { + "action": "store_true", + "default": False, + "help": ( + "If set, strings won't be filtered out of the results to analyze in Iodide." + ), + }, + "no-server": { + "action": "store_true", + "default": False, + "help": "If set, the data won't be opened in Iodide.", + }, + "compare-to": { + "nargs": "*", + "default": [], + "help": ( + "Compare the results from this test to the historical data in the folder(s) " + "specified through this option. Only JSON data can be processed for the " + "moment. Each folder containing those JSONs is considered as a distinct " + "data point to compare with the newest run." + ), + }, + "stats": { + "action": "store_true", + "default": False, + "help": "If set, browsertime statistics will be reported.", + }, + }) def run(self, metadata): exclusions = None @@ -73,12 +71,10 @@ def run(self, metadata): raise Exception(f"{dirpath} is not a directory") # TODO: Handle more than just JSON data. for jsonfile in dirpath.rglob("*.json"): - metadata.add_result( - { - "results": str(jsonfile.resolve()), - "name": jsonfile.parent.name, - } - ) + metadata.add_result({ + "results": str(jsonfile.resolve()), + "name": jsonfile.parent.name, + }) results = filtered_metrics( metadata, diff --git a/python/mozperftest/mozperftest/metrics/perfboard/grafana.py b/python/mozperftest/mozperftest/metrics/perfboard/grafana.py index 1fa76ea991ff9..f738ff859637f 100644 --- a/python/mozperftest/mozperftest/metrics/perfboard/grafana.py +++ b/python/mozperftest/mozperftest/metrics/perfboard/grafana.py @@ -26,9 +26,9 @@ def __init__(self, layer, key, host="perfboard.dev.mozaws.net", port=3000): def get_dashboard(self, title): existing = self.client.search.search_dashboards(tag="component") - existing = dict( - [(dashboard["title"].lower(), dashboard["uid"]) for dashboard in existing] - ) + existing = dict([ + (dashboard["title"].lower(), dashboard["uid"]) for dashboard in existing + ]) if title in existing: return self.client.dashboard.get_dashboard(existing[title]) self.layer.debug(f"Creating dashboard {title}") diff --git a/python/mozperftest/mozperftest/metrics/perfboard/influx.py b/python/mozperftest/mozperftest/metrics/perfboard/influx.py index 4f7e27072c26b..7010766032f5b 100644 --- a/python/mozperftest/mozperftest/metrics/perfboard/influx.py +++ b/python/mozperftest/mozperftest/metrics/perfboard/influx.py @@ -17,49 +17,47 @@ class Influx(Layer): name = "perfboard" activated = False arguments = COMMON_ARGS - arguments.update( - { - "dashboard": { - "type": str, - "default": None, - "help": "Name of the dashboard - defaults to the script" - " `component` metadata. When not set, falls back to" - " `perftest`", - }, - "influx-host": { - "type": str, - "default": "perfboard.dev.mozaws.net", - }, - "influx-user": { - "type": str, - "default": "admin", - }, - "influx-port": { - "type": int, - "default": 8086, - }, - "influx-password": { - "type": str, - "default": None, - }, - "influx-db": { - "type": str, - "default": "perf", - }, - "grafana-host": { - "type": str, - "default": "perfboard.dev.mozaws.net", - }, - "grafana-key": { - "type": str, - "default": None, - }, - "grafana-port": { - "type": int, - "default": 3000, - }, - } - ) + arguments.update({ + "dashboard": { + "type": str, + "default": None, + "help": "Name of the dashboard - defaults to the script" + " `component` metadata. When not set, falls back to" + " `perftest`", + }, + "influx-host": { + "type": str, + "default": "perfboard.dev.mozaws.net", + }, + "influx-user": { + "type": str, + "default": "admin", + }, + "influx-port": { + "type": int, + "default": 8086, + }, + "influx-password": { + "type": str, + "default": None, + }, + "influx-db": { + "type": str, + "default": "perf", + }, + "grafana-host": { + "type": str, + "default": "perfboard.dev.mozaws.net", + }, + "grafana-key": { + "type": str, + "default": None, + }, + "grafana-port": { + "type": int, + "default": 3000, + }, + }) def _setup(self): venv = self.mach_cmd.virtualenv_manager @@ -157,9 +155,9 @@ def run(self, metadata): metric_name = line["subtest"] short_name = metric_name.split(".")[-1] short_name = short_name.lower() - if metrics and not any( - [m.lower().startswith(short_name.lower()) for m in metrics] - ): + if metrics and not any([ + m.lower().startswith(short_name.lower()) for m in metrics + ]): continue values = [v["value"] for v in line["data"]] data[short_name].extend(values) diff --git a/python/mozperftest/mozperftest/metrics/perfherder.py b/python/mozperftest/mozperftest/metrics/perfherder.py index 1d445e26e9dcf..89391ff56732c 100644 --- a/python/mozperftest/mozperftest/metrics/perfherder.py +++ b/python/mozperftest/mozperftest/metrics/perfherder.py @@ -31,23 +31,21 @@ class Perfherder(Layer): activated = False arguments = COMMON_ARGS - arguments.update( - { - "stats": { - "action": "store_true", - "default": False, - "help": "If set, browsertime statistics will be reported.", - }, - "timestamp": { - "type": float, - "default": None, - "help": ( - "Timestamp to use for the perfherder data. Can be the " - "current date or a past date if needed." - ), - }, - } - ) + arguments.update({ + "stats": { + "action": "store_true", + "default": False, + "help": "If set, browsertime statistics will be reported.", + }, + "timestamp": { + "type": float, + "default": None, + "help": ( + "Timestamp to use for the perfherder data. Can be the " + "current date or a past date if needed." + ), + }, + }) def run(self, metadata): """Processes the given results into a perfherder-formatted data blob. diff --git a/python/mozperftest/mozperftest/metrics/utils.py b/python/mozperftest/mozperftest/metrics/utils.py index a947434684567..2e09b2b1292c8 100644 --- a/python/mozperftest/mozperftest/metrics/utils.py +++ b/python/mozperftest/mozperftest/metrics/utils.py @@ -19,9 +19,14 @@ # These are the properties we know about in the schema. # If anything other than these is present, then we will # fail validation. -KNOWN_PERFHERDER_PROPS = set( - ["name", "value", "unit", "lowerIsBetter", "shouldAlert", "alertThreshold"] -) +KNOWN_PERFHERDER_PROPS = set([ + "name", + "value", + "unit", + "lowerIsBetter", + "shouldAlert", + "alertThreshold", +]) KNOWN_SUITE_PROPS = set( set(["results", "transformer", "transformer-options", "extraOptions", "framework"]) | KNOWN_PERFHERDER_PROPS @@ -125,7 +130,7 @@ def _check(field): raise ValueError(f"Unexpected metrics definition {field}") if sfield[0] not in KNOWN_SUITE_PROPS: raise ValueError( - f"Unknown field '{sfield[0]}', should be in " f"{KNOWN_SUITE_PROPS}" + f"Unknown field '{sfield[0]}', should be in {KNOWN_SUITE_PROPS}" ) sfield = [sfield[0], sfield[2]] diff --git a/python/mozperftest/mozperftest/metrics/visualmetrics.py b/python/mozperftest/mozperftest/metrics/visualmetrics.py index 1003834b9e998..036048ce3bc5c 100644 --- a/python/mozperftest/mozperftest/metrics/visualmetrics.py +++ b/python/mozperftest/mozperftest/metrics/visualmetrics.py @@ -87,14 +87,12 @@ def run(self, metadata): self.info(f"Treated {treated} videos.") if len(self.metrics) > 0: - metadata.add_result( - { - "name": metadata.script["name"] + "-vm", - "framework": {"name": "mozperftest"}, - "transformer": "mozperftest.metrics.visualmetrics:VisualData", - "results": list(self.metrics.values()), - } - ) + metadata.add_result({ + "name": metadata.script["name"] + "-vm", + "framework": {"name": "mozperftest"}, + "transformer": "mozperftest.metrics.visualmetrics:VisualData", + "results": list(self.metrics.values()), + }) # we also extend --perfherder-metrics and --console-metrics if they # are activated diff --git a/python/mozperftest/mozperftest/runner.py b/python/mozperftest/mozperftest/runner.py index 86ee812605435..1830b05fc5743 100644 --- a/python/mozperftest/mozperftest/runner.py +++ b/python/mozperftest/mozperftest/runner.py @@ -14,6 +14,7 @@ When the module is executed directly, if the --on-try option is used, it will fetch arguments from Tascluster's parameters. """ + import json import logging import os diff --git a/python/mozperftest/mozperftest/system/geckoprofiler.py b/python/mozperftest/mozperftest/system/geckoprofiler.py index 0ae545c8e41dd..64c95f02d4e00 100644 --- a/python/mozperftest/mozperftest/system/geckoprofiler.py +++ b/python/mozperftest/mozperftest/system/geckoprofiler.py @@ -80,9 +80,9 @@ def start(self, geckoprofiler_opts=None): config_content = f"""env: MOZ_PROFILER_STARTUP: 1 - MOZ_PROFILER_STARTUP_INTERVAL: {geckoprofiler_opts['interval']} - MOZ_PROFILER_STARTUP_FEATURES: {geckoprofiler_opts['features']} - MOZ_PROFILER_STARTUP_FILTERS: {geckoprofiler_opts['filters']} + MOZ_PROFILER_STARTUP_INTERVAL: {geckoprofiler_opts["interval"]} + MOZ_PROFILER_STARTUP_FEATURES: {geckoprofiler_opts["features"]} + MOZ_PROFILER_STARTUP_FILTERS: {geckoprofiler_opts["filters"]} """.encode() with tempfile.NamedTemporaryFile(delete=False) as config_file: diff --git a/python/mozperftest/mozperftest/system/proxy.py b/python/mozperftest/mozperftest/system/proxy.py index f9cfa2ac2cf10..3938a93e4a729 100644 --- a/python/mozperftest/mozperftest/system/proxy.py +++ b/python/mozperftest/mozperftest/system/proxy.py @@ -160,9 +160,9 @@ def run(self, metadata): if metadata.flavor == "mobile-browser": command.extend(["--tool=%s" % "mitmproxy-android"]) command.extend(["--binary=android"]) - command.extend( - [f"--app={get_pretty_app_name(self.get_arg('android-app-name'))}"] - ) + command.extend([ + f"--app={get_pretty_app_name(self.get_arg('android-app-name'))}" + ]) else: command.extend(["--tool=%s" % "mitmproxy"]) # XXX See bug 1712337, we need a single point where we can get the binary used from diff --git a/python/mozperftest/mozperftest/test/alert.py b/python/mozperftest/mozperftest/test/alert.py index e6cb079707274..2d13eb132af4f 100644 --- a/python/mozperftest/mozperftest/test/alert.py +++ b/python/mozperftest/mozperftest/test/alert.py @@ -462,12 +462,10 @@ def run(self, metadata): text=False, ) if test not in self.perfherder_data: - failed_commands.append( - { - "cmd": cmd, - "test": test, - } - ) + failed_commands.append({ + "cmd": cmd, + "test": test, + }) # Output results in a more readable manner for test, perfherder_data in self.perfherder_data.items(): diff --git a/python/mozperftest/mozperftest/test/androidlog.py b/python/mozperftest/mozperftest/test/androidlog.py index 88bf01f2feae9..95d480326a952 100644 --- a/python/mozperftest/mozperftest/test/androidlog.py +++ b/python/mozperftest/mozperftest/test/androidlog.py @@ -50,13 +50,11 @@ def __call__(self, metadata): "transform-subtest-name": self.get_arg("subtest-name"), } - metadata.add_result( - { - "results": str(self._get_logcat()), - "transformer": "LogCatTimeTransformer", - "transformer-options": options, - "name": "LogCat", - } - ) + metadata.add_result({ + "results": str(self._get_logcat()), + "transformer": "LogCatTimeTransformer", + "transformer-options": options, + "name": "LogCat", + }) return metadata diff --git a/python/mozperftest/mozperftest/test/browsertime/runner.py b/python/mozperftest/mozperftest/test/browsertime/runner.py index 4600807350b7a..da2a12cf4b4d1 100644 --- a/python/mozperftest/mozperftest/test/browsertime/runner.py +++ b/python/mozperftest/mozperftest/test/browsertime/runner.py @@ -353,9 +353,10 @@ def run(self, metadata): existing = self.get_arg("browsertime-existing-results") if existing: - metadata.add_result( - {"results": existing, "name": self._test_script["name"]} - ) + metadata.add_result({ + "results": existing, + "name": self._test_script["name"], + }) return metadata cycles = self.get_arg("cycles", 1) @@ -464,8 +465,9 @@ def _one_cycle(self, metadata, result_dir): if exit_code != 0: raise NodeException(exit_code) - metadata.add_result( - {"results": str(result_dir), "name": self._test_script["name"]} - ) + metadata.add_result({ + "results": str(result_dir), + "name": self._test_script["name"], + }) return metadata diff --git a/python/mozperftest/mozperftest/test/browsertime/visualtools.py b/python/mozperftest/mozperftest/test/browsertime/visualtools.py index c46175760a266..ddaf8dff7d2dc 100644 --- a/python/mozperftest/mozperftest/test/browsertime/visualtools.py +++ b/python/mozperftest/mozperftest/test/browsertime/visualtools.py @@ -1,8 +1,8 @@ # 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/. -""" Collects visualmetrics dependencies. -""" +"""Collects visualmetrics dependencies.""" + import contextlib import os import subprocess diff --git a/python/mozperftest/mozperftest/test/mochitest.py b/python/mozperftest/mozperftest/test/mochitest.py index 5d72ee6e64cf7..1fc977cc91e73 100644 --- a/python/mozperftest/mozperftest/test/mochitest.py +++ b/python/mozperftest/mozperftest/test/mochitest.py @@ -138,13 +138,11 @@ def _enable_gecko_profiling(self): if self.get_arg("gecko-profile"): gecko_profile_args.append("--profiler") - gecko_profile_args.extend( - [ - f"--setenv=MOZ_PROFILER_STARTUP_FEATURES={gecko_profile_features}", - f"--setenv=MOZ_PROFILER_STARTUP_FILTERS={gecko_profile_threads}", - f"--setenv=MOZ_PROFILER_STARTUP_ENTRIES={gecko_profile_entries}", - ] - ) + gecko_profile_args.extend([ + f"--setenv=MOZ_PROFILER_STARTUP_FEATURES={gecko_profile_features}", + f"--setenv=MOZ_PROFILER_STARTUP_FILTERS={gecko_profile_threads}", + f"--setenv=MOZ_PROFILER_STARTUP_ENTRIES={gecko_profile_entries}", + ]) if gecko_profile_interval: gecko_profile_args.append( f"--setenv=MOZ_PROFILER_STARTUP_INTERVAL={gecko_profile_interval}" @@ -192,23 +190,19 @@ def _setup_mochitest_android_args(self, metadata): if not ON_TRY: os.environ["MOZ_HOST_BIN"] = self.mach_cmd.bindir - mochitest_android_args.extend( - [ - f"--setenv=MOZ_HOST_BIN={os.environ['MOZ_HOST_BIN']}", - ] - ) + mochitest_android_args.extend([ + f"--setenv=MOZ_HOST_BIN={os.environ['MOZ_HOST_BIN']}", + ]) else: os.environ["MOZ_HOST_BIN"] = str( Path(os.getenv("MOZ_FETCHES_DIR"), "hostutils") ) - mochitest_android_args.extend( - [ - f"--setenv=MOZ_HOST_BIN={os.environ['MOZ_HOST_BIN']}", - f"--remote-webserver={os.environ['HOST_IP']}", - "--http-port=8854", - "--ssl-port=4454", - ] - ) + mochitest_android_args.extend([ + f"--setenv=MOZ_HOST_BIN={os.environ['MOZ_HOST_BIN']}", + f"--remote-webserver={os.environ['HOST_IP']}", + "--http-port=8854", + "--ssl-port=4454", + ]) return mochitest_android_args @@ -240,8 +234,7 @@ def remote_run(self, test, metadata): ) if not manifest_flavor: raise MissingMochitestInformation( - "Mochitest flavor needs to be provided" - "(e.g. plain, browser-chrome, ...)" + "Mochitest flavor needs to be provided(e.g. plain, browser-chrome, ...)" ) manifest_path = Path(test.parent, manifest_name) @@ -307,7 +300,6 @@ def run(self, metadata): results = [] cycles = self.get_arg("cycles", 1) for cycle in range(1, cycles + 1): - metadata.run_hook( "before_cycle", metadata, self.env, cycle, metadata.script ) @@ -358,13 +350,11 @@ def run(self, metadata): if len(results) == 0: raise NoPerfMetricsError("mochitest") - metadata.add_result( - { - "name": test_name, - "framework": {"name": "mozperftest"}, - "transformer": "mozperftest.test.mochitest:MochitestData", - "results": results, - } - ) + metadata.add_result({ + "name": test_name, + "framework": {"name": "mozperftest"}, + "transformer": "mozperftest.test.mochitest:MochitestData", + "results": results, + }) return metadata diff --git a/python/mozperftest/mozperftest/test/shellscript.py b/python/mozperftest/mozperftest/test/shellscript.py index 8d2aadca4d614..314bc53b8ded9 100644 --- a/python/mozperftest/mozperftest/test/shellscript.py +++ b/python/mozperftest/mozperftest/test/shellscript.py @@ -200,14 +200,12 @@ def output_timeout_handler(proc): shutil.copytree(testing_dir, output_dir) self.env.set_arg("output", output_dir) - metadata.add_result( - { - "name": test["name"], - "framework": {"name": "mozperftest"}, - "transformer": "mozperftest.test.shellscript:ShellScriptData", - "shouldAlert": True, - "results": self.parse_metrics(), - } - ) + metadata.add_result({ + "name": test["name"], + "framework": {"name": "mozperftest"}, + "transformer": "mozperftest.test.shellscript:ShellScriptData", + "shouldAlert": True, + "results": self.parse_metrics(), + }) return metadata diff --git a/python/mozperftest/mozperftest/test/webpagetest.py b/python/mozperftest/mozperftest/test/webpagetest.py index 77a9119d1e90b..1b1a6f25b5cb5 100644 --- a/python/mozperftest/mozperftest/test/webpagetest.py +++ b/python/mozperftest/mozperftest/test/webpagetest.py @@ -358,22 +358,20 @@ def confirm_correct_browser_and_location(self, data, options): def add_wpt_run_to_metadata(self, wbt_run, metadata, website): requested_values = self.extract_desired_values_from_wpt_run(wbt_run) if requested_values is not None: - metadata.add_result( - { - "name": ("WebPageTest:" + re.match(r"(^.\w+)", website)[0]), - "framework": {"name": "mozperftest"}, - "transformer": "mozperftest.test.webpagetest:WebPageTestData", - "shouldAlert": True, - "results": [ - { - "values": [metric_value], - "name": metric_name, - "shouldAlert": True, - } - for metric_name, metric_value in requested_values.items() - ], - } - ) + metadata.add_result({ + "name": ("WebPageTest:" + re.match(r"(^.\w+)", website)[0]), + "framework": {"name": "mozperftest"}, + "transformer": "mozperftest.test.webpagetest:WebPageTestData", + "shouldAlert": True, + "results": [ + { + "values": [metric_value], + "name": metric_name, + "shouldAlert": True, + } + for metric_name, metric_value in requested_values.items() + ], + }) def extract_desired_values_from_wpt_run(self, wpt_run): view_types = ["firstView"] diff --git a/python/mozperftest/mozperftest/test/xpcshell.py b/python/mozperftest/mozperftest/test/xpcshell.py index bbcdad1ce44aa..0dc014bb888b9 100644 --- a/python/mozperftest/mozperftest/test/xpcshell.py +++ b/python/mozperftest/mozperftest/test/xpcshell.py @@ -166,17 +166,15 @@ def run(self, metadata): if len(results.items()) == 0: raise NoPerfMetricsError("xpcshell") - metadata.add_result( - { - "name": test.name, - "framework": {"name": "mozperftest"}, - "transformer": "mozperftest.test.xpcshell:XPCShellData", - "results": [ - {"values": measures, "name": subtest} - for subtest, measures in results.items() - ], - } - ) + metadata.add_result({ + "name": test.name, + "framework": {"name": "mozperftest"}, + "transformer": "mozperftest.test.xpcshell:XPCShellData", + "results": [ + {"values": measures, "name": subtest} + for subtest, measures in results.items() + ], + }) return metadata diff --git a/python/mozperftest/mozperftest/tests/test_alert.py b/python/mozperftest/mozperftest/tests/test_alert.py index fbf2a323cf2d2..c6caf2eed38de 100644 --- a/python/mozperftest/mozperftest/tests/test_alert.py +++ b/python/mozperftest/mozperftest/tests/test_alert.py @@ -45,9 +45,7 @@ def test_alert_basic_command(alert_json, expected_command): "mozperftest.test.alert.requests.get" ) as mocked_request, mock.patch( "mozperftest.test.alert.mozprocess" - ) as mocked_mozprocess, ( - MOCK_DATA_DIR / alert_json - ).open() as f: + ) as mocked_mozprocess, (MOCK_DATA_DIR / alert_json).open() as f: mocked_response = mock.MagicMock() mocked_response.configure_mock(status_code=200) mocked_response.json.return_value = json.load(f) @@ -94,9 +92,7 @@ def test_alert_basic_command_test_specification(alert_json, tests, expected_comm "mozperftest.test.alert.requests.get" ) as mocked_request, mock.patch( "mozperftest.test.alert.mozprocess" - ) as mocked_mozprocess, ( - MOCK_DATA_DIR / alert_json - ).open() as f: + ) as mocked_mozprocess, (MOCK_DATA_DIR / alert_json).open() as f: mocked_response = mock.MagicMock() mocked_response.configure_mock(status_code=200) mocked_response.json.return_value = json.load(f) @@ -298,9 +294,7 @@ def test_alert_exact_command( "mozperftest.test.alert.requests.get" ) as mocked_request, mock.patch( "mozperftest.test.alert.mozprocess" - ) as mocked_mozprocess, ( - MOCK_DATA_DIR / alert_json - ).open() as alert_file, ( + ) as mocked_mozprocess, (MOCK_DATA_DIR / alert_json).open() as alert_file, ( MOCK_DATA_DIR / task_info_json ).open() as task_file: mocked_alert_response = mock.MagicMock() @@ -337,9 +331,7 @@ def test_alert_basic_command_failed(): "mozperftest.test.alert.requests.get" ) as mocked_request, mock.patch( "mozperftest.test.alert.mozprocess" - ) as mocked_mozprocess, ( - MOCK_DATA_DIR / alert_json - ).open() as f: + ) as mocked_mozprocess, (MOCK_DATA_DIR / alert_json).open() as f: mocked_response = mock.MagicMock() mocked_response.configure_mock(status_code=200) mocked_response.json.return_value = json.load(f) diff --git a/python/mozperftest/mozperftest/tests/test_argparser.py b/python/mozperftest/mozperftest/tests/test_argparser.py index 35051772b1f4b..831b0c2c9d7aa 100644 --- a/python/mozperftest/mozperftest/tests/test_argparser.py +++ b/python/mozperftest/mozperftest/tests/test_argparser.py @@ -31,18 +31,20 @@ def test_argparser_defaults(): def test_options(): assert Options.args["--proxy"]["help"] == "Activates the proxy layer" assert Options.args["--no-browsertime"]["help"] == ( - "Deactivates the " "browsertime layer" + "Deactivates the browsertime layer" ) def test_layer_option(): parser = PerftestArgumentParser() - assert parser.parse_args(["--notebook-metrics"]) == parser.parse_args( - ["--notebook-metrics", "--notebook"] - ) - assert parser.parse_known_args(["--notebook-metrics"]) == parser.parse_known_args( - ["--notebook-metrics", "--notebook"] - ) + assert parser.parse_args(["--notebook-metrics"]) == parser.parse_args([ + "--notebook-metrics", + "--notebook", + ]) + assert parser.parse_known_args(["--notebook-metrics"]) == parser.parse_known_args([ + "--notebook-metrics", + "--notebook", + ]) def test_bad_test_date(): diff --git a/python/mozperftest/mozperftest/tests/test_browsertime.py b/python/mozperftest/mozperftest/tests/test_browsertime.py index 6345d116ca5af..d094fedc56852 100644 --- a/python/mozperftest/mozperftest/tests/test_browsertime.py +++ b/python/mozperftest/mozperftest/tests/test_browsertime.py @@ -290,9 +290,9 @@ def test_add_options(): "mozperftest.test.browsertime.runner.BrowsertimeRunner._setup_node_packages" ) def test_install_url(*mocked): - url = "https://here/tarball/" + "".join( - [random.choice(string.hexdigits[:-6]) for c in range(40)] - ) + url = "https://here/tarball/" + "".join([ + random.choice(string.hexdigits[:-6]) for c in range(40) + ]) mach, metadata, env = get_running_env( browsertime_install_url=url, tests=[EXAMPLE_TEST], diff --git a/python/mozperftest/mozperftest/tests/test_change_detector.py b/python/mozperftest/mozperftest/tests/test_change_detector.py index f9cf6c644d500..663c1458e5350 100644 --- a/python/mozperftest/mozperftest/tests/test_change_detector.py +++ b/python/mozperftest/mozperftest/tests/test_change_detector.py @@ -50,49 +50,43 @@ async def test_change_detector_basic(kwargs=None, return_value=({}, {})): @pytest.mark.asyncio async def test_change_detector_with_task_name(): - await test_change_detector_basic( - { - "task_names": ["test-platform/opt-browsertime-test"], + await test_change_detector_basic({ + "task_names": ["test-platform/opt-browsertime-test"], + "new_test_name": None, + "platform": None, + "new_platform": None, + "base_branch": "try", + "new_branch": "try", + "base_revision": "99", + "new_revision": "99", + }) + + +@pytest.mark.asyncio +async def test_change_detector_option_failure(): + with pytest.raises(Exception): + await test_change_detector_basic({ + "test_name": None, "new_test_name": None, - "platform": None, + "platform": "test-platform/opt", "new_platform": None, "base_branch": "try", "new_branch": "try", "base_revision": "99", "new_revision": "99", - } - ) - - -@pytest.mark.asyncio -async def test_change_detector_option_failure(): - with pytest.raises(Exception): - await test_change_detector_basic( - { - "test_name": None, - "new_test_name": None, - "platform": "test-platform/opt", - "new_platform": None, - "base_branch": "try", - "new_branch": "try", - "base_revision": "99", - "new_revision": "99", - } - ) + }) with pytest.raises(Exception): - await test_change_detector_basic( - { - "test_name": "browsertime-test", - "new_test_name": None, - "platform": None, - "new_platform": None, - "base_branch": "try", - "new_branch": "try", - "base_revision": "99", - "new_revision": "99", - } - ) + await test_change_detector_basic({ + "test_name": "browsertime-test", + "new_test_name": None, + "platform": None, + "new_platform": None, + "base_branch": "try", + "new_branch": "try", + "base_revision": "99", + "new_revision": "99", + }) @pytest.mark.asyncio diff --git a/python/mozperftest/mozperftest/tests/test_functionaltestrunner.py b/python/mozperftest/mozperftest/tests/test_functionaltestrunner.py index 20f8362d60a07..e9c55abec008d 100644 --- a/python/mozperftest/mozperftest/tests/test_functionaltestrunner.py +++ b/python/mozperftest/mozperftest/tests/test_functionaltestrunner.py @@ -12,9 +12,7 @@ def test_functionaltestrunner_pass(): "mozperftest.test.functionaltestrunner.load_class_from_path" ) as load_class_path_mock, mock.patch( "mozperftest.test.functionaltestrunner.FunctionalTestProcessor" - ), mock.patch( - "mozperftest.test.functionaltestrunner.mozlog" - ): + ), mock.patch("mozperftest.test.functionaltestrunner.mozlog"): test_mock = mock.MagicMock() test_mock.test.return_value = 0 load_class_path_mock.return_value = test_mock diff --git a/python/mozperftest/mozperftest/tests/test_influx.py b/python/mozperftest/mozperftest/tests/test_influx.py index 99382ab0ea399..b19752cb7ddc1 100644 --- a/python/mozperftest/mozperftest/tests/test_influx.py +++ b/python/mozperftest/mozperftest/tests/test_influx.py @@ -62,20 +62,18 @@ def mocks(): responses.add( responses.GET, re.compile(secrets), - body=json.dumps( - { - "secret": { - "influx_host": "influxdb", - "influx_port": 0, - "influx_user": "admin", - "influx_password": "pass", - "influx_db": "db", - "grafana_key": "xxx", - "grafana_host": "grafana", - "grafana_port": 0, - } + body=json.dumps({ + "secret": { + "influx_host": "influxdb", + "influx_port": 0, + "influx_user": "admin", + "influx_password": "pass", + "influx_db": "db", + "grafana_key": "xxx", + "grafana_host": "grafana", + "grafana_port": 0, } - ), + }), status=200, ) diff --git a/python/mozperftest/mozperftest/tests/test_mochitest.py b/python/mozperftest/mozperftest/tests/test_mochitest.py index 6535009077c25..6cb3a9584647f 100644 --- a/python/mozperftest/mozperftest/tests/test_mochitest.py +++ b/python/mozperftest/mozperftest/tests/test_mochitest.py @@ -207,9 +207,7 @@ def test_mochitest_profiling(fake_file_path): "mozperftest.test.functionaltestrunner.FunctionalTestRunner.test" ) as test_mock, mock.patch( "mozperftest.test.mochitest.install_requirements_file" - ), mock.patch( - "mozperftest.test.mochitest.Path" - ): + ), mock.patch("mozperftest.test.mochitest.Path"): test_mock.return_value = (0, mock.MagicMock()) try: with pytest.raises(NoPerfMetricsError): diff --git a/python/mozperftest/mozperftest/tests/test_notebookupload.py b/python/mozperftest/mozperftest/tests/test_notebookupload.py index f586e0d699278..9d1f3b162ab66 100644 --- a/python/mozperftest/mozperftest/tests/test_notebookupload.py +++ b/python/mozperftest/mozperftest/tests/test_notebookupload.py @@ -54,9 +54,9 @@ def test_notebookupload_with_filter(notebook, no_filter): for data in data_dict["data"]: assert type(data["value"]) in (int, float) - notebook.assert_has_calls( - [mock.call().post_to_iodide(["scatterplot"], start_local_server=True)] - ) + notebook.assert_has_calls([ + mock.call().post_to_iodide(["scatterplot"], start_local_server=True) + ]) @pytest.mark.parametrize("stats", [False, True]) @@ -88,9 +88,9 @@ def test_compare_to_success(notebook, stats): else: assert any("statistics" in element["subtest"] for element in kwargs["data"]) - notebook.assert_has_calls( - [mock.call().post_to_iodide(["compare"], start_local_server=True)] - ) + notebook.assert_has_calls([ + mock.call().post_to_iodide(["compare"], start_local_server=True) + ]) @pytest.mark.parametrize("filepath", ["invalidPath", str(BT_DATA)]) diff --git a/python/mozperftest/mozperftest/tests/test_perfherder.py b/python/mozperftest/mozperftest/tests/test_perfherder.py index 7f6f99c3f52b4..d37e77dd0e26c 100644 --- a/python/mozperftest/mozperftest/tests/test_perfherder.py +++ b/python/mozperftest/mozperftest/tests/test_perfherder.py @@ -229,14 +229,12 @@ def test_perfherder_simple_names(): # Check if only firstPaint/resource metrics were obtained and # that simplifications occurred - assert all( - [ - "firstPaint" in subtest["name"] - or "duration" in subtest["name"] - or "count" in subtest["name"] - for subtest in output["suites"][0]["subtests"] - ] - ) + assert all([ + "firstPaint" in subtest["name"] + or "duration" in subtest["name"] + or "count" in subtest["name"] + for subtest in output["suites"][0]["subtests"] + ]) found_all = {"firstPaint": False, "count": False, "duration": False} for subtest in output["suites"][0]["subtests"]: @@ -253,23 +251,19 @@ def test_perfherder_simple_names(): # Statistics are not simplified by default assert ( - len( - [ - subtest - for subtest in output["suites"][0]["subtests"] - if "statistics" in subtest["name"] - ] - ) + len([ + subtest + for subtest in output["suites"][0]["subtests"] + if "statistics" in subtest["name"] + ]) == 27 ) assert ( - len( - [ - subtest - for subtest in output["suites"][0]["subtests"] - if "statistics" not in subtest["name"] - ] - ) + len([ + subtest + for subtest in output["suites"][0]["subtests"] + if "statistics" not in subtest["name"] + ]) == 3 ) @@ -305,14 +299,12 @@ def test_perfherder_names_simplified_with_no_exclusions(): # In this case, some metrics will be called "median", "mean", etc. # since those are the simplifications of the first statistics entries # that were found. - assert not all( - [ - "firstPaint" in subtest["name"] - or "duration" in subtest["name"] - or "count" in subtest["name"] - for subtest in output["suites"][0]["subtests"] - ] - ) + assert not all([ + "firstPaint" in subtest["name"] + or "duration" in subtest["name"] + or "count" in subtest["name"] + for subtest in output["suites"][0]["subtests"] + ]) found_all = {"firstPaint": False, "count": False, "duration": False} for subtest in output["suites"][0]["subtests"]: @@ -326,23 +318,19 @@ def test_perfherder_names_simplified_with_no_exclusions(): # Only a portion of the metrics should still have statistics in # their name due to a naming conflict that only emits a warning assert ( - len( - [ - subtest - for subtest in output["suites"][0]["subtests"] - if "statistics" in subtest["name"] - ] - ) + len([ + subtest + for subtest in output["suites"][0]["subtests"] + if "statistics" in subtest["name"] + ]) == 18 ) assert ( - len( - [ - subtest - for subtest in output["suites"][0]["subtests"] - if "statistics" not in subtest["name"] - ] - ) + len([ + subtest + for subtest in output["suites"][0]["subtests"] + if "statistics" not in subtest["name"] + ]) == 12 ) @@ -370,9 +358,11 @@ def test_perfherder_with_extra_metadata_options(): output = json.loads(f.read()) assert len(output["suites"]) == 1 - assert sorted(output["suites"][0]["extraOptions"]) == sorted( - ["option", "second-option", "simpleperf"] - ) + assert sorted(output["suites"][0]["extraOptions"]) == sorted([ + "option", + "second-option", + "simpleperf", + ]) def test_perfherder_with_extra_options(): @@ -397,9 +387,10 @@ def test_perfherder_with_extra_options(): output = json.loads(f.read()) assert len(output["suites"]) == 1 - assert sorted(output["suites"][0]["extraOptions"]) == sorted( - ["option", "second-option"] - ) + assert sorted(output["suites"][0]["extraOptions"]) == sorted([ + "option", + "second-option", + ]) def test_perfherder_with_alerting(): @@ -425,20 +416,16 @@ def test_perfherder_with_alerting(): assert len(output["suites"]) == 1 assert sorted(output["suites"][0]["extraOptions"]) == sorted(["option"]) - assert all( - [ - subtest["shouldAlert"] - for subtest in output["suites"][0]["subtests"] - if "resource" in subtest["name"] - ] - ) - assert not all( - [ - subtest["shouldAlert"] - for subtest in output["suites"][0]["subtests"] - if "firstPaint" in subtest["name"] - ] - ) + assert all([ + subtest["shouldAlert"] + for subtest in output["suites"][0]["subtests"] + if "resource" in subtest["name"] + ]) + assert not all([ + subtest["shouldAlert"] + for subtest in output["suites"][0]["subtests"] + if "firstPaint" in subtest["name"] + ]) def test_perfherder_with_subunits(): @@ -463,20 +450,16 @@ def test_perfherder_with_subunits(): output = json.loads(f.read()) assert len(output["suites"]) == 1 - assert all( - [ - subtest["unit"] == "a-unit" - for subtest in output["suites"][0]["subtests"] - if "resource" in subtest["name"] - ] - ) - assert all( - [ - subtest["unit"] == "ms" - for subtest in output["suites"][0]["subtests"] - if "firstPaint" in subtest["name"] - ] - ) + assert all([ + subtest["unit"] == "a-unit" + for subtest in output["suites"][0]["subtests"] + if "resource" in subtest["name"] + ]) + assert all([ + subtest["unit"] == "ms" + for subtest in output["suites"][0]["subtests"] + if "firstPaint" in subtest["name"] + ]) def test_perfherder_with_supraunits(): @@ -503,20 +486,16 @@ def test_perfherder_with_supraunits(): assert len(output["suites"]) == 1 assert output["suites"][0]["unit"] == "new-unit" - assert all( - [ - subtest["unit"] == "a-unit" - for subtest in output["suites"][0]["subtests"] - if "resource" in subtest["name"] - ] - ) - assert all( - [ - subtest["unit"] == "new-unit" - for subtest in output["suites"][0]["subtests"] - if "firstPaint" in subtest["name"] - ] - ) + assert all([ + subtest["unit"] == "a-unit" + for subtest in output["suites"][0]["subtests"] + if "resource" in subtest["name"] + ]) + assert all([ + subtest["unit"] == "new-unit" + for subtest in output["suites"][0]["subtests"] + if "firstPaint" in subtest["name"] + ]) def test_perfherder_transforms(): @@ -560,18 +539,16 @@ def processor(groups): return (float(groups[0]) * 1000) + float(groups[1]) re_w_group = r".*Displayed.*org\.mozilla\.fennec_aurora.*\+([\d]+)s([\d]+)ms.*" - metadata.add_result( - { - "results": str(HERE / "data" / "home_activity.txt"), - "transformer": "LogCatTimeTransformer", - "transformer-options": { - "first-timestamp": re_w_group, - "processor": processor, - "transform-subtest-name": "TimeToDisplayed", - }, - "name": "LogCat", - } - ) + metadata.add_result({ + "results": str(HERE / "data" / "home_activity.txt"), + "transformer": "LogCatTimeTransformer", + "transformer-options": { + "first-timestamp": re_w_group, + "processor": processor, + "transform-subtest-name": "TimeToDisplayed", + }, + "name": "LogCat", + }) with temp_file() as output: env.set_arg("output", output) diff --git a/python/mozperftest/mozperftest/tests/test_perftestnotebook.py b/python/mozperftest/mozperftest/tests/test_perftestnotebook.py index 58debe9343a0d..6fe350e673a4e 100644 --- a/python/mozperftest/mozperftest/tests/test_perftestnotebook.py +++ b/python/mozperftest/mozperftest/tests/test_perftestnotebook.py @@ -66,9 +66,10 @@ def mocked_open(self, *args, **kwargs): assert list_of_calls.count(mock.call().__enter__()) == 3 browser.assert_called_with(str(upload_file_path)) - server.assert_has_calls( - [mock.call().serve_forever(), mock.call().server_close()] - ) + server.assert_has_calls([ + mock.call().serve_forever(), + mock.call().server_close(), + ]) if __name__ == "__main__": diff --git a/python/mozperftest/mozperftest/tests/test_runner.py b/python/mozperftest/mozperftest/tests/test_runner.py index 4b65775e99814..bfbc7f9337f20 100644 --- a/python/mozperftest/mozperftest/tests/test_runner.py +++ b/python/mozperftest/mozperftest/tests/test_runner.py @@ -48,17 +48,13 @@ def test_side_by_side(arg, patched_mozperftest_tools): "mozperftest.runner._create_artifacts_dir", return_value="fake_path" ) as _, mock.patch( "mozperftest.runner._save_params", return_value="fake_path" - ) as _, mock.patch( - "sys.modules", return_value=mock.MagicMock() - ) as _: - main( - [ - "tools", - "side-by-side", - "-t", - "fake-test-name", - ] - ) + ) as _, mock.patch("sys.modules", return_value=mock.MagicMock()) as _: + main([ + "tools", + "side-by-side", + "-t", + "fake-test-name", + ]) if __name__ == "__main__": diff --git a/python/mozperftest/mozperftest/tests/test_simpleperf.py b/python/mozperftest/mozperftest/tests/test_simpleperf.py index d5a917a354b5a..e6953352e39e9 100644 --- a/python/mozperftest/mozperftest/tests/test_simpleperf.py +++ b/python/mozperftest/mozperftest/tests/test_simpleperf.py @@ -580,9 +580,7 @@ def test_local_simpleperf_symbolicate_timeout(tmp_path): ) as mock_popen, mock.patch( "mozperftest.system.simpleperf.find_node_executable", return_value=[str(node_path)], - ), mock.patch.object( - profiler, "_cleanup" - ) as mock_cleanup: + ), mock.patch.object(profiler, "_cleanup") as mock_cleanup: # Mock processes import_process = make_mock_process(context=True) @@ -682,14 +680,9 @@ def test_ci_simpleperf_symbolicate(tmp_path): clear=False, ), mock.patch("mozperftest.system.simpleperf.ON_TRY", True), mock.patch( "mozperftest.utils.ON_TRY", True - ), mock.patch( - "tempfile.mkdtemp", return_value=str(mock_work_dir_path) - ), mock.patch( + ), mock.patch("tempfile.mkdtemp", return_value=str(mock_work_dir_path)), mock.patch( "shutil.rmtree" - ) as mock_rmtree, mock.patch( - "subprocess.Popen" - ) as mock_popen: - + ) as mock_rmtree, mock.patch("subprocess.Popen") as mock_popen: # Mock processes import_process = make_mock_process(context=True) @@ -839,17 +832,12 @@ def test_ci_simpleperf_symbolicate_timeout(tmp_path): "MOZ_FETCHES_DIR": str(mock_fetch_path), }, clear=False, - ), mock.patch( - "shutil.rmtree" - ) as mock_rmtree, mock.patch( + ), mock.patch("shutil.rmtree") as mock_rmtree, mock.patch( "subprocess.Popen" ) as mock_popen, mock.patch( "mozperftest.system.simpleperf.find_node_executable", return_value=[str(node_path)], - ), mock.patch.object( - profiler, "_cleanup" - ) as mock_cleanup: - + ), mock.patch.object(profiler, "_cleanup") as mock_cleanup: # Mock processes import_process = make_mock_process(context=True) diff --git a/python/mozperftest/mozperftest/tests/test_utils.py b/python/mozperftest/mozperftest/tests/test_utils.py index 3a9b87e2ec36f..e4fc120636919 100644 --- a/python/mozperftest/mozperftest/tests/test_utils.py +++ b/python/mozperftest/mozperftest/tests/test_utils.py @@ -87,15 +87,13 @@ def test_install_package(): vem.bin_path = "someplace" with mock.patch("subprocess.check_call") as mock_check_call: assert install_package(vem, "foo") - mock_check_call.assert_called_once_with( - [ - vem.python_path, - "-m", - "pip", - "install", - "foo", - ] - ) + mock_check_call.assert_called_once_with([ + vem.python_path, + "-m", + "pip", + "install", + "foo", + ]) def test_install_requirements_file(): @@ -105,19 +103,17 @@ def test_install_requirements_file(): "mozperftest.utils.os" ): assert install_requirements_file(vem, "foo") - mock_check_call.assert_called_once_with( - [ - vem.python_path, - "-m", - "pip", - "install", - "-r", - "foo", - "--no-index", - "--find-links", - "https://pypi.pub.build.mozilla.org/pub/", - ] - ) + mock_check_call.assert_called_once_with([ + vem.python_path, + "-m", + "pip", + "install", + "-r", + "foo", + "--no-index", + "--find-links", + "https://pypi.pub.build.mozilla.org/pub/", + ]) @mock.patch("pip._internal.req.constructors.install_req_from_line", new=_req) diff --git a/python/mozperftest/mozperftest/tests/test_xpcshell.py b/python/mozperftest/mozperftest/tests/test_xpcshell.py index efdb4a53875e8..ec8e0092360ff 100644 --- a/python/mozperftest/mozperftest/tests/test_xpcshell.py +++ b/python/mozperftest/mozperftest/tests/test_xpcshell.py @@ -28,17 +28,17 @@ def runTests(self, args): self.log.log_raw({"action": "log", "message": "message"}) # these are the metrics sent by the scripts - self.log.log_raw( - { - "action": "log", - "message": '"perfMetrics"', - "extra": {"metrics1": 1, "metrics2": 2}, - } - ) - - self.log.log_raw( - {"action": "log", "message": '"perfMetrics"', "extra": {"metrics3": 3}} - ) + self.log.log_raw({ + "action": "log", + "message": '"perfMetrics"', + "extra": {"metrics1": 1, "metrics2": 2}, + }) + + self.log.log_raw({ + "action": "log", + "message": '"perfMetrics"', + "extra": {"metrics3": 3}, + }) self.log.test_end("test end") self.log.suite_end("suite end") diff --git a/python/mozperftest/mozperftest/utils.py b/python/mozperftest/mozperftest/utils.py index f48c065fd2bd6..1cd8404609bd5 100644 --- a/python/mozperftest/mozperftest/utils.py +++ b/python/mozperftest/mozperftest/utils.py @@ -236,9 +236,13 @@ def install_package(virtualenv_manager, package, ignore_failure=False): return True with silence(): try: - subprocess.check_call( - [virtualenv_manager.python_path, "-m", "pip", "install", package] - ) + subprocess.check_call([ + virtualenv_manager.python_path, + "-m", + "pip", + "install", + package, + ]) return True except Exception: if not ignore_failure: @@ -281,19 +285,17 @@ def install_requirements_file( cwd = os.getcwd() try: os.chdir(Path(requirements_file).parent) - subprocess.check_call( - [ - virtualenv_manager.python_path, - "-m", - "pip", - "install", - "-r", - requirements_file, - "--no-index", - "--find-links", - "https://pypi.pub.build.mozilla.org/pub/", - ] - ) + subprocess.check_call([ + virtualenv_manager.python_path, + "-m", + "pip", + "install", + "-r", + requirements_file, + "--no-index", + "--find-links", + "https://pypi.pub.build.mozilla.org/pub/", + ]) return True except Exception: if not ignore_failure: diff --git a/python/mozrelease/mozrelease/attribute_builds.py b/python/mozrelease/mozrelease/attribute_builds.py index 9a91a21d1b82b..51c36c382cebd 100644 --- a/python/mozrelease/mozrelease/attribute_builds.py +++ b/python/mozrelease/mozrelease/attribute_builds.py @@ -181,13 +181,11 @@ def main(): work = [] for i in args.input: fn = os.path.basename(i) - work.append( - { - "input": i, - "output": os.path.join(args.output, fn), - "attribution": args.attribution, - } - ) + work.append({ + "input": i, + "output": os.path.join(args.output, fn), + "attribution": args.attribution, + }) else: log.error("No configuration found. Set ATTRIBUTION_CONFIG or pass arguments.") return 1 diff --git a/python/mozrelease/mozrelease/balrog.py b/python/mozrelease/mozrelease/balrog.py index b38bf9bfca98d..dfd6790a94e11 100644 --- a/python/mozrelease/mozrelease/balrog.py +++ b/python/mozrelease/mozrelease/balrog.py @@ -62,10 +62,8 @@ def generate_update_properties(context, config): conditions = _generate_conditions(context, entry.get("conditions", {})) if conditions is not None: - result.append( - { - "fields": fields, - "for": conditions, - } - ) + result.append({ + "fields": fields, + "for": conditions, + }) return result diff --git a/python/mozrelease/mozrelease/buglist_creator.py b/python/mozrelease/mozrelease/buglist_creator.py index ab6bdb4485ba8..a970016210213 100644 --- a/python/mozrelease/mozrelease/buglist_creator.py +++ b/python/mozrelease/mozrelease/buglist_creator.py @@ -18,7 +18,7 @@ BUG_NUMBER_REGEX = re.compile(r"bug \d+", re.IGNORECASE) CHANGELOG_TO_FROM_STRING = "{product}_{version}_RELEASE" CHANGESET_URL_TEMPLATE = ( - "{repo}/{logtype}" "?rev={to_version}+%25+{from_version}&revcount=1000" + "{repo}/{logtype}?rev={to_version}+%25+{from_version}&revcount=1000" ) FULL_CHANGESET_TEMPLATE = "* [Full Mercurial changelog]({url})\n" LIST_DESCRIPTION_TEMPLATE = "Comparing Mercurial tag {from_version} to {to_version}:\n" @@ -245,10 +245,8 @@ def email_release_drivers( notify = Notify(notify_options) for address in addresses: - notify.email( - { - "address": address, - "subject": subject, - "content": content, - } - ) + notify.email({ + "address": address, + "subject": subject, + "content": content, + }) diff --git a/python/mozrelease/mozrelease/mach_commands.py b/python/mozrelease/mozrelease/mach_commands.py index 00027e3da98be..35630698ae66f 100644 --- a/python/mozrelease/mozrelease/mach_commands.py +++ b/python/mozrelease/mozrelease/mach_commands.py @@ -59,8 +59,7 @@ def buglist(command_context, version, product, revision, repo): required=True, action="append", dest="addresses", - help="The email address to send the bug list to " - "(may be specified more than once.", + help="The email address to send the bug list to (may be specified more than once.", ) @CommandArgument( "--version", diff --git a/python/mozrelease/mozrelease/paths.py b/python/mozrelease/mozrelease/paths.py index df8907ec636cd..0114304aeb902 100644 --- a/python/mozrelease/mozrelease/paths.py +++ b/python/mozrelease/mozrelease/paths.py @@ -59,34 +59,28 @@ def getReleaseInstallerPath( MozillaVersion(version) > MozillaVersion(last_linux_bz2_version) ): compression = "xz" - return "/".join( - [ - p.strip("/") - for p in [ - platform, - locale, - "%s-%s.tar.%s" % (productName, version, compression), - ] + return "/".join([ + p.strip("/") + for p in [ + platform, + locale, + "%s-%s.tar.%s" % (productName, version, compression), ] - ) + ]) elif "mac" in platform: - return "/".join( - [ - p.strip("/") - for p in [platform, locale, "%s %s.dmg" % (brandName, version)] - ] - ) + return "/".join([ + p.strip("/") + for p in [platform, locale, "%s %s.dmg" % (brandName, version)] + ]) elif platform.startswith("win"): - return "/".join( - [ - p.strip("/") - for p in [ - platform, - locale, - "%s Setup %s.exe" % (brandName, version), - ] + return "/".join([ + p.strip("/") + for p in [ + platform, + locale, + "%s Setup %s.exe" % (brandName, version), ] - ) + ]) else: raise "Unsupported platform" elif platform.startswith("android"): diff --git a/python/mozrelease/mozrelease/scriptworker_canary.py b/python/mozrelease/mozrelease/scriptworker_canary.py index dc60ccc0a6283..d91b9d4b35f83 100644 --- a/python/mozrelease/mozrelease/scriptworker_canary.py +++ b/python/mozrelease/mozrelease/scriptworker_canary.py @@ -57,9 +57,7 @@ def configure_ssh(ssh_key_secret): ssh_key_file.chmod(0o600) hgrc_content = ( - "[ui]\n" - "username = trybld\n" - "ssh = ssh -i {path} -l {user}\n".format( + "[ui]\nusername = trybld\nssh = ssh -i {path} -l {user}\n".format( path=ssh_key_file, user=ssh_key["user"] ) ) @@ -90,14 +88,12 @@ def push_canary(scriptworkers, addresses, ssh_key_secret): mach = Path(GECKO) / "mach" base_command = [str(mach), "try", "scriptworker", "--closed-tree", "--push-to-vcs"] for address in addresses: - base_command.extend( - [ - "--route", - f"notify.email.{address}.on-failed", - "--route", - f"notify.email.{address}.on-exception", - ] - ) + base_command.extend([ + "--route", + f"notify.email.{address}.on-failed", + "--route", + f"notify.email.{address}.on-exception", + ]) with configure_ssh(ssh_key_secret): env = os.environ.copy() diff --git a/python/mozrelease/mozrelease/update_verify.py b/python/mozrelease/mozrelease/update_verify.py index 326517ca5c6bd..1634cdd2c9548 100644 --- a/python/mozrelease/mozrelease/update_verify.py +++ b/python/mozrelease/mozrelease/update_verify.py @@ -188,20 +188,18 @@ def addRelease( locales = sorted(list(locales.split())) if isinstance(patch_types, str): patch_types = list(patch_types.split()) - self.releases.append( - { - "release": release, - "build_id": build_id, - "locales": locales, - "patch_types": patch_types, - "from": from_path, - "ftp_server_from": ftp_server_from, - "ftp_server_to": ftp_server_to, - "mar_channel_IDs": mar_channel_IDs, - "platform": platform, - "updater_package": updater_package, - } - ) + self.releases.append({ + "release": release, + "build_id": build_id, + "locales": locales, + "patch_types": patch_types, + "from": from_path, + "ftp_server_from": ftp_server_from, + "ftp_server_to": ftp_server_to, + "mar_channel_IDs": mar_channel_IDs, + "platform": platform, + "updater_package": updater_package, + }) def addLocaleToRelease(self, build_id, locale, from_path=None): r = self.getRelease(build_id, from_path) diff --git a/python/mozterm/test/test_widgets.py b/python/mozterm/test/test_widgets.py index 04cb507acda30..1c4b944342d0a 100644 --- a/python/mozterm/test/test_widgets.py +++ b/python/mozterm/test/test_widgets.py @@ -31,12 +31,10 @@ def terminal(): ) def test_footer(terminal): footer = Footer(terminal=terminal) - footer.write( - [ - ("bright_black", "foo"), - ("green", "bar"), - ] - ) + footer.write([ + ("bright_black", "foo"), + ("green", "bar"), + ]) value = terminal.stream.getvalue() expected = "\x1b7\x1b[90mfoo\x1b(B\x1b[m \x1b[32mbar\x1b(B\x1b[m\x1b8" assert value == expected diff --git a/python/mozversioncontrol/mozversioncontrol/repo/git.py b/python/mozversioncontrol/mozversioncontrol/repo/git.py index 1f39adeff99c3..7bd3e70503dbb 100644 --- a/python/mozversioncontrol/mozversioncontrol/repo/git.py +++ b/python/mozversioncontrol/mozversioncontrol/repo/git.py @@ -415,24 +415,22 @@ def data(content): # adding or modifying the files from `changed_files`. # fast-import will output the sha1 for that temporary commit on stdout # (via `get-mark`). - fast_import = "\n".join( - [ - f"commit refs/machtry/{branch}", - "mark :1", - f"author {author}", - f"committer {committer}", - data(commit_message), - f"from {current_head}", - "\n".join( - f"M 100644 inline {path}\n{data(content)}" - for path, content in (changed_files or {}).items() - ), - f"reset refs/machtry/{branch}", - "from 0000000000000000000000000000000000000000", - "get-mark :1", - "", - ] - ) + fast_import = "\n".join([ + f"commit refs/machtry/{branch}", + "mark :1", + f"author {author}", + f"committer {committer}", + data(commit_message), + f"from {current_head}", + "\n".join( + f"M 100644 inline {path}\n{data(content)}" + for path, content in (changed_files or {}).items() + ), + f"reset refs/machtry/{branch}", + "from 0000000000000000000000000000000000000000", + "get-mark :1", + "", + ]) cmd = (str(self._tool), "fast-import", "--quiet") stdout = subprocess.check_output( diff --git a/python/mozversioncontrol/mozversioncontrol/repo/mercurial.py b/python/mozversioncontrol/mozversioncontrol/repo/mercurial.py index d5e3bd7ad76f0..5c23950abe16c 100644 --- a/python/mozversioncontrol/mozversioncontrol/repo/mercurial.py +++ b/python/mozversioncontrol/mozversioncontrol/repo/mercurial.py @@ -427,12 +427,10 @@ def _update_mercurial_repo(self, url, dest: Path, revision): print(f"Ensuring {url} is up to date at {dest}") env = os.environ.copy() - env.update( - { - "HGPLAIN": "1", - "HGRCPATH": "!", - } - ) + env.update({ + "HGPLAIN": "1", + "HGRCPATH": "!", + }) try: subprocess.check_call(pull_args, cwd=str(cwd), env=env) diff --git a/python/mozversioncontrol/test/test_commit.py b/python/mozversioncontrol/test/test_commit.py index a9e245652449d..333f1ffeddd3a 100644 --- a/python/mozversioncontrol/test/test_commit.py +++ b/python/mozversioncontrol/test/test_commit.py @@ -121,9 +121,9 @@ def find_diff_marker(patch: str, filename: str): ] assert matches, f"No diff marker found for '{filename}'" - assert ( - len(matches) == 1 - ), f"More than one diff marker for '{filename}': {matches}" + assert len(matches) == 1, ( + f"More than one diff marker for '{filename}': {matches}" + ) return matches[0] diff --git a/python/mozversioncontrol/test/test_get_mozilla_remote_args.py b/python/mozversioncontrol/test/test_get_mozilla_remote_args.py index 621d0ec92db92..07fd17440a78a 100644 --- a/python/mozversioncontrol/test/test_get_mozilla_remote_args.py +++ b/python/mozversioncontrol/test/test_get_mozilla_remote_args.py @@ -51,17 +51,17 @@ def test_get_mozilla_remote_args(is_cinnabar, expected_remotes, repo): remotes = vcs.get_mozilla_remote_args() - assert remotes == [ - "--remotes" - ], "Default `--remotes` passed without finding official remote." + assert remotes == ["--remotes"], ( + "Default `--remotes` passed without finding official remote." + ) repo.execute_next_step() remotes = sorted(vcs.get_mozilla_remote_args()) - assert ( - remotes == expected_remotes - ), "Multiple non-try remote arguments should be found." + assert remotes == expected_remotes, ( + "Multiple non-try remote arguments should be found." + ) if __name__ == "__main__": diff --git a/python/mozversioncontrol/test/test_try_commit.py b/python/mozversioncontrol/test/test_try_commit.py index 35912bb8b358b..9c847a6125077 100644 --- a/python/mozversioncontrol/test/test_try_commit.py +++ b/python/mozversioncontrol/test/test_try_commit.py @@ -22,17 +22,17 @@ def test_try_commit(repo): except MissingVCSExtension: pytest.xfail("Requires the Mercurial evolve extension.") - assert ( - vcs.head_ref == initial_head_ref - ), "We should have reverted to previous head after try_commit" + assert vcs.head_ref == initial_head_ref, ( + "We should have reverted to previous head after try_commit" + ) # Create an empty commit. with vcs.try_commit(commit_message) as head: assert vcs.get_changed_files(rev=head) == [] - assert ( - vcs.head_ref == initial_head_ref - ), "We should have reverted to previous head after try_commit" + assert vcs.head_ref == initial_head_ref, ( + "We should have reverted to previous head after try_commit" + ) if __name__ == "__main__": diff --git a/python/sites/lint.txt b/python/sites/lint.txt index 3540bfa41f43d..6f82241a1315f 100644 --- a/python/sites/lint.txt +++ b/python/sites/lint.txt @@ -2,7 +2,6 @@ requires-python:>=3.9 # We need sphinx to avoid some rstcheck errors and warnings pypi:Sphinx==7.1.2 pypi:alabaster==0.7.13 -pypi:black==24.8.0 pypi:codespell==2.4.0 pypi:dataclasses==0.6 pypi:distlib==0.3.7 @@ -15,7 +14,7 @@ pypi:psutil==5.9.4 pypi:py==1.11.0 pypi:pytz==2022.7.1 pypi:rstcheck==6.2.4 -pypi:ruff==0.13.3 +pypi:ruff==0.14.9 pypi:snowballstemmer==2.2.0 pypi:sphinxcontrib-applehelp==1.0.4 pypi:sphinxcontrib-htmlhelp==2.0.1 diff --git a/python/sites/python-test.txt b/python/sites/python-test.txt index e383f5e70122f..77bd39ebcb681 100644 --- a/python/sites/python-test.txt +++ b/python/sites/python-test.txt @@ -4,7 +4,6 @@ pypi:Flask==2.1.3 # We need sphinx to avoid some rstcheck errors and warnings pypi:Sphinx==7.1.2 pypi:alabaster==0.7.13 -pypi:black==24.8.0 pypi:codespell==2.4.0 pypi:dataclasses==0.6 pypi:docutils==0.18.1 @@ -15,7 +14,7 @@ pypi:pytest-taskgraph==0.2.0 pypi:pytest==8.4.2 pypi:pytz==2022.7.1 pypi:rstcheck==6.2.4 -pypi:ruff==0.9.0 +pypi:ruff==0.14.9 pypi:snowballstemmer==2.2.0 pypi:sphinxcontrib-applehelp==1.0.4 pypi:sphinxcontrib-htmlhelp==2.0.1 diff --git a/security/manager/ssl/gen_cert_header.py b/security/manager/ssl/gen_cert_header.py index d1fa552f61731..752f646ec356a 100644 --- a/security/manager/ssl/gen_cert_header.py +++ b/security/manager/ssl/gen_cert_header.py @@ -34,7 +34,7 @@ def write_header(output, array_name, certificates): output.write(f" {line}\n") output.write("};\n") output.write( - f'const mozilla::Span {array_name}[] = {{ {", ".join(certificate_names)} }};\n' + f"const mozilla::Span {array_name}[] = {{ {', '.join(certificate_names)} }};\n" ) diff --git a/security/manager/ssl/nsNSSIOLayer.cpp b/security/manager/ssl/nsNSSIOLayer.cpp index 8e77933a89c75..8c258bb20d2ac 100644 --- a/security/manager/ssl/nsNSSIOLayer.cpp +++ b/security/manager/ssl/nsNSSIOLayer.cpp @@ -1258,9 +1258,6 @@ static PRFileDesc* nsSSLIOLayerImportFD(PRFileDesc* fd, if (!sslSock) { return nullptr; } - if (SSL_SetPKCS11PinArg(sslSock, infoObject) != SECSuccess) { - return nullptr; - } if (SSL_HandshakeCallback(sslSock, HandshakeCallback, infoObject) != SECSuccess) { return nullptr; diff --git a/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py b/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py index 4170e5d93385b..e04cd2ea5f645 100644 --- a/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py +++ b/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py @@ -12,6 +12,7 @@ Requires Python 3. """ + import argparse import io import re diff --git a/security/manager/tools/pytlsbinding.py b/security/manager/tools/pytlsbinding.py index 07f210bd9b253..539ae5204af02 100644 --- a/security/manager/tools/pytlsbinding.py +++ b/security/manager/tools/pytlsbinding.py @@ -28,7 +28,6 @@ (SHA-256) are supported. """ - import base64 import hashlib import json diff --git a/security/manager/tools/regen_root_ca_metadata.py b/security/manager/tools/regen_root_ca_metadata.py index 8063e27680eb9..3de176b6dc942 100755 --- a/security/manager/tools/regen_root_ca_metadata.py +++ b/security/manager/tools/regen_root_ca_metadata.py @@ -271,12 +271,12 @@ def write_root_hashes(path, certdata, known_root_hashes): tmpl = Template(ROOT_HASHES_ENTRY_TEMPLATE) for root in certdata: root_hash = known_root_hashes[root.sha256base64()] - digest_half_1 = "".join( - [f"0x{c:02x}, " for c in root_hash.digest[: len(root_hash.digest) >> 1]] - ).removesuffix(" ") - digest_half_2 = "".join( - [f"0x{c:02x}, " for c in root_hash.digest[len(root_hash.digest) >> 1 :]] - ).removesuffix(", ") + digest_half_1 = "".join([ + f"0x{c:02x}, " for c in root_hash.digest[: len(root_hash.digest) >> 1] + ]).removesuffix(" ") + digest_half_2 = "".join([ + f"0x{c:02x}, " for c in root_hash.digest[len(root_hash.digest) >> 1 :] + ]).removesuffix(", ") f.write( tmpl.substitute( label=root_hash.label, diff --git a/security/sandbox/win/src/sandboxbroker/ConfigHelpers.cpp b/security/sandbox/win/src/sandboxbroker/ConfigHelpers.cpp index 9486abfeafa11..c573267613544 100644 --- a/security/sandbox/win/src/sandboxbroker/ConfigHelpers.cpp +++ b/security/sandbox/win/src/sandboxbroker/ConfigHelpers.cpp @@ -31,9 +31,11 @@ SizeTrackingConfig::SizeTrackingConfig(sandbox::TargetConfig* aConfig, MOZ_ASSERT(mConfig); // The calculation uses the kPolMemPageCount constant in sandbox_policy.h. - // We reduce the allowable size by 1 to account for the PolicyGlobal. + // We reduce the allowable size by 2 to account for the PolicyGlobal and + // padding that occurs during LowLevelPolicy::Done. See bug 2009140. MOZ_ASSERT(aStoragePages > 0); - MOZ_ASSERT(static_cast(aStoragePages) < sandbox::kPolMemPageCount); + MOZ_ASSERT(static_cast(aStoragePages) <= + sandbox::kPolMemPageCount - 2); constexpr int32_t kOneMemPage = 4096; mRemainingSize = kOneMemPage * aStoragePages; diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp index d027e92ffd65b..d97dab9771fb7 100644 --- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp +++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp @@ -1362,8 +1362,9 @@ void SandboxBroker::SetSecurityLevelForGPUProcess(int32_t aSandboxLevel) { config->AddRestrictingRandomSid(); // Policy wrapper to keep track of available rule space. The full policy has - // 14 pages, so 13 allows one page for generic process rules. - sandboxing::SizeTrackingConfig trackingConfig(config, 13); + // 14 pages, so 12 allows two pages for generic process rules and to allow for + // padding that occurs in LowLevelPolicy::Done. See bug 2009140. + sandboxing::SizeTrackingConfig trackingConfig(config, 12); if (StaticPrefs::security_sandbox_chrome_pipe_rule_enabled()) { // Add the policy for the client side of a pipe. It is just a file diff --git a/servo/components/style/Cargo.toml b/servo/components/style/Cargo.toml index 99aff22d11789..1c37aa62bef6c 100644 --- a/servo/components/style/Cargo.toml +++ b/servo/components/style/Cargo.toml @@ -89,6 +89,8 @@ smallvec = "1.0" static_assertions = "1.1" static_prefs = { path = "../../../modules/libpref/init/static_prefs" } string_cache = { version = "0.8", optional = true } +strum = "0.27" +strum_macros = "0.27" style_derive = {path = "../style_derive"} style_traits = {path = "../style_traits"} to_shmem = {path = "../to_shmem"} diff --git a/servo/components/style/custom_properties.rs b/servo/components/style/custom_properties.rs index 25fe176ccb8b4..199d6db4de5f3 100644 --- a/servo/components/style/custom_properties.rs +++ b/servo/components/style/custom_properties.rs @@ -27,6 +27,7 @@ use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet}; use crate::stylesheets::UrlExtraData; use crate::stylist::Stylist; use crate::values::computed::{self, ToComputedValue}; +use crate::values::generics::calc::SortKey as AttrUnit; use crate::values::specified::FontRelativeLength; use crate::values::AtomIdent; use crate::Atom; @@ -476,8 +477,9 @@ enum SubstitutionFunctionKind { #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem, Parse)] enum AttributeType { None, + RawString, Type(Descriptor), - Unit, + Unit(AttrUnit), } #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] @@ -841,18 +843,12 @@ fn parse_declaration_value_block<'i, 't>( name.as_ref() }); - let mut attribute_syntax = AttributeType::None; - if substitution_kind == SubstitutionFunctionKind::Attr - && input - .try_parse(|input| input.expect_function_matching("type")) - .is_ok() - { - // TODO(descalante): determine what to do for `type(garbage)` bug 2006626 - attribute_syntax = input - .parse_nested_block(Descriptor::from_css_parser) - .ok() - .map_or(AttributeType::None, AttributeType::Type); - } + let attribute_syntax = + if substitution_kind == SubstitutionFunctionKind::Attr { + parse_attr_type(input) + } else { + AttributeType::None + }; // We want the order of the references to match source order. So we need to reserve our slot // now, _before_ parsing our fallback. Note that we don't care if parsing fails after all, since @@ -972,6 +968,32 @@ fn parse_declaration_value_block<'i, 't>( Ok((first_token_type, last_token_type)) } +/// Parse = type( ) | raw-string | number | . +/// https://drafts.csswg.org/css-values-5/#attr-notation +fn parse_attr_type<'i, 't>(input: &mut Parser<'i, 't>) -> AttributeType { + input + .try_parse(|input| { + Ok(match input.next()? { + Token::Function(ref name) if name.eq_ignore_ascii_case("type") => { + AttributeType::Type(input.parse_nested_block(Descriptor::from_css_parser)?) + }, + Token::Ident(ref ident) => { + if ident.eq_ignore_ascii_case("raw-string") { + AttributeType::RawString + } else { + let unit = AttrUnit::from_ident(ident).map_err(|_| { + input.new_custom_error(StyleParseErrorKind::UnspecifiedError) + })?; + AttributeType::Unit(unit) + } + }, + Token::Delim('%') => AttributeType::Unit(AttrUnit::Percentage), + _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), + }) + }) + .unwrap_or(AttributeType::None) +} + /// A struct that takes care of encapsulating the cascade process for custom properties. pub struct CustomPropertiesBuilder<'a, 'b: 'a> { seen: PrecomputedHashSet<&'a Name>, @@ -2149,6 +2171,13 @@ fn substitute_one_reference<'a>( references: &mut std::iter::Peekable>, attr_provider: &dyn AttributeProvider, ) -> Result, ()> { + let simple_subst = |s: &str| { + Some(Substitution::new( + Cow::Owned(quoted_css_string(s)), + TokenSerializationType::Nothing, + TokenSerializationType::Nothing, + )) + }; let substitution: Option<_> = match reference.substitution_kind { SubstitutionFunctionKind::Var => { let registration = stylist.get_custom_property_registration(&reference.name); @@ -2163,28 +2192,56 @@ fn substitute_one_reference<'a>( .get(&reference.name, device, url_data) .map(Substitution::from_value) }, + // https://drafts.csswg.org/css-values-5/#attr-substitution SubstitutionFunctionKind::Attr => attr_provider .get_attr(AtomIdent::cast(&reference.name)) - .and_then(|attr| { - let AttributeType::Type(syntax) = &reference.attribute_syntax else { - return Some(Substitution::new( - Cow::Owned(quoted_css_string(&attr)), - TokenSerializationType::Nothing, - TokenSerializationType::Nothing, - )); - }; - let mut input = ParserInput::new(&attr); - let mut parser = Parser::new(&mut input); - let value = SpecifiedRegisteredValue::parse( - &mut parser, - syntax, - url_data, - AllowComputationallyDependent::Yes, - ) - .ok()?; - let value = value.to_computed_value(computed_context); - Some(Substitution::from_value(value.to_variable_value())) - }), + .map_or_else( + || { + // Special case when fallback and are omitted. + // See FAILURE: https://drafts.csswg.org/css-values-5/#attr-substitution + if reference.fallback.is_none() + && reference.attribute_syntax == AttributeType::None + { + simple_subst("") + } else { + None + } + }, + |attr| { + let mut input = ParserInput::new(&attr); + let mut parser = Parser::new(&mut input); + match &reference.attribute_syntax { + AttributeType::Unit(unit) => { + let css = { + // Verify that attribute data is a . + parser.expect_number().ok()?; + let mut s = attr.clone(); + s.push_str(unit.as_ref()); + s + }; + let serialization = match unit { + AttrUnit::Number => TokenSerializationType::Number, + AttrUnit::Percentage => TokenSerializationType::Percentage, + _ => TokenSerializationType::Dimension, + }; + let value = + ComputedValue::new(css, url_data, serialization, serialization); + Some(Substitution::from_value(value)) + }, + AttributeType::Type(syntax) => { + let value = SpecifiedRegisteredValue::parse( + &mut parser, + syntax, + url_data, + AllowComputationallyDependent::Yes, + ) + .ok()?; + Some(Substitution::from_value(value.to_variable_value())) + }, + AttributeType::RawString | AttributeType::None => simple_subst(&attr), + } + }, + ), }; if let Some(s) = substitution { diff --git a/servo/components/style/properties_and_values/syntax/mod.rs b/servo/components/style/properties_and_values/syntax/mod.rs index e0b62af03ccb6..83e74584c7e12 100644 --- a/servo/components/style/properties_and_values/syntax/mod.rs +++ b/servo/components/style/properties_and_values/syntax/mod.rs @@ -58,8 +58,19 @@ impl Descriptor { /// https://drafts.csswg.org/css-values-5/#typedef-syntax #[inline] pub fn from_css_parser<'i>(input: &mut CSSParser<'i, '_>) -> Result> { - //TODO(bug 2006624): Should also accept let mut components = vec![]; + + if input.try_parse(|i| i.expect_delim('*')).is_ok() { + return Ok(Self::universal()); + } + + // Parse if given. + if let Ok(syntax_string) = input.try_parse(|i| i.expect_string_cloned()) { + return Self::from_str(syntax_string.as_ref(), /* save_specified = */ true).or_else( + |err| Err(input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(err))), + ); + } + loop { let name = Self::try_parse_component_name(input).map_err(|err| { input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(err)) diff --git a/servo/components/style/properties_and_values/value.rs b/servo/components/style/properties_and_values/value.rs index fbb2e86704f8d..f322ab2cd5ddf 100644 --- a/servo/components/style/properties_and_values/value.rs +++ b/servo/components/style/properties_and_values/value.rs @@ -240,6 +240,37 @@ impl Value { } } +impl + Value> +where + Self: ToCss, +{ + fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) { + match &self.v { + ValueInner::Component(component) => component.serialization_types(), + ValueInner::Universal(_) => unreachable!(), + ValueInner::List(list) => list + .components + .first() + .map_or(Default::default(), |f| f.serialization_types()), + } + } + + /// Convert to an untyped variable value. + pub fn to_variable_value(&self) -> ComputedPropertyValue { + if let ValueInner::Universal(ref value) = self.v { + return (**value).clone(); + } + let serialization_types = self.serialization_types(); + ComputedPropertyValue::new( + self.to_css_string(), + &self.url_data, + serialization_types.0, + serialization_types.1, + ) + } +} + /// A specified registered custom property value. #[derive( Animate, ToComputedValue, ToResolvedValue, ToCss, Clone, Debug, MallocSizeOf, PartialEq, ToShmem, @@ -322,17 +353,6 @@ impl SpecifiedValue { } impl ComputedValue { - fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) { - match &self.v { - ValueInner::Component(component) => component.serialization_types(), - ValueInner::Universal(_) => unreachable!(), - ValueInner::List(list) => list - .components - .first() - .map_or(Default::default(), |f| f.serialization_types()), - } - } - fn to_declared_value(&self) -> properties::CustomDeclarationValue { if let ValueInner::Universal(ref var) = self.v { return properties::CustomDeclarationValue::Unparsed(Arc::clone(var)); @@ -351,19 +371,6 @@ impl ComputedValue { } } - /// Convert to an untyped variable value. - pub fn to_variable_value(&self) -> ComputedPropertyValue { - if let ValueInner::Universal(ref value) = self.v { - return (**value).clone(); - } - let serialization_types = self.serialization_types(); - ComputedPropertyValue::new( - self.to_css_string(), - &self.url_data, - serialization_types.0, - serialization_types.1, - ) - } } /// Whether the computed value parsing should allow computationaly dependent values like 3em or diff --git a/servo/components/style/values/generics/calc.rs b/servo/components/style/values/generics/calc.rs index 663e52d3b3766..3b1a9a60b6408 100644 --- a/servo/components/style/values/generics/calc.rs +++ b/servo/components/style/values/generics/calc.rs @@ -11,9 +11,11 @@ use crate::values::generics::length::GenericAnchorSizeFunction; use crate::values::generics::position::{GenericAnchorFunction, GenericAnchorSide}; use num_traits::Zero; use smallvec::SmallVec; +use std::convert::AsRef; use std::fmt::{self, Write}; use std::ops::{Add, Mul, Neg, Rem, Sub}; use std::{cmp, mem}; +use strum_macros::AsRefStr; use style_traits::{CssWriter, NumericValue, ToCss, ToTyped, TypedValue}; use thin_vec::ThinVec; @@ -116,10 +118,16 @@ pub enum RoundingStrategy { /// This determines the order in which we serialize members of a calc() sum. /// /// See https://drafts.csswg.org/css-values-4/#sort-a-calculations-children -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive( + AsRefStr, Clone, Copy, Debug, Eq, Ord, Parse, PartialEq, PartialOrd, MallocSizeOf, ToShmem, +)] +#[strum(serialize_all = "lowercase")] #[allow(missing_docs)] pub enum SortKey { + #[strum(serialize = "")] Number, + #[css(skip)] + #[strum(serialize = "%")] Percentage, Cap, Ch, @@ -147,6 +155,7 @@ pub enum SortKey { Lvmax, Lvmin, Lvw, + Ms, Px, Rcap, Rch, @@ -154,7 +163,7 @@ pub enum SortKey { Rex, Ric, Rlh, - Sec, + S, // Sec Svb, Svh, Svi, @@ -167,7 +176,9 @@ pub enum SortKey { Vmax, Vmin, Vw, + #[css(skip)] ColorComponent, + #[css(skip)] Other, } diff --git a/servo/components/style/values/specified/calc.rs b/servo/components/style/values/specified/calc.rs index b440908ea1e8a..cbc43682c3ed2 100644 --- a/servo/components/style/values/specified/calc.rs +++ b/servo/components/style/values/specified/calc.rs @@ -306,7 +306,7 @@ impl generic::CalcNodeLeaf for Leaf { match *self { Self::Number(..) => SortKey::Number, Self::Percentage(..) => SortKey::Percentage, - Self::Time(..) => SortKey::Sec, + Self::Time(..) => SortKey::S, Self::Resolution(..) => SortKey::Dppx, Self::Angle(..) => SortKey::Deg, Self::Length(ref l) => match *l { diff --git a/servo/components/style/values/specified/counters.rs b/servo/components/style/values/specified/counters.rs index 078c3190dc542..b89d53358bf62 100644 --- a/servo/components/style/values/specified/counters.rs +++ b/servo/components/style/values/specified/counters.rs @@ -226,7 +226,7 @@ impl Parse for Content { let style = Content::parse_counter_style(context, input); Ok(generics::ContentItem::Counters(name, separator, style)) }), - "attr" => input.parse_nested_block(|input| { + "attr" if !static_prefs::pref!("layout.css.attr.enabled") => input.parse_nested_block(|input| { Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?)) }), _ => { diff --git a/stylelint-rollouts.config.js b/stylelint-rollouts.config.js index 558ef36523315..04bd62656560c 100644 --- a/stylelint-rollouts.config.js +++ b/stylelint-rollouts.config.js @@ -123,6 +123,7 @@ module.exports = [ "browser/extensions/newtab/content-src/components/Weather/_Weather.scss", "browser/extensions/newtab/content-src/components/Widgets/FocusTimer/_FocusTimer.scss", "browser/extensions/newtab/content-src/components/Widgets/Lists/_Lists.scss", + "browser/extensions/newtab/content-src/components/Widgets/WeatherForecast/WeatherForecast.scss", "browser/extensions/newtab/content-src/components/Widgets/_Widgets.scss", "browser/extensions/newtab/content-src/styles/_icons.scss", "browser/extensions/newtab/content-src/styles/_mixins.scss", @@ -503,6 +504,7 @@ module.exports = [ "browser/extensions/newtab/content-src/components/Weather/_Weather.scss", "browser/extensions/newtab/content-src/components/Widgets/FocusTimer/_FocusTimer.scss", "browser/extensions/newtab/content-src/components/Widgets/Lists/_Lists.scss", + "browser/extensions/newtab/content-src/components/Widgets/WeatherForecast/WeatherForecast.scss", "browser/extensions/newtab/content-src/components/Widgets/_Widgets.scss", "browser/extensions/newtab/content-src/styles/_icons.scss", "browser/extensions/newtab/content-src/styles/_mixins.scss", diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index b879af0d4dc58..fc6b1d30a0f68 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -315,7 +315,7 @@ end = "2025-08-30" [[wildcard-audits.hawk]] who = "Ryan Safaeian " criteria = "safe-to-deploy" -user-id = 158511 # Yarik (lotas) +user-id = 158511 # Yaraslau Kurmyza (lotas) start = "2022-05-05" end = "2026-04-24" notes = "Hawk is written and maintained by mozilla employees." diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 3013c818ab5ad..8010150606e0f 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -31,10 +31,6 @@ notes = "This is the upstream code plus the ARM intrinsics workaround from qcms, audit-as-crates-io = true notes = "This is the upstream code plus a few local fixes, see bug 1685697." -[policy.bhttp] -audit-as-crates-io = true -notes = "This is upstream code in between released versions." - [policy."bindgen:0.72.0@git:9366e0af8da529c958b4cd4fcbe492d951c86f5c"] audit-as-crates-io = true notes = "This is the upstream code not yet released" @@ -197,10 +193,6 @@ notes = "This is a pinned version of the upstream code, pending update of the cr audit-as-crates-io = true notes = "This is the upstream code plus a backported fix." -[policy.ohttp] -audit-as-crates-io = true -notes = "This is upstream code in between released versions." - [policy.osclientcerts] audit-as-crates-io = false notes = "This is a first-party crate that happens to have been pushed to crates.io a very long time ago but was yanked." diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 8fde91def44d7..b997d3370864d 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -44,8 +44,8 @@ user-login = "jschanck" user-name = "John Schanck" [[publisher.bhttp]] -version = "0.6.1" -when = "2025-06-19" +version = "0.7.2" +when = "2025-12-11" user-id = 128763 user-login = "martinthomson" user-name = "Martin Thomson" @@ -300,7 +300,7 @@ version = "5.0.1" when = "2024-09-13" user-id = 158511 user-login = "lotas" -user-name = "Yarik" +user-name = "Yaraslau Kurmyza" [[publisher.headers]] version = "0.3.9" @@ -457,8 +457,8 @@ user-login = "seanmonstar" user-name = "Sean McArthur" [[publisher.ohttp]] -version = "0.6.1" -when = "2025-06-19" +version = "0.7.2" +when = "2025-12-11" user-id = 128763 user-login = "martinthomson" user-name = "Martin Thomson" diff --git a/taskcluster/android_taskgraph/__init__.py b/taskcluster/android_taskgraph/__init__.py index 116e7d7637765..46e1711444dc2 100644 --- a/taskcluster/android_taskgraph/__init__.py +++ b/taskcluster/android_taskgraph/__init__.py @@ -20,15 +20,13 @@ def register(graph_config): Import all modules that are siblings of this one, triggering decorators in the process. """ - _import_modules( - [ - "job", - "parameters", - "target_tasks", - "util.group_by", - "worker_types", - ] - ) + _import_modules([ + "job", + "parameters", + "target_tasks", + "util.group_by", + "worker_types", + ]) def _import_modules(modules): diff --git a/taskcluster/android_taskgraph/job.py b/taskcluster/android_taskgraph/job.py index f8635c76675c3..ca434dc5c39cf 100644 --- a/taskcluster/android_taskgraph/job.py +++ b/taskcluster/android_taskgraph/job.py @@ -24,31 +24,27 @@ Optional("json"): bool, } -gradlew_schema = Schema( - { - Required("using"): "gradlew", - Optional("pre-gradlew"): [[str]], - Required("gradlew"): [str], - Optional("post-gradlew"): [[str]], - # Base work directory used to set up the task. - Required("workdir"): str, - Optional("use-caches"): Any(bool, [str]), - Optional("secrets"): [secret_schema], - Optional("dummy-secrets"): [dummy_secret_schema], - } -) - -run_commands_schema = Schema( - { - Required("using"): "run-commands", - Optional("pre-commands"): [[str]], - Required("commands"): [[taskref_or_string]], - Required("workdir"): str, - Optional("use-caches"): Any(bool, [str]), - Optional("secrets"): [secret_schema], - Optional("dummy-secrets"): [dummy_secret_schema], - } -) +gradlew_schema = Schema({ + Required("using"): "gradlew", + Optional("pre-gradlew"): [[str]], + Required("gradlew"): [str], + Optional("post-gradlew"): [[str]], + # Base work directory used to set up the task. + Required("workdir"): str, + Optional("use-caches"): Any(bool, [str]), + Optional("secrets"): [secret_schema], + Optional("dummy-secrets"): [dummy_secret_schema], +}) + +run_commands_schema = Schema({ + Required("using"): "run-commands", + Optional("pre-commands"): [[str]], + Required("commands"): [[taskref_or_string]], + Required("workdir"): str, + Optional("use-caches"): Any(bool, [str]), + Optional("secrets"): [secret_schema], + Optional("dummy-secrets"): [dummy_secret_schema], +}) @run_job_using("docker-worker", "run-commands", schema=run_commands_schema) @@ -78,15 +74,13 @@ def configure_gradlew(config, job, taskdesc): fetches_dir = "/builds/worker/fetches" topsrc_dir = "/builds/worker/checkouts/gecko" - worker.setdefault("env", {}).update( - { - "ANDROID_SDK_ROOT": path.join(fetches_dir, "android-sdk-linux"), - "GRADLE_USER_HOME": path.join( - topsrc_dir, "mobile/android/gradle/dotgradle-offline" - ), - "MOZ_BUILD_DATE": config.params["moz_build_date"], - } - ) + worker.setdefault("env", {}).update({ + "ANDROID_SDK_ROOT": path.join(fetches_dir, "android-sdk-linux"), + "GRADLE_USER_HOME": path.join( + topsrc_dir, "mobile/android/gradle/dotgradle-offline" + ), + "MOZ_BUILD_DATE": config.params["moz_build_date"], + }) worker["env"].setdefault( "MOZCONFIG", path.join( @@ -103,14 +97,12 @@ def configure_gradlew(config, job, taskdesc): for secret in run.pop("dummy-secrets", []) ] secrets = [_generate_secret_command(secret) for secret in run.get("secrets", [])] - worker["env"].update( - { - "PRE_GRADLEW": _convert_commands_to_string(run.pop("pre-gradlew", [])), - "GET_SECRETS": _convert_commands_to_string(dummy_secrets + secrets), - "GRADLEW_ARGS": " ".join(run.pop("gradlew")), - "POST_GRADLEW": _convert_commands_to_string(run.pop("post-gradlew", [])), - } - ) + worker["env"].update({ + "PRE_GRADLEW": _convert_commands_to_string(run.pop("pre-gradlew", [])), + "GET_SECRETS": _convert_commands_to_string(dummy_secrets + secrets), + "GRADLEW_ARGS": " ".join(run.pop("gradlew")), + "POST_GRADLEW": _convert_commands_to_string(run.pop("post-gradlew", [])), + }) run["command"] = ( "/builds/worker/checkouts/gecko/taskcluster/scripts/builder/build-android.sh" ) diff --git a/taskcluster/android_taskgraph/transforms/beetmover_android_app.py b/taskcluster/android_taskgraph/transforms/beetmover_android_app.py index 6f8d8c50f39c0..9d2aad6fdd63b 100644 --- a/taskcluster/android_taskgraph/transforms/beetmover_android_app.py +++ b/taskcluster/android_taskgraph/transforms/beetmover_android_app.py @@ -102,15 +102,13 @@ def make_task_worker(config, tasks): locale = task["attributes"].get("locale") build_type = task["attributes"]["build-type"] - task["worker"].update( - { - "implementation": "beetmover", - "release-properties": craft_release_properties(config, task), - "artifact-map": generate_beetmover_artifact_map( - config, task, platform=build_type, locale=locale - ), - } - ) + task["worker"].update({ + "implementation": "beetmover", + "release-properties": craft_release_properties(config, task), + "artifact-map": generate_beetmover_artifact_map( + config, task, platform=build_type, locale=locale + ), + }) if locale: task["worker"]["locale"] = locale diff --git a/taskcluster/android_taskgraph/transforms/build_android_app.py b/taskcluster/android_taskgraph/transforms/build_android_app.py index 88a81c350bdc3..034f33b6232dc 100644 --- a/taskcluster/android_taskgraph/transforms/build_android_app.py +++ b/taskcluster/android_taskgraph/transforms/build_android_app.py @@ -6,7 +6,6 @@ build-apk and build-bundle kinds. """ - from taskgraph.transforms.base import TransformSequence from taskgraph.util import path @@ -82,30 +81,26 @@ def add_shippable_secrets(config, tasks): task.pop("include-shippable-secrets", False) and config.params["level"] == "3" ): - secrets.extend( - [ - { - "key": key, - "name": _get_secret_index(task["name"]), - "path": target_file, - } - for key, target_file in _get_secrets_keys_and_target_files(task) - ] - ) + secrets.extend([ + { + "key": key, + "name": _get_secret_index(task["name"]), + "path": target_file, + } + for key, target_file in _get_secrets_keys_and_target_files(task) + ]) else: - dummy_secrets.extend( - [ - { - "content": fake_value, - "path": target_file, - } - for fake_value, target_file in ( - ("faketoken", ".adjust_token"), - ("faketoken", ".mls_token"), - ("https://fake@sentry.prod.mozaws.net/368", ".sentry_token"), - ) - ] - ) + dummy_secrets.extend([ + { + "content": fake_value, + "path": target_file, + } + for fake_value, target_file in ( + ("faketoken", ".adjust_token"), + ("faketoken", ".mls_token"), + ("https://fake@sentry.prod.mozaws.net/368", ".sentry_token"), + ) + ]) yield task @@ -120,16 +115,14 @@ def _get_secrets_keys_and_target_files(task): if task["name"].startswith("fenix-"): gradle_build_type = task["run"]["gradle-build-type"] - secrets.extend( - [ - ( - "firebase", - f"app/src/{gradle_build_type}/res/values/firebase.xml", - ), - ("wallpaper_url", ".wallpaper_url"), - ("pocket_consumer_key", ".pocket_consumer_key"), - ] - ) + secrets.extend([ + ( + "firebase", + f"app/src/{gradle_build_type}/res/values/firebase.xml", + ), + ("wallpaper_url", ".wallpaper_url"), + ("pocket_consumer_key", ".pocket_consumer_key"), + ]) return secrets @@ -221,13 +214,11 @@ def add_disable_optimization(config, tasks): def add_nightly_version(config, tasks): for task in tasks: if task.pop("include-nightly-version", False): - task["run"]["gradlew"].extend( - [ - # We only set the `official` flag here. The actual version name will be determined - # by Gradle (depending on the Gecko/A-C version being used) - "-Pofficial" - ] - ) + task["run"]["gradlew"].extend([ + # We only set the `official` flag here. The actual version name will be determined + # by Gradle (depending on the Gecko/A-C version being used) + "-Pofficial" + ]) yield task @@ -235,9 +226,10 @@ def add_nightly_version(config, tasks): def add_release_version(config, tasks): for task in tasks: if task.pop("include-release-version", False): - task["run"]["gradlew"].extend( - ["-PversionName={}".format(config.params["version"]), "-Pofficial"] - ) + task["run"]["gradlew"].extend([ + "-PversionName={}".format(config.params["version"]), + "-Pofficial", + ]) yield task @@ -271,19 +263,17 @@ def add_artifacts(config, tasks): apk_name = artifact_template["name"].format( gradle_build=gradle_build, **apk ) - artifacts.append( - { - "type": artifact_template["type"], - "name": apk_name, - "path": artifact_template["path"].format( - gradle_build_type=gradle_build_type, - gradle_build=gradle_build, - apk_path=apk_path, - source_project_name=source_project_name, - **apk, - ), - } - ) + artifacts.append({ + "type": artifact_template["type"], + "name": apk_name, + "path": artifact_template["path"].format( + gradle_build_type=gradle_build_type, + gradle_build=gradle_build, + apk_path=apk_path, + source_project_name=source_project_name, + **apk, + ), + }) apks[apk["abi"]] = { "name": apk_name, } @@ -297,19 +287,17 @@ def add_artifacts(config, tasks): else: aab_filename = f"app-{gradle_build}-{gradle_build_type}.aab" - artifacts.append( - { - "type": artifact_template["type"], - "name": artifact_template["name"], - "path": artifact_template["path"].format( - gradle_build_type=gradle_build_type, - gradle_build=gradle_build, - source_project_name=source_project_name, - variant_name=variant_name, - aab_filename=aab_filename, - ), - } - ) + artifacts.append({ + "type": artifact_template["type"], + "name": artifact_template["name"], + "path": artifact_template["path"].format( + gradle_build_type=gradle_build_type, + gradle_build=gradle_build, + source_project_name=source_project_name, + variant_name=variant_name, + aab_filename=aab_filename, + ), + }) task["attributes"]["aab"] = artifact_template["name"] yield task diff --git a/taskcluster/android_taskgraph/transforms/build_components.py b/taskcluster/android_taskgraph/transforms/build_components.py index d4e1f8c508ece..2413a92b4fcbb 100644 --- a/taskcluster/android_taskgraph/transforms/build_components.py +++ b/taskcluster/android_taskgraph/transforms/build_components.py @@ -148,15 +148,13 @@ def add_artifacts(config, tasks): ]: if key in task: optional_artifact_template = task.pop(key, {}) - build_artifact_definitions.append( - { - "type": optional_artifact_template["type"], - "name": optional_artifact_template["name"], - "path": optional_artifact_template["path"].format( - component_path=get_path(component) - ), - } - ) + build_artifact_definitions.append({ + "type": optional_artifact_template["type"], + "name": optional_artifact_template["name"], + "path": optional_artifact_template["path"].format( + component_path=get_path(component) + ), + }) if artifact_template: all_extensions = get_extensions(component) @@ -185,22 +183,20 @@ def add_artifacts(config, tasks): artifact_full_name = artifact_template["name"].format( artifact_file_name=artifact_file_name, ) - build_artifact_definitions.append( - { - "type": artifact_template["type"], - "name": artifact_full_name, - "path": artifact_template["path"].format( - component_path=get_path(component), - component=component, - version=craft_path_version( - version, - task["attributes"]["build-type"], - nightly_version, - ), - artifact_file_name=artifact_file_name, + build_artifact_definitions.append({ + "type": artifact_template["type"], + "name": artifact_full_name, + "path": artifact_template["path"].format( + component_path=get_path(component), + component=component, + version=craft_path_version( + version, + task["attributes"]["build-type"], + nightly_version, ), - } - ) + artifact_file_name=artifact_file_name, + ), + }) artifacts[extension] = artifact_full_name diff --git a/taskcluster/android_taskgraph/transforms/notify.py b/taskcluster/android_taskgraph/transforms/notify.py index d4fe097e3ee78..ad09253bf8c2d 100644 --- a/taskcluster/android_taskgraph/transforms/notify.py +++ b/taskcluster/android_taskgraph/transforms/notify.py @@ -38,12 +38,10 @@ def add_notify_email(config, tasks): } routes = task.setdefault("routes", []) - routes.extend( - [ - f"notify.email.{address}.on-{reason}" - for address in email_config["to-addresses"] - for reason in email_config["on-reasons"] - ] - ) + routes.extend([ + f"notify.email.{address}.on-{reason}" + for address in email_config["to-addresses"] + for reason in email_config["on-reasons"] + ]) yield task diff --git a/taskcluster/android_taskgraph/transforms/push_android_app.py b/taskcluster/android_taskgraph/transforms/push_android_app.py index a074b039bb2ec..0c396ed722ec8 100644 --- a/taskcluster/android_taskgraph/transforms/push_android_app.py +++ b/taskcluster/android_taskgraph/transforms/push_android_app.py @@ -6,7 +6,6 @@ kind. """ - from taskgraph.transforms.base import TransformSequence from taskgraph.util.schema import resolve_keyed_by @@ -31,6 +30,6 @@ def resolve_keys(config, tasks): **{ "build-type": task["attributes"]["build-type"], "level": config.params["level"], - } + }, ) yield task diff --git a/taskcluster/android_taskgraph/transforms/signing.py b/taskcluster/android_taskgraph/transforms/signing.py index 6f55aa1ba73b3..421329a69ac49 100644 --- a/taskcluster/android_taskgraph/transforms/signing.py +++ b/taskcluster/android_taskgraph/transforms/signing.py @@ -27,7 +27,7 @@ def resolve_keys(config, tasks): **{ "build-type": task["attributes"]["build-type"], "level": config.params["level"], - } + }, ) yield task @@ -65,8 +65,7 @@ def filter_out_checksums(config, tasks): def set_detached_signature_artifacts(config, tasks): for task in tasks: task["attributes"]["artifacts"] = { - extension - + _DETACHED_SIGNATURE_EXTENSION: path + extension + _DETACHED_SIGNATURE_EXTENSION: path + _DETACHED_SIGNATURE_EXTENSION for extension, path in task["attributes"]["artifacts"].items() } diff --git a/taskcluster/android_taskgraph/transforms/startup_tests_deps.py b/taskcluster/android_taskgraph/transforms/startup_tests_deps.py index 1d14866b70832..df9ae291c47fa 100644 --- a/taskcluster/android_taskgraph/transforms/startup_tests_deps.py +++ b/taskcluster/android_taskgraph/transforms/startup_tests_deps.py @@ -22,7 +22,7 @@ def add_android_startup_test_dependencies(config, jobs): if job_build_type in t.attributes.get("build-type", "") ] if matching_tasks: - job.setdefault("dependencies", {}).update( - {t.label: t.label for t in matching_tasks} - ) + job.setdefault("dependencies", {}).update({ + t.label: t.label for t in matching_tasks + }) yield job diff --git a/taskcluster/android_taskgraph/transforms/ui_tests.py b/taskcluster/android_taskgraph/transforms/ui_tests.py index 182c189e97554..370c0c570b4d2 100644 --- a/taskcluster/android_taskgraph/transforms/ui_tests.py +++ b/taskcluster/android_taskgraph/transforms/ui_tests.py @@ -96,16 +96,14 @@ def apk_path(component_fragment, variant, apk_filename): run = task.setdefault("run", {}) post_gradlew = run.setdefault("post-gradlew", []) - post_gradlew.append( - [ - "python3", - "taskcluster/scripts/tests/test-lab.py", - flank_config, - apk_app, - "--apk_test", - apk_test, - ] - ) + post_gradlew.append([ + "python3", + "taskcluster/scripts/tests/test-lab.py", + flank_config, + apk_app, + "--apk_test", + apk_test, + ]) yield task diff --git a/taskcluster/android_taskgraph/transforms/upstream_artifacts.py b/taskcluster/android_taskgraph/transforms/upstream_artifacts.py index 5c0906136baba..72a45d979a27b 100644 --- a/taskcluster/android_taskgraph/transforms/upstream_artifacts.py +++ b/taskcluster/android_taskgraph/transforms/upstream_artifacts.py @@ -35,23 +35,19 @@ def build_upstream_artifacts(config, tasks): only_archs = task.pop("only-archs", []) for dep in get_dependencies(config, task): paths = list(dep.attributes.get("artifacts", {}).values()) - paths.extend( - [ - apk_metadata["name"] - for arch, apk_metadata in dep.attributes.get("apks", {}).items() - if not only_archs or arch in only_archs - ] - ) + paths.extend([ + apk_metadata["name"] + for arch, apk_metadata in dep.attributes.get("apks", {}).items() + if not only_archs or arch in only_archs + ]) if dep.attributes.get("aab"): paths.extend([dep.attributes.get("aab")]) if paths: - worker_definition["upstream-artifacts"].append( - { - "taskId": {"task-reference": f"<{dep.kind}>"}, - "taskType": _get_task_type(dep.kind), - "paths": sorted(paths), - } - ) + worker_definition["upstream-artifacts"].append({ + "taskId": {"task-reference": f"<{dep.kind}>"}, + "taskType": _get_task_type(dep.kind), + "paths": sorted(paths), + }) task.setdefault("worker", {}).update(worker_definition) yield task diff --git a/taskcluster/android_taskgraph/util/group_by.py b/taskcluster/android_taskgraph/util/group_by.py index 2770c056264d4..fdd818917a9be 100644 --- a/taskcluster/android_taskgraph/util/group_by.py +++ b/taskcluster/android_taskgraph/util/group_by.py @@ -25,13 +25,11 @@ def component_grouping(config, tasks): and task.attributes.get("is_final_chunked_task", True) ] for (_, build_type), group_tasks in groups.items(): - group_tasks.extend( - [ - task - for task in tasks_for_all_components - if task.attributes.get("build-type") == build_type - ] - ) + group_tasks.extend([ + task + for task in tasks_for_all_components + if task.attributes.get("build-type") == build_type + ]) return groups.values() diff --git a/taskcluster/android_taskgraph/util/scriptworker.py b/taskcluster/android_taskgraph/util/scriptworker.py index 9171d09b5603a..8d05199de1d2e 100644 --- a/taskcluster/android_taskgraph/util/scriptworker.py +++ b/taskcluster/android_taskgraph/util/scriptworker.py @@ -115,14 +115,12 @@ def generate_beetmover_upstream_artifacts( if not paths: continue - upstream_artifacts.append( - { - "taskId": {"task-reference": f"<{dep}>"}, - "taskType": map_config["tasktype_map"].get(dep), - "paths": sorted(paths), - "locale": current_locale, - } - ) + upstream_artifacts.append({ + "taskId": {"task-reference": f"<{dep}>"}, + "taskType": map_config["tasktype_map"].get(dep), + "paths": sorted(paths), + "locale": current_locale, + }) upstream_artifacts.sort(key=lambda u: u["paths"]) return upstream_artifacts @@ -271,17 +269,17 @@ def generate_beetmover_artifact_map(config, job, **kwargs): else: folder_prefix = f"{version}-candidates/build{build_number}/android/" - kwargs.update( - {"locale": locale, "version": version, "folder_prefix": folder_prefix} - ) + kwargs.update({ + "locale": locale, + "version": version, + "folder_prefix": folder_prefix, + }) kwargs.update(**platforms) paths = jsone.render(paths, kwargs) - artifacts.append( - { - "taskId": {"task-reference": f"<{dep}>"}, - "locale": locale, - "paths": paths, - } - ) + artifacts.append({ + "taskId": {"task-reference": f"<{dep}>"}, + "locale": locale, + "paths": paths, + }) return artifacts diff --git a/taskcluster/android_taskgraph/worker_types.py b/taskcluster/android_taskgraph/worker_types.py index 5a249116700db..7f8394142ab82 100644 --- a/taskcluster/android_taskgraph/worker_types.py +++ b/taskcluster/android_taskgraph/worker_types.py @@ -53,12 +53,10 @@ def build_scriptworker_beetmover_payload(config, task, task_def): } scope_prefix = config.graph_config["scriptworker"]["scope-prefix"] - task_def["scopes"].extend( - [ - "{}:beetmover:action:{}".format(scope_prefix, worker["action"]), - "{}:beetmover:bucket:{}".format(scope_prefix, worker["bucket"]), - ] - ) + task_def["scopes"].extend([ + "{}:beetmover:action:{}".format(scope_prefix, worker["action"]), + "{}:beetmover:bucket:{}".format(scope_prefix, worker["bucket"]), + ]) @payload_builder( diff --git a/taskcluster/docker/funsize-update-generator/scripts/funsize.py b/taskcluster/docker/funsize-update-generator/scripts/funsize.py index 0fd4a076ce826..3683af82b37f7 100644 --- a/taskcluster/docker/funsize-update-generator/scripts/funsize.py +++ b/taskcluster/docker/funsize-update-generator/scripts/funsize.py @@ -63,7 +63,7 @@ def strtobool(value: str): if value in false_vals: return 0 - raise ValueError(f'Expected one of: {", ".join(true_vals + false_vals)}') + raise ValueError(f"Expected one of: {', '.join(true_vals + false_vals)}") def verify_signature(mar, cert): diff --git a/taskcluster/docker/snap-coreXX-build/snap-tests/basic_tests.py b/taskcluster/docker/snap-coreXX-build/snap-tests/basic_tests.py index b3f0e3b70195b..ddbfc0d5d586c 100644 --- a/taskcluster/docker/snap-coreXX-build/snap-tests/basic_tests.py +++ b/taskcluster/docker/snap-coreXX-build/snap-tests/basic_tests.py @@ -251,9 +251,10 @@ def is_debug_build(self): return self._is_debug_build def need_allow_system_access(self): - geckodriver_output = subprocess.check_output( - [self._EXE_PATH, "--help"] - ).decode() + geckodriver_output = subprocess.check_output([ + self._EXE_PATH, + "--help", + ]).decode() return "--allow-system-access" in geckodriver_output def update_channel(self): @@ -414,16 +415,16 @@ def assert_rendering(self, exp, element_or_driver): (left, upper, right, lower) = bbox assert right >= left, f"Inconsistent boundaries right={right} left={left}" - assert ( - lower >= upper - ), f"Inconsistent boundaries lower={lower} upper={upper}" + assert lower >= upper, ( + f"Inconsistent boundaries lower={lower} upper={upper}" + ) if ((right - left) <= 2) or ((lower - upper) <= 2): self._logger.info("Difference is a <= 2 pixels band, ignoring") return - assert ( - diff_px_on_bbox <= allowance - ), "Mismatching screenshots for {}".format(exp["reference"]) + assert diff_px_on_bbox <= allowance, ( + "Mismatching screenshots for {}".format(exp["reference"]) + ) class SnapTests(SnapTestsBase): @@ -451,9 +452,9 @@ def test_about_support(self, exp): ) self._wait.until(lambda d: len(distributionid_box.text) > 0) self._logger.info(f"about:support distribution ID: {distributionid_box.text}") - assert ( - distributionid_box.text == exp["distribution_id"] - ), "distribution_id should match" + assert distributionid_box.text == exp["distribution_id"], ( + "distribution_id should match" + ) windowing_protocol = self._driver.execute_script( "return document.querySelector('th[data-l10n-id=\"graphics-window-protocol\"').parentNode.lastChild.textContent;" @@ -471,18 +472,18 @@ def test_about_buildconfig(self, exp): ) self._wait.until(lambda d: len(source_link.text) > 0) self._logger.info(f"about:buildconfig source: {source_link.text}") - assert source_link.text.startswith( - exp["source_repo"] - ), "source repo should exists and match" + assert source_link.text.startswith(exp["source_repo"]), ( + "source repo should exists and match" + ) build_flags_box = self._wait.until( EC.visibility_of_element_located((By.CSS_SELECTOR, "p:last-child")) ) self._wait.until(lambda d: len(build_flags_box.text) > 0) self._logger.info(f"about:support buildflags: {build_flags_box.text}") - assert ( - build_flags_box.text.find(exp["official"]) >= 0 - ), "official build flag should be there" + assert build_flags_box.text.find(exp["official"]) >= 0, ( + "official build flag should be there" + ) return True @@ -499,9 +500,10 @@ def test_youtube(self, exp): self._logger.info("Wait for consent form") try: self._wait.until( - EC.visibility_of_element_located( - (By.CSS_SELECTOR, "button[aria-label*=Accept]") - ) + EC.visibility_of_element_located(( + By.CSS_SELECTOR, + "button[aria-label*=Accept]", + )) ).click() except TimeoutException: self._logger.info("Wait for consent form: timed out, maybe it is not here") @@ -513,9 +515,10 @@ def test_youtube(self, exp): self._logger.info("Wait for cable proposal") try: self._wait.until( - EC.visibility_of_element_located( - (By.CSS_SELECTOR, "button[aria-label*=Dismiss]") - ) + EC.visibility_of_element_located(( + By.CSS_SELECTOR, + "button[aria-label*=Dismiss]", + )) ).click() except TimeoutException: self._logger.info( @@ -547,9 +550,9 @@ def test_youtube(self, exp): self._logger.info( "video duration: {}".format(video.get_property("duration")) ) - assert ( - video.get_property("duration") > exp["duration"] - ), "youtube video should have duration" + assert video.get_property("duration") > exp["duration"], ( + "youtube video should have duration" + ) self._wait.until( lambda d: video.get_property("currentTime") > exp["playback"] @@ -557,9 +560,9 @@ def test_youtube(self, exp): self._logger.info( "video played: {}".format(video.get_property("currentTime")) ) - assert ( - video.get_property("currentTime") > exp["playback"] - ), "youtube video should perform playback" + assert video.get_property("currentTime") > exp["playback"], ( + "youtube video should perform playback" + ) except TimeoutException as ex: self._logger.info("video detection timed out") self._logger.info(f"video: {video}") @@ -579,30 +582,34 @@ def wait_for_enable_drm(self): ) enable_drm_button = self._wait.until( - EC.visibility_of_element_located( - (By.CSS_SELECTOR, ".notification-button[label='Enable DRM']") - ) + EC.visibility_of_element_located(( + By.CSS_SELECTOR, + ".notification-button[label='Enable DRM']", + )) ) self._logger.info("Enabling DRMs") enable_drm_button.click() self._wait.until( - EC.invisibility_of_element_located( - (By.CSS_SELECTOR, ".notification-button[label='Enable DRM']") - ) + EC.invisibility_of_element_located(( + By.CSS_SELECTOR, + ".notification-button[label='Enable DRM']", + )) ) self._logger.info("Installing DRMs") self._wait.until( - EC.visibility_of_element_located( - (By.CSS_SELECTOR, ".infobar[value='drmContentCDMInstalling']") - ) + EC.visibility_of_element_located(( + By.CSS_SELECTOR, + ".infobar[value='drmContentCDMInstalling']", + )) ) self._logger.info("Waiting for DRMs installation to complete") self._longwait.until( - EC.invisibility_of_element_located( - (By.CSS_SELECTOR, ".infobar[value='drmContentCDMInstalling']") - ) + EC.invisibility_of_element_located(( + By.CSS_SELECTOR, + ".infobar[value='drmContentCDMInstalling']", + )) ) self._driver.set_context("content") @@ -620,24 +627,25 @@ def test_youtube_film(self, exp): # Wait for duration to be set to something self._logger.info("Wait for video to start") video = self._wait.until( - EC.visibility_of_element_located( - (By.CSS_SELECTOR, "video.html5-main-video") - ) + EC.visibility_of_element_located(( + By.CSS_SELECTOR, + "video.html5-main-video", + )) ) self._wait.until(lambda d: type(video.get_property("duration")) is float) self._logger.info("video duration: {}".format(video.get_property("duration"))) - assert ( - video.get_property("duration") > exp["duration"] - ), "youtube video should have duration" + assert video.get_property("duration") > exp["duration"], ( + "youtube video should have duration" + ) self._driver.execute_script("arguments[0].click();", video) video.send_keys("k") self._wait.until(lambda d: video.get_property("currentTime") > exp["playback"]) self._logger.info("video played: {}".format(video.get_property("currentTime"))) - assert ( - video.get_property("currentTime") > exp["playback"] - ), "youtube video should perform playback" + assert video.get_property("currentTime") > exp["playback"], ( + "youtube video should perform playback" + ) return True diff --git a/taskcluster/docker/snap-coreXX-build/snap-tests/qa_tests.py b/taskcluster/docker/snap-coreXX-build/snap-tests/qa_tests.py index dbae1906d0b7c..d50b2499f4b9a 100644 --- a/taskcluster/docker/snap-coreXX-build/snap-tests/qa_tests.py +++ b/taskcluster/docker/snap-coreXX-build/snap-tests/qa_tests.py @@ -108,9 +108,10 @@ def _test_audio_playback( self._logger.info("find video") video = self._wait.until( - EC.visibility_of_element_located( - (By.CSS_SELECTOR, video_selector or "video") - ) + EC.visibility_of_element_located(( + By.CSS_SELECTOR, + video_selector or "video", + )) ) self._wait.until(lambda d: type(video.get_property("duration")) is float) assert video.get_property("duration") > 0.0, "