tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit b3b5d4ab09d0073a31fc517d599141f9b5776a89
parent fd161a24aefc27e6c8997f2f4601d465a1d521d3
Author: Florian Zia <fzia@mozilla.com>
Date:   Fri, 12 Dec 2025 13:00:56 +0000

Bug 2000992 - Add moz-src:/// URI support to storybook loaders r=tgiles

Differential Revision: https://phabricator.services.mozilla.com/D275607

Diffstat:
Mbrowser/components/storybook/.storybook/main.js | 13++++++++++---
Mbrowser/components/storybook/.storybook/moz-styles-loader.js | 121++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mbrowser/components/storybook/.storybook/moz-uri-utils.js | 19++++++++++++++++++-
3 files changed, 117 insertions(+), 36 deletions(-)

diff --git a/browser/components/storybook/.storybook/main.js b/browser/components/storybook/.storybook/main.js @@ -5,7 +5,7 @@ const path = require("path"); const webpack = require("webpack"); -const rewriteChromeUri = require("./chrome-uri-utils.js"); +const { rewriteChromeUri, rewriteMozSrcUri } = require("./moz-uri-utils.js"); const mdIndexer = require("./markdown-story-indexer.js"); const projectRoot = path.resolve(__dirname, "../../../../"); @@ -105,6 +105,13 @@ module.exports = { }) ); + config.plugins.push( + // Rewrite moz-src:/// URI imports to file system paths. + new webpack.NormalModuleReplacementPlugin(/^moz-src:\/\/\//, resource => { + resource.request = rewriteMozSrcUri(resource.request); + }) + ); + config.module.rules.push({ test: /\.ftl$/, type: "asset/source", @@ -113,12 +120,12 @@ module.exports = { config.module.rules.push({ test: /\.m?js$/, exclude: /\.storybook/, - use: [{ loader: path.resolve(__dirname, "./chrome-styles-loader.js") }], + use: [{ loader: path.resolve(__dirname, "./moz-styles-loader.js") }], }); // Replace the default CSS rule with a rule to emit a separate CSS file and // export the URL. This allows us to rewrite the source to use CSS imports - // via the chrome-styles-loader. + // via the moz-styles-loader. let cssFileTest = /\.css$/.toString(); let cssRuleIndex = config.module.rules.findIndex( rule => rule.test.toString() === cssFileTest diff --git a/browser/components/storybook/.storybook/moz-styles-loader.js b/browser/components/storybook/.storybook/moz-styles-loader.js @@ -4,10 +4,11 @@ /* eslint-env node */ /** - * This file contains a webpack loader which rewrites JS source files to use CSS - * imports when running in Storybook. This allows JS files loaded in Storybook to use - * chrome:// URIs when loading external stylesheets without having to worry - * about Storybook being able to find and detect changes to the files. + * This file contains a webpack loader which rewrites JS source files to use + * CSS imports when running in Storybook. This allows JS files loaded in + * Storybook to use chrome:// and moz-src:/// URIs when loading external + * stylesheets without having to worry about Storybook being able to find and + * detect changes to the files. * * This loader allows Lit-based custom element code like this to work with * Storybook: @@ -54,53 +55,109 @@ * </template> * `; * } + * + * For moz-src:/// URIs the path is resolved relative to the importing file: + * + * render() { + * return html` + * <link rel="stylesheet" href="moz-src:///third_party/js/prosemirror/prosemirror-view/style/prosemirror.css" /> + * ... + * `; + * } + * + * Gets rewritten to: + * + * import prosemirrorStyles from "../../../../third_party/js/prosemirror/prosemirror-view/style/prosemirror.css"; + * ... + * render() { + * return html` + * <link rel="stylesheet" href=${prosemirrorStyles} /> + * ... + * `; + * } */ const path = require("path"); -const projectRoot = path.join(process.cwd(), "../../.."); -const rewriteChromeUri = require("./chrome-uri-utils.js"); +const projectRoot = path.resolve(__dirname, "../../../../"); +const { rewriteChromeUri, rewriteMozSrcUri } = require("./moz-uri-utils.js"); /** - * Return an array of the unique chrome:// CSS URIs referenced in this file. + * Return an array of the unique chrome:// and moz-src:/// CSS URIs referenced in this file. * * @param {string} source - The source file to scan. - * @returns {string[]} Unique list of chrome:// CSS URIs + * @returns {string[]} Unique list of chrome:// and moz-src:/// CSS URIs */ -function getReferencedChromeUris(source) { - const chromeRegex = /chrome:\/\/.*?\.css/g; +function getReferencedCssUris(source) { + const cssRegexes = [/chrome:\/\/.*?\.css/g, /moz-src:\/\/\/.*?\.css/g]; const matches = new Set(); - for (let match of source.matchAll(chromeRegex)) { - // Add the full URI to the set of matches. - matches.add(match[0]); + for (let regex of cssRegexes) { + for (let match of source.matchAll(regex)) { + // Add the full URI to the set of matches. + matches.add(match[0]); + } } return [...matches]; } /** - * Replace references to chrome:// URIs with the relative path on disk from the - * project root. + * Resolve a CSS URI to a local path and its absolute dependency path. + * + * @param {string} cssUri - The CSS URI to resolve. + * @param {string} resourcePath - The path of the file. + * @returns {{localPath: string, dependencyPath: string}} The local relative path and absolute dependency path. + */ +function resolveCssUri(cssUri, resourcePath) { + let localPath = ""; + let dependencyPath = ""; + + if (cssUri.startsWith("chrome://")) { + localPath = rewriteChromeUri(cssUri); + if (localPath) { + dependencyPath = path.join(projectRoot, localPath); + } + } + if (cssUri.startsWith("moz-src:///")) { + const absolutePath = rewriteMozSrcUri(cssUri); + if (absolutePath) { + localPath = path.relative(path.dirname(resourcePath), absolutePath); + // Ensure the path is treated as a relative file and not a package when imported. + if (!localPath.startsWith(".")) { + localPath = `./${localPath}`; + } + dependencyPath = absolutePath; + } + } + + return { localPath, dependencyPath }; +} + +/** + * Replace references to chrome:// and moz-src:/// URIs with the relative path + * on disk from the project root. * * @this {WebpackLoader} https://webpack.js.org/api/loaders/ * @param {string} source - The source file to update. * @returns {string} The updated source. */ -async function rewriteChromeUris(source) { - const chromeUriToLocalPath = new Map(); - // We're going to rewrite the chrome:// URIs, find all referenced URIs. - let chromeDependencies = getReferencedChromeUris(source); - for (let chromeUri of chromeDependencies) { - let localRelativePath = rewriteChromeUri(chromeUri); - if (localRelativePath) { - localRelativePath = localRelativePath.replaceAll("\\", "/"); - // Store the mapping to a local path for this chrome URI. - chromeUriToLocalPath.set(chromeUri, localRelativePath); +async function rewriteCssUris(source) { + const cssUriToLocalPath = new Map(); + // We're going to rewrite the chrome:// and moz-src:/// URIs, find all referenced URIs. + let cssDependencies = getReferencedCssUris(source); + for (let cssUri of cssDependencies) { + const { localPath, dependencyPath } = resolveCssUri( + cssUri, + this.resourcePath + ); + if (localPath) { + // Store the mapping to a local path for this URI. + cssUriToLocalPath.set(cssUri, localPath); // Tell webpack the file being handled depends on the referenced file. - this.addMissingDependency(path.join(projectRoot, localRelativePath)); + this.addMissingDependency(dependencyPath); } } - // Rewrite the source file with mapped chrome:// URIs. + // Rewrite the source file with mapped chrome:// and moz-src:/// URIs. let rewrittenSource = source; - for (let [chromeUri, localPath] of chromeUriToLocalPath.entries()) { + for (let [cssUri, localPath] of cssUriToLocalPath.entries()) { // Generate an import friendly variable name for the default export from // the CSS file e.g. __chrome_styles_loader__moztoggleStyles. let cssImport = `__chrome_styles_loader__${path @@ -112,10 +169,10 @@ async function rewriteChromeUris(source) { path.basename(this.resourcePath) == "moz-label.mjs" || this.resourcePath.endsWith(".js") ) { - rewrittenSource = rewrittenSource.replaceAll(`"${chromeUri}"`, cssImport); + rewrittenSource = rewrittenSource.replaceAll(`"${cssUri}"`, cssImport); } else { rewrittenSource = rewrittenSource.replaceAll( - chromeUri, + cssUri, `\$\{${cssImport}\}` ); } @@ -134,12 +191,12 @@ async function rewriteChromeUris(source) { * @param {Map} sourceMap - Source map data, unused. * @param {object} meta - Metadata, unused. */ -module.exports = async function chromeUriLoader(source) { +module.exports = async function mozUriLoader(source) { // Get a callback to tell webpack when we're done. const callback = this.async(); // Rewrite the source async since that appears to be preferred (and will be // necessary once we support rewriting CSS/SVG/etc). - const newSource = await rewriteChromeUris.call(this, source); + const newSource = await rewriteCssUris.call(this, source); // Give webpack the rewritten content. callback(null, newSource); }; diff --git a/browser/components/storybook/.storybook/moz-uri-utils.js b/browser/components/storybook/.storybook/moz-uri-utils.js @@ -3,7 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* eslint-env node */ +const path = require("path"); const [prefixMap, aliasMap, sourceMap] = require("./chrome-map.js"); +const projectRoot = path.resolve(__dirname, "../../../../"); function rewriteChromeUri(uri) { if (uri in aliasMap) { @@ -27,4 +29,19 @@ function rewriteChromeUri(uri) { return ""; } -module.exports = rewriteChromeUri; +function rewriteMozSrcUri(uri) { + if (!uri.startsWith("moz-src:///")) { + return ""; + } + const relativePath = uri.replace(/^moz-src:\/\/\//, ""); + const resolvedPath = path.resolve(projectRoot, relativePath); + if (!resolvedPath.startsWith(projectRoot)) { + return ""; + } + return resolvedPath; +} + +module.exports = { + rewriteChromeUri, + rewriteMozSrcUri, +};