atuq 0.1.0: la receta del envoltorio, derivada de firefox
Primera receta del navegador de la distro (SDD 26). No compila nada: depende de `firefox`, copia su árbol y le pone encima cuatro ficheros. Hash b3:2d6e4dcf. LA CAPA SON CUATRO FICHEROS Y CADA UNO ESTÁ DONDE ESTÁ POR UNA RAZÓN: - `defaults/pref/autoconfig.js` — el único gancho que corre ANTES de que exista un perfil. - `atuq.cfg` — prefs de fábrica + carga del chrome. Con `defaultPref` y no `lockPref`: son valores de arranque, no una cárcel; lo que de verdad se bloquea va en policies.json. - `chrome/atuq.css` — el aspecto. - `distribution/policies.json` — telemetría, updates, primer arranque. POR QUÉ EL CSS NO ES `userChrome.css`: ese fichero vive en el PERFIL y exige que el usuario prenda `toolkit.legacyUserProfileCustomizations.stylesheets`. atuq tiene que verse como atuq en el primer arranque, con un perfil recién creado, sin que nadie prenda nada. El único gancho a nivel de APLICACIÓN es nsIStyleSheetService desde autoconfig — y llegar a él es la razón de `sandbox_enabled = false`. Se registra como USER_SHEET, el mismo nivel de cascada que userChrome, para que el usuario pueda seguir pisándolo. DOS TRAMPAS DEL FORMATO, ESCRITAS EN EL PROPIO FICHERO: - la PRIMERA LÍNEA de un autoconfig se ignora siempre, por diseño (herencia de Netscape). Código en la línea 1 no corre y no da error: el fallo más caro de ese fichero es el que no dice nada. - el CSS va acotado con @-moz-document a browser.xhtml. Una hoja USER sin acotar alcanza CUALQUIER documento chrome —visor de PDF, inspector, diálogos— y un selector como `toolbar` termina pintando ventanas que nadie miró. `extensions.autoDisableScopes = 0` es la otra mitad del `--with-unsigned-addon-scopes` que ya viaja en el build de firefox: sin los dos, las extensiones de la distro se instalan DESACTIVADAS. LAS ASERCIONES VAN PRIMERO Y FALLAN RUIDOSAS. Un derivado que no encuentra su base produciría un directorio con cuatro ficheros de config, `Store::has` lo daría por presente y el fallo aparecería el día que alguien abra el navegador (regla 3 del CLAUDE.md). Y al final imprime el inventario de la capa con sus tamaños, que es la primera pregunta del diagnóstico cuando «atuq se ve como Firefox». LO QUE LA v0.1 NO HACE, A PROPÓSITO: no renombra el binario ni toca application.ini, y no re-empaqueta omni.ja. Las dos cosas necesitan mirar la forma real del árbol que selle `firefox`, y adivinarla desde acá daría un artefacto que arranca en una máquina y en ninguna otra. No se puede construir todavía: su dep `firefox` está en vuelo.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# atuq 0.1.0 — el navegador de la distro, como ARTEFACTO DERIVADO de firefox.
|
||||
#
|
||||
# ══ POR QUÉ DERIVADO Y NO UN FORK DE FUENTE ════════════════════════════════════════════════════
|
||||
# Zen —el fork de Firefox al que esto reemplaza en nuestro catálogo— parchea el árbol de Gecko y
|
||||
# recompila. Nosotros no distribuimos un binario: sellamos artefactos. Entonces atuq puede DEPENDER
|
||||
# de `firefox` y poner su capa encima, y eso cambia el precio de todo:
|
||||
# · una iteración de chrome cuesta SEGUNDOS, no las cuatro horas de un build de Gecko;
|
||||
# · no mueve el `ArtifactHash` de `firefox` ⇒ el corpus no se invalida;
|
||||
# · las CVE se heredan gratis: cuando sube `firefox`, atuq se reconstruye solo.
|
||||
# El razonamiento completo, con lo que se promete y lo que NO (spoiler: no habrá «modo Tor»), está
|
||||
# en `docs/26-atuq-envoltorio-gecko.md`.
|
||||
#
|
||||
# ══ SU FUENTE ES ESTE REPO ═════════════════════════════════════════════════════════════════════
|
||||
# `source.dir` es el modo de fuente que se agregó para esto (hammer-core/src/recipe.rs): el árbol
|
||||
# vive en `recipes/atuq/` y se hashea por CONTENIDO con `ArtifactHash::of_tree`, igual que ya se
|
||||
# hacía con los `patches`. No hay commit que pinear y no hay fetch: editar un CSS mueve el hash.
|
||||
#
|
||||
# ══ LO QUE ESTA v0.1 NO HACE, A PROPÓSITO ══════════════════════════════════════════════════════
|
||||
# No renombra el binario ni toca `application.ini`. El branding necesita mirar la forma real del
|
||||
# árbol que sella `firefox` —qué es `firefox-bin`, cómo se resuelve el appdir— y adivinarlo desde
|
||||
# acá produciría un artefacto que arranca en la máquina de quien lo escribió y en ninguna otra.
|
||||
# Tampoco re-empaqueta `omni.ja`, que es donde vive el chrome de verdad; eso es la v0.2 y trae dos
|
||||
# condiciones ya sabidas (re-empacar determinista y preservar el orden del `jarlog` del PGO).
|
||||
name = "atuq"
|
||||
version = "0.1.0"
|
||||
# El artefacto CONTIENE Firefox, así que hereda su licencia. El overlay de `recipes/atuq/` es
|
||||
# nuestro, pero eso no cambia lo que se distribuye.
|
||||
license = "MPL-2.0"
|
||||
|
||||
[source]
|
||||
dir = "atuq"
|
||||
|
||||
[build]
|
||||
# No se compila nada: esta receta copia y edita ficheros. `compiler` y `link` entran igual en el
|
||||
# hash, así que se dejan en el default del corpus para no inventar una identidad que no significa
|
||||
# nada.
|
||||
target = "x86_64-linux-musl"
|
||||
|
||||
[build.phases]
|
||||
configure = "echo 'atuq: nada que configurar — es un artefacto derivado'"
|
||||
compile = "echo 'atuq: nada que compilar — el motor lo pone la dep firefox'"
|
||||
install = '''
|
||||
set -e
|
||||
SRC=/usr/lib/firefox
|
||||
DST=/out/usr/lib/atuq
|
||||
|
||||
# ── LAS ASERCIONES VAN PRIMERO, Y FALLAN RUIDOSAS ─────────────────────────────────────────────
|
||||
# Un derivado que no encuentra su base no debe producir un artefacto flaco: produciría un
|
||||
# directorio con cuatro ficheros de configuración, `Store::has` lo daría por presente y el fallo
|
||||
# aparecería el día que alguien intente abrir el navegador. Un ausente falla fuerte; un vacío llega
|
||||
# hasta el final diciendo que todo fue bien (regla 3 del CLAUDE.md).
|
||||
[ -d "$SRC" ] || { echo "atuq: no está el árbol de firefox en $SRC — ¿la dep se materializó?" >&2; exit 1; }
|
||||
[ -x "$SRC/firefox" ] || { echo "atuq: $SRC/firefox no existe o no es ejecutable" >&2; exit 1; }
|
||||
[ -d "$SRC/browser" ] || { echo "atuq: falta $SRC/browser — el árbol no tiene la forma esperada" >&2; exit 1; }
|
||||
|
||||
mkdir -p "$DST"
|
||||
cp -a "$SRC"/. "$DST"/
|
||||
|
||||
# ── LA CAPA DE atuq ───────────────────────────────────────────────────────────────────────────
|
||||
# `defaults/pref/` es el único sitio desde el que se puede pedir el autoconfig ANTES de que exista
|
||||
# un perfil; el resto cuelga de ahí.
|
||||
mkdir -p "$DST/defaults/pref" "$DST/chrome" "$DST/distribution"
|
||||
cp /src/prefs/autoconfig.js "$DST/defaults/pref/autoconfig.js"
|
||||
cp /src/atuq.cfg "$DST/atuq.cfg"
|
||||
cp /src/chrome/atuq.css "$DST/chrome/atuq.css"
|
||||
cp /src/distribution/policies.json "$DST/distribution/policies.json"
|
||||
|
||||
# El comando que teclea la gente. Apunta al binario SIN renombrar (ver la nota de la cabecera):
|
||||
# renombrarlo es branding y branding es la v0.2.
|
||||
mkdir -p /out/usr/bin
|
||||
ln -sf ../lib/atuq/firefox /out/usr/bin/atuq
|
||||
|
||||
# ── INVENTARIO: LO QUE SE MIRA CUANDO «ATUQ SE VE COMO FIREFOX» ───────────────────────────────
|
||||
# Los cuatro ficheros de la capa son el 100% de lo que distingue este artefacto de su base, y son
|
||||
# los que un `cp` silencioso podría no haber puesto. Se listan para que el log del build responda
|
||||
# solo la primera pregunta del diagnóstico.
|
||||
echo "atuq: capa aplicada —"
|
||||
for f in defaults/pref/autoconfig.js atuq.cfg chrome/atuq.css distribution/policies.json; do
|
||||
[ -s "$DST/$f" ] || { echo "atuq: $f quedó VACÍO o ausente" >&2; exit 1; }
|
||||
echo " $f ($(wc -c < "$DST/$f") bytes)"
|
||||
done
|
||||
'''
|
||||
|
||||
[deps]
|
||||
# `firefox` como dep de BUILD y no de runtime: su árbol se materializa en el sandbox, se copia
|
||||
# dentro del artefacto de atuq y a partir de ahí atuq no lo necesita más. El precio es que el
|
||||
# artefacto pesa lo que pesa Firefox otra vez; la salida barata (una granja de symlinks) rompe la
|
||||
# resolución del appdir por /proc/self/exe, así que la v0.1 paga el disco y lo dice.
|
||||
build = ["firefox"]
|
||||
@@ -0,0 +1,33 @@
|
||||
# atuq — el envoltorio Gecko de la distro
|
||||
|
||||
`atuq` (quechua: *zorro*) es el navegador de la distro: **un artefacto DERIVADO de `firefox`**, no un
|
||||
fork de su fuente. El diseño completo está en [`docs/26-atuq-envoltorio-gecko.md`](../../docs/26-atuq-envoltorio-gecko.md).
|
||||
|
||||
Este directorio ES la fuente de atuq — `recipes/atuq.toml` lo declara con `source.dir` y hammer lo
|
||||
hashea por CONTENIDO. Editar un fichero de acá mueve el `ArtifactHash`; no hay commit que pinear.
|
||||
|
||||
## Qué hay acá y dónde cae
|
||||
|
||||
| Acá | En el artefacto | Qué hace |
|
||||
|---|---|---|
|
||||
| `prefs/autoconfig.js` | `defaults/pref/autoconfig.js` | Le dice a Gecko que lea `atuq.cfg`. Es el único gancho posible antes de que arranque el perfil. |
|
||||
| `atuq.cfg` | `<appdir>/atuq.cfg` | Prefs de fábrica + carga la hoja de estilo del chrome. |
|
||||
| `chrome/atuq.css` | `<appdir>/chrome/atuq.css` | El aspecto. Se inyecta como AGENT_SHEET desde `atuq.cfg`. |
|
||||
| `distribution/policies.json` | `<appdir>/distribution/policies.json` | Política de fábrica: telemetría, updates, scope de extensiones. |
|
||||
|
||||
## Por qué el CSS no es `userChrome.css`
|
||||
|
||||
`userChrome.css` vive en el PERFIL del usuario y exige que el usuario prenda
|
||||
`toolkit.legacyUserProfileCustomizations.stylesheets`. Una distro no puede depender de eso: el
|
||||
navegador tiene que verse como atuq la primera vez que arranca, con un perfil recién creado. La vía
|
||||
que sí funciona a nivel de aplicación es el `nsIStyleSheetService` desde autoconfig, que es lo que
|
||||
hace `atuq.cfg`.
|
||||
|
||||
## Lo que TODAVÍA no está
|
||||
|
||||
- **Branding** (nombre, iconos, `application.ini`): v0.2. Necesita mirar el artefacto real de
|
||||
`firefox` para no adivinar la forma del árbol.
|
||||
- **Re-empaque de `omni.ja`**: es donde va el chrome de verdad (split view, dientes propios). Trae
|
||||
dos condiciones ya sabidas: re-empacar determinista, y **preservar el orden del `jarlog`** cuando
|
||||
el PGO exista, o se tira a la basura la optimización de arranque.
|
||||
- **La extensión de `sct`** y el host de native messaging (SDD 26 §6.1 y §7).
|
||||
@@ -0,0 +1,66 @@
|
||||
// ⚠ LA PRIMERA LÍNEA DE UN FICHERO AUTOCONFIG SE IGNORA — SIEMPRE, POR DISEÑO.
|
||||
// El parser de Gecko salta la línea 1 sin mirarla (herencia del formato de Netscape). Si acá
|
||||
// hubiera código en vez de este comentario, no correría y no habría ningún error: el fallo más caro
|
||||
// de este fichero es el que no dice nada. Por eso la línea 1 es, deliberadamente, un comentario.
|
||||
|
||||
// ── PREFS DE FÁBRICA ───────────────────────────────────────────────────────────────────────────
|
||||
// `defaultPref` y no `lockPref`: son valores de arranque, no una cárcel. Una distro que decide POR
|
||||
// el usuario y no lo deja cambiar de idea es exactamente lo que atuq no quiere ser. Lo que sí se
|
||||
// bloquea con política de verdad va en `distribution/policies.json`.
|
||||
|
||||
// Los dientes: pestañas verticales, que Firefox trae nativas desde la 133 y vienen apagadas.
|
||||
defaultPref("sidebar.revamp", true);
|
||||
defaultPref("sidebar.verticalTabs", true);
|
||||
|
||||
// La distro se actualiza con hammer. Un navegador que se auto-actualiza por su cuenta rompería el
|
||||
// invariante entero: el binario dejaría de ser el que el artefacto dice que es.
|
||||
defaultPref("app.update.auto", false);
|
||||
defaultPref("browser.shell.checkDefaultBrowser", false);
|
||||
|
||||
// Telemetría. No es «apagar una casilla»: es que la distro no reporta, punto. Lo verificable de
|
||||
// verdad —que la clausura no tenga a dónde reportar— es otro frente (SDD 26 §6); esto es el suelo.
|
||||
defaultPref("datareporting.healthreport.uploadEnabled", false);
|
||||
defaultPref("datareporting.policy.dataSubmissionEnabled", false);
|
||||
defaultPref("toolkit.telemetry.enabled", false);
|
||||
defaultPref("toolkit.telemetry.unified", false);
|
||||
defaultPref("toolkit.telemetry.archive.enabled", false);
|
||||
|
||||
// Extensiones que la distro deja en `distribution/extensions/`: sin esto Gecko las instala
|
||||
// DESACTIVADAS y el usuario ve una lista de complementos apagados que nunca pidió apagar.
|
||||
// El otro lado de esto es `--with-unsigned-addon-scopes=app,system`, que va en el CONFIGURE de
|
||||
// `recipes/firefox.toml` y por eso no se puede resolver desde acá.
|
||||
defaultPref("extensions.autoDisableScopes", 0);
|
||||
|
||||
// ── EL CHROME ──────────────────────────────────────────────────────────────────────────────────
|
||||
// Por qué acá y no en `userChrome.css`: ese fichero vive en el PERFIL y exige que el usuario prenda
|
||||
// `toolkit.legacyUserProfileCustomizations.stylesheets`. atuq tiene que verse como atuq en el
|
||||
// primer arranque, con un perfil recién creado, sin que nadie prenda nada. El único gancho a nivel
|
||||
// de APLICACIÓN es nsIStyleSheetService, y llegar a él es la razón de `sandbox_enabled = false`.
|
||||
//
|
||||
// USER_SHEET y no AGENT_SHEET: es el mismo nivel de cascada que usa `userChrome.css`, así que lo
|
||||
// que se escriba en `chrome/atuq.css` se comporta como la gente espera y el usuario puede seguir
|
||||
// pisándolo con el suyo. AGENT_SHEET ganaría siempre y además alcanzaría al CONTENIDO de las
|
||||
// páginas, que no es lo que queremos tocar.
|
||||
try {
|
||||
const { classes: Cc, interfaces: Ci } = Components;
|
||||
const sss = Cc["@mozilla.org/content/style-sheet-service;1"]
|
||||
.getService(Ci.nsIStyleSheetService);
|
||||
const ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
|
||||
const dirs = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||
// "GreD" = el directorio de la aplicación (donde vive el binario). En una instalación normal es
|
||||
// el mismo que "XCurProcD"; se usa GreD porque es el que no cambia si algún día atuq se lanza
|
||||
// con un `-app`.
|
||||
const css = dirs.get("GreD", Ci.nsIFile);
|
||||
css.append("chrome");
|
||||
css.append("atuq.css");
|
||||
if (css.exists()) {
|
||||
const uri = ios.newFileURI(css);
|
||||
if (!sss.sheetRegistered(uri, sss.USER_SHEET)) {
|
||||
sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Un fallo acá NO debe impedir que el navegador arranque: sin estilo es feo, sin navegador no hay
|
||||
// nada. Queda en la consola de errores, que es donde se mira cuando «atuq se ve como Firefox».
|
||||
Components.utils.reportError("atuq.cfg: no pude registrar chrome/atuq.css: " + e);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* atuq.css — el aspecto de atuq. Se registra como USER_SHEET desde `atuq.cfg`.
|
||||
*
|
||||
* ⚠ TODO lo de acá va acotado con @-moz-document a la ventana del navegador. Una hoja de nivel
|
||||
* USER sin acotar alcanza CUALQUIER documento chrome —el visor de PDF, el inspector, los diálogos—
|
||||
* y un selector genérico como `toolbar` termina pintando ventanas que nadie miró nunca.
|
||||
*
|
||||
* v0.1: deliberadamente conservador. El chrome de verdad (dientes propios, split view) vive en el
|
||||
* re-empaque de `omni.ja`, que es la v0.2 y necesita el artefacto real de firefox delante.
|
||||
*/
|
||||
|
||||
@-moz-document url("chrome://browser/content/browser.xhtml") {
|
||||
:root {
|
||||
/* El acento de la distro. Un solo token para que el resto cuelgue de acá y no haya seis
|
||||
* literales de color desperdigados que se desincronizan en la primera edición. */
|
||||
--atuq-acento: #b5651d;
|
||||
--atuq-radio: 8px;
|
||||
}
|
||||
|
||||
/* La pestaña activa: identidad visible sin reescribir la barra entera. */
|
||||
#tabbrowser-tabs .tabbrowser-tab[selected] .tab-background {
|
||||
border-radius: var(--atuq-radio);
|
||||
box-shadow: inset 0 -2px 0 0 var(--atuq-acento);
|
||||
}
|
||||
|
||||
/* La barra de direcciones enfocada usa el acento en vez del azul de Firefox. */
|
||||
#urlbar[focused="true"] > #urlbar-background {
|
||||
outline-color: var(--atuq-acento);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"policies": {
|
||||
"DisableTelemetry": true,
|
||||
"DisableFirefoxStudies": true,
|
||||
"DisableAppUpdate": true,
|
||||
"DontCheckDefaultBrowser": true,
|
||||
"DisableProfileImport": false,
|
||||
"OverrideFirstRunPage": "",
|
||||
"OverridePostUpdatePage": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Le dice a Gecko que lea `atuq.cfg` del directorio de la aplicación.
|
||||
//
|
||||
// `general.config.obscure_value = 0` porque el formato histórico esperaba el fichero "ofuscado"
|
||||
// con un ROT-13 que no protege nada; ponerlo en 0 es lo que permite que `atuq.cfg` sea texto plano
|
||||
// legible y revisable, que es lo que queremos para una política de distro.
|
||||
// `sandbox_enabled = false` da acceso a los componentes XPCOM desde el .cfg: sin eso no se puede
|
||||
// registrar la hoja de estilo del chrome, que es la razón principal de que este fichero exista.
|
||||
pref("general.config.filename", "atuq.cfg");
|
||||
pref("general.config.obscure_value", 0);
|
||||
pref("general.config.sandbox_enabled", false);
|
||||
Reference in New Issue
Block a user