T-386: the engine's JS surface through one shim; T-387 (parts): one component-install script, a derived push list
T-386 (cavekit-engine-update R4b): render/goanna/modules/JihadEngine.jsm (resource://gre/modules/) holds the engine modules the components import (LightweightThemeManager, AddonManager, PermissionsUtils) and the about:addons markup the Personas pane decorates (ids, row anonids and classes, the view controller, the URLs). jihadPersonas.js and jihadBundledAddons.js go through it; check-engine-api.py reads its SURFACE block (one list, not two) and fails a component importing an engine module around it (check 4b); its mozconfig search is one level down (a recursive glob had walked the objdirs for over an hour). test/jihad-engine-shim-test.js passes; device (Enyo): about:addons shows Personas and Uninstall, the Personas view lists the 14 and Default look in use. T-387 (R6), two of three: build/install-components.sh installs every render/goanna/components/*.manifest (now files, not heredocs) with the files it names and the shim, for the device bundle and the desktop dist alike (the bundle is identical: same files, same registrations); push-engine-update.sh derives the engine's loose files from the branch's diff and the objdir's install records (build/engine/engine-loose-files.py): videocontrols.xml, AddonManager.jsm, amContentHandler.js and amInstallTrigger.js now go with XPIProvider.jsm (they did not before), verified on the device. build-goanna.sh stops on a failed mach step (a failed configure had been hidden by an && list). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J67ZH8aYy8uzhgZDZ6Nvx9
This commit is contained in:
1 parent
9d8d0d7c31
commit
e3e2417899
25 files changed
+416
-203
No files matched your search
@@ -50,10 +50,14 @@ fi
|
||||
cd "$SRC"
|
||||
|
||||
# ./mach is a shell/python polyglot that re-execs itself under python3.
|
||||
# Each step's failure stops the script (T-387, 2026-10-18): `./mach configure && ./mach build` hid a
|
||||
# failed configure from set -e (a non-final command of an && list), and the script went on to
|
||||
# install our components into the OLD dist and report "done" over an engine that never built.
|
||||
case "$STAGE" in
|
||||
configure) ./mach configure ;;
|
||||
build) ./mach build ;;
|
||||
all) ./mach configure && ./mach build ;;
|
||||
configure) ./mach configure || { echo "ERROR: mach configure failed"; exit 1; } ;;
|
||||
build) ./mach build || { echo "ERROR: mach build failed"; exit 1; } ;;
|
||||
all) ./mach configure || { echo "ERROR: mach configure failed"; exit 1; }
|
||||
./mach build || { echo "ERROR: mach build failed"; exit 1; } ;;
|
||||
*) echo "unknown stage: $STAGE (use configure|build|all)"; exit 2 ;;
|
||||
esac
|
||||
|
||||
@@ -88,30 +92,17 @@ fi
|
||||
# modal XUL window that cannot open here, and every install is cancelled).
|
||||
PROMPT_JS=/jihad/render/goanna/components/jihadInstallPrompt.js
|
||||
if [ -f "$PROMPT_JS" ] && [ -d "$DISTBIN/components" ]; then
|
||||
cp -L "$PROMPT_JS" "$DISTBIN/components/jihadInstallPrompt.js"
|
||||
cat > "$DISTBIN/components/jihad-install-prompt.manifest" <<'MANIFEST'
|
||||
component {5cf8e2a6-91a1-44f5-9d33-8ab6f2c40d17} jihadInstallPrompt.js
|
||||
contract @mozilla.org/addons/web-install-prompt;1 {5cf8e2a6-91a1-44f5-9d33-8ab6f2c40d17}
|
||||
MANIFEST
|
||||
if ! grep -q '^manifest components/jihad-install-prompt.manifest$' "$DISTBIN/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest components/jihad-install-prompt.manifest' >> "$DISTBIN/chrome.manifest"
|
||||
fi
|
||||
echo " xpi prompt: registered in the desktop dist"
|
||||
fi
|
||||
|
||||
# --- Jihad's own XPCOM components and the engine shim: the device bundle's script (R6, T-387) ----
|
||||
sh /jihad/build/install-components.sh "$DISTBIN" /jihad
|
||||
|
||||
# --- Personas (components/jihadPersonas.js + the bundled set), into the DESKTOP dist -------------
|
||||
# Mirrors make-device-bundle.sh, so about:addons → Personas and about:personas behave as on the
|
||||
# device (cavekit-gre-widgets.md R7; inspector 2026-10-08 found the desktop gallery empty).
|
||||
PERSONAS_JS=/jihad/render/goanna/components/jihadPersonas.js
|
||||
if [ -f "$PERSONAS_JS" ] && [ -d "$DISTBIN/components" ]; then
|
||||
cp -L "$PERSONAS_JS" "$DISTBIN/components/jihadPersonas.js"
|
||||
cat > "$DISTBIN/components/jihad-personas.manifest" <<'MANIFEST'
|
||||
component {6f0d2b1c-7a3e-4b58-9c21-3e9a5d7f1b02} jihadPersonas.js
|
||||
contract @riverstonerelay.net/jihad/personas;1 {6f0d2b1c-7a3e-4b58-9c21-3e9a5d7f1b02}
|
||||
MANIFEST
|
||||
if ! grep -q '^manifest components/jihad-personas.manifest$' "$DISTBIN/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest components/jihad-personas.manifest' >> "$DISTBIN/chrome.manifest"
|
||||
fi
|
||||
rm -rf "$DISTBIN/personas"; mkdir -p "$DISTBIN/personas"
|
||||
cp -L /jihad/render/goanna/personas/personas.json "$DISTBIN/personas/"
|
||||
python3 -c 'import json,sys; [print(p["file"]) for p in json.load(open(sys.argv[1]))["personas"]]' \
|
||||
@@ -126,23 +117,8 @@ fi
|
||||
CERTS_JS=/jihad/render/goanna/components/jihadCerts.js
|
||||
PROXY_JS=/jihad/render/goanna/components/jihadProxy.js
|
||||
if [ -f "$CERTS_JS" ] && [ -f "$PROXY_JS" ] && [ -d "$DISTBIN/components" ]; then
|
||||
cp -L "$CERTS_JS" "$DISTBIN/components/jihadCerts.js"
|
||||
cp -L "$PROXY_JS" "$DISTBIN/components/jihadProxy.js"
|
||||
mkdir -p "$DISTBIN/certs"
|
||||
cp -L /jihad/render/goanna/certs/jihad-cert-lists.json "$DISTBIN/certs/"
|
||||
cat > "$DISTBIN/components/jihad-certs.manifest" <<'MANIFEST'
|
||||
component {3c8e2f1a-5d47-4b9e-a2c6-7f0b1d9e4a63} jihadCerts.js
|
||||
contract @riverstonerelay.net/jihad/certs;1 {3c8e2f1a-5d47-4b9e-a2c6-7f0b1d9e4a63}
|
||||
component {8a1d5e3b-2c94-4f07-b6e1-0d7c3a9f5b28} jihadCerts.js
|
||||
contract @mozilla.org/nsCertificateDialogs;1 {8a1d5e3b-2c94-4f07-b6e1-0d7c3a9f5b28}
|
||||
component {5b7e2c94-1d3a-4f68-8e0b-a49c6d2f7e13} jihadCerts.js
|
||||
contract @riverstonerelay.net/jihad/cert-policy;1 {5b7e2c94-1d3a-4f68-8e0b-a49c6d2f7e13}
|
||||
category content-policy JihadCertPolicy @riverstonerelay.net/jihad/cert-policy;1
|
||||
MANIFEST
|
||||
cat > "$DISTBIN/components/jihad-proxy.manifest" <<'MANIFEST'
|
||||
component {d47b9c02-6e15-4a8f-93b1-5c2e8f0a7d14} jihadProxy.js
|
||||
contract @riverstonerelay.net/jihad/proxy;1 {d47b9c02-6e15-4a8f-93b1-5c2e8f0a7d14}
|
||||
MANIFEST
|
||||
for m in jihad-certs jihad-proxy; do
|
||||
grep -q "^manifest components/$m.manifest$" "$DISTBIN/chrome.manifest" 2>/dev/null || echo "manifest components/$m.manifest" >> "$DISTBIN/chrome.manifest"
|
||||
done
|
||||
@@ -160,19 +136,9 @@ if [ -d "$PREFSUI" ] && [ -f "$ABOUT_JS" ] && [ -d "$DISTBIN/components" ]; then
|
||||
mkdir -p "$DISTBIN/prefsui"
|
||||
cp -R "$PREFSUI/content" "$DISTBIN/prefsui/"
|
||||
cp -L "$PREFSUI/jihad-prefsui.manifest" "$DISTBIN/"
|
||||
cp -L "$ABOUT_JS" "$DISTBIN/components/jihadAboutPreferences.js"
|
||||
cat > "$DISTBIN/components/jihad-about-preferences.manifest" <<'MANIFEST'
|
||||
component {0f2b8f4e-6c31-4a7b-9f2d-5c1e73a4b806} jihadAboutPreferences.js
|
||||
contract @mozilla.org/network/protocol/about;1?what=preferences {0f2b8f4e-6c31-4a7b-9f2d-5c1e73a4b806}
|
||||
contract @mozilla.org/network/protocol/about;1?what=settings {0f2b8f4e-6c31-4a7b-9f2d-5c1e73a4b806}
|
||||
contract @mozilla.org/network/protocol/about;1?what=personas {0f2b8f4e-6c31-4a7b-9f2d-5c1e73a4b806}
|
||||
MANIFEST
|
||||
if ! grep -q '^manifest jihad-prefsui.manifest$' "$DISTBIN/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest jihad-prefsui.manifest' >> "$DISTBIN/chrome.manifest"
|
||||
fi
|
||||
if ! grep -q '^manifest components/jihad-about-preferences.manifest$' "$DISTBIN/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest components/jihad-about-preferences.manifest' >> "$DISTBIN/chrome.manifest"
|
||||
fi
|
||||
for f in jihad-prefsui.manifest prefsui/content/preferences.html prefsui/content/preferences.js \
|
||||
prefsui/content/preferences.css components/jihadAboutPreferences.js \
|
||||
components/jihad-about-preferences.manifest; do
|
||||
|
||||
@@ -59,23 +59,26 @@ IMPLEMENTED = [
|
||||
("xpcom/system/nsIXULRuntime.idl", "nsIXULRuntime"),
|
||||
("xpcom/system/nsIPlatformInfo.idl", "nsIPlatformInfo"),
|
||||
]
|
||||
# Engine JS: file, the names that must still be there.
|
||||
JS_NEEDS = [
|
||||
("toolkit/mozapps/extensions/LightweightThemeManager.jsm",
|
||||
["parseTheme", "previewTheme", "resetPreview", "currentTheme", "usedThemes", "forgetUsedTheme",
|
||||
'"lightweightThemes."', "usedThemes", "lightweight-theme-styling-update", "@personas.mozilla.org"]),
|
||||
("toolkit/mozapps/extensions/AddonManager.jsm",
|
||||
["getInstallForFile", "getAddonByID", "addAddonListener"]),
|
||||
("toolkit/mozapps/extensions/content/extensions.xul",
|
||||
['id="list-view"', 'id="addon-list"', 'id="addon-list-empty"', 'id="addons-page"']),
|
||||
("toolkit/mozapps/extensions/content/extensions.xml",
|
||||
['anonid="enable-btn"', 'anonid="disable-btn"', 'anonid="remove-btn"', 'anonid="version"',
|
||||
'anonid="description"', 'anonid="control-container"', 'class="content-inner-container"',
|
||||
'class="disabled-postfix"']),
|
||||
("toolkit/mozapps/extensions/content/extensions.js",
|
||||
["gViewController", "currentViewId", '"ViewChanged"', "extensions.ui.lastCategory",
|
||||
'"category-" + aId']), # the Personas pane renames #category-theme
|
||||
]
|
||||
# Engine JS: file, the names that must still be there. T-386 (R4b): read from the shim every
|
||||
# component goes through, render/goanna/modules/JihadEngine.jsm (its SURFACE block is JSON), so the
|
||||
# checker checks exactly what the components use.
|
||||
SHIM = os.path.join(REPO, "render", "goanna", "modules", "JihadEngine.jsm")
|
||||
# engine modules a component may import directly: stable for years, not the surface's business
|
||||
DIRECT_OK = {"XPCOMUtils", "Services", "NetUtil", "FileUtils", "Timer", "JihadEngine"}
|
||||
|
||||
|
||||
def shim_surface():
|
||||
with open(SHIM, encoding="utf-8") as f:
|
||||
t = f.read()
|
||||
m = re.search(r"// SURFACE-BEGIN[^\n]*\nconst SURFACE = (\{.*?\});\n// SURFACE-END", t, re.S)
|
||||
if not m:
|
||||
raise SystemExit("check-engine-api: no SURFACE block in " + SHIM)
|
||||
return json.loads(m.group(1))
|
||||
|
||||
|
||||
SURFACE = shim_surface()
|
||||
JS_NEEDS = ([(m["source"], m["needs"]) for m in SURFACE["modules"].values()] +
|
||||
sorted(SURFACE["addons"]["markup"].items()))
|
||||
CLIPBOARD_IDL = "widget/nsIClipboardHelper.idl"
|
||||
# Prefs the project sets that the engine has no default for, on purpose — read by our own code.
|
||||
OURS_ONLY = {
|
||||
@@ -301,6 +304,21 @@ def check(uxp, rep):
|
||||
else:
|
||||
rep.ok("%s (%d names)" % (f, len(needs)))
|
||||
|
||||
print("4b. components reach the engine's modules through JihadEngine.jsm")
|
||||
comp_dir = os.path.join(REPO, "render", "goanna", "components")
|
||||
direct = []
|
||||
for f in sorted(glob.glob(os.path.join(comp_dir, "*.js"))):
|
||||
for n, line in enumerate(read(f).splitlines(), 1):
|
||||
if line.lstrip().startswith(("//", "*", "/*")):
|
||||
continue
|
||||
for mod in re.findall(r"resource://gre/modules/(?:[A-Za-z]+/)*([A-Za-z]+)\.jsm", line):
|
||||
if mod not in DIRECT_OK:
|
||||
direct.append("%s:%d imports %s" % (os.path.basename(f), n, mod))
|
||||
if direct:
|
||||
rep.fail("engine modules imported around the shim: " + "; ".join(direct))
|
||||
else:
|
||||
rep.ok("no component imports an engine module around the shim (%d modules in the surface)" % len(SURFACE["modules"]))
|
||||
|
||||
print("5. engine prefs the project sets")
|
||||
defaults = ""
|
||||
# Default-pref files only — not tests, which mention prefs that no longer exist.
|
||||
@@ -348,7 +366,9 @@ def check(uxp, rep):
|
||||
conf += read(f)
|
||||
except OSError:
|
||||
pass
|
||||
for mc in glob.glob(os.path.join(REPO, "build/**/mozconfig*"), recursive=True):
|
||||
# one level down (build/<dir>/mozconfig*): a recursive glob walked the objdirs under build/
|
||||
# (millions of files) and the check took over an hour (T-386, 2026-10-18)
|
||||
for mc in sorted(glob.glob(os.path.join(REPO, "build", "*", "mozconfig*"))):
|
||||
for opt in re.findall(r"ac_add_options\s+--(?:enable|disable|with|without)-([\w-]+)", read(mc)):
|
||||
if not re.search(r"(enable|disable|with|without)-" + re.escape(opt) + r"\b", conf):
|
||||
rep.fail("%s: option --*-%s is not in the engine's configure" % (os.path.relpath(mc, REPO), opt))
|
||||
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
"""engine-loose-files.py — cavekit-engine-update.md R6 (T-387): the engine's own files that this
|
||||
project's engine branch changes and that ship as LOOSE files next to libxul (script modules,
|
||||
components, chrome bindings), so a libxul push alone would leave them stale on the device.
|
||||
|
||||
Derived, not named: the files the branch changes since the release it sits on
|
||||
(`git diff --name-only UXP_TAG UXP_PIN` in the engine tree), mapped to where the build put them
|
||||
in dist/bin by the objdir's own records: the install manifest (`_build_manifests/install/dist_bin`),
|
||||
the preprocessor's dependency files (`**/.deps/*.pp`, for preprocessed modules such as
|
||||
XPIProvider.jsm) and dist/bin's links into the source (chrome files). Prints the dist-relative
|
||||
paths that also exist in the bundle, one per line.
|
||||
|
||||
engine-loose-files.py --objdir O --uxp TREE --base TAG --head REV [--bundle DIR] [--why]
|
||||
"""
|
||||
import argparse, glob, os, subprocess, sys
|
||||
|
||||
SRC_PREFIX = "/src/uxp/" # the engine tree as the build container mounts it
|
||||
|
||||
|
||||
def changed_files(uxp, base, head):
|
||||
out = subprocess.check_output(["git", "-C", uxp, "diff", "--name-only", base, head], text=True)
|
||||
return {l.strip() for l in out.splitlines() if l.strip()}
|
||||
|
||||
|
||||
def source_rel(path, uxp):
|
||||
for p in (SRC_PREFIX, os.path.abspath(uxp) + "/"):
|
||||
if path.startswith(p):
|
||||
return path[len(p):]
|
||||
return None
|
||||
|
||||
|
||||
def dist_map(objdir, uxp):
|
||||
"""source path (engine-relative) -> set of dist/bin-relative destinations"""
|
||||
m = {}
|
||||
def add(src, dest):
|
||||
rel = source_rel(src, uxp)
|
||||
if rel:
|
||||
m.setdefault(rel, set()).add(os.path.normpath(dest))
|
||||
# 1. the install manifest: 1 symlink, 2 copy, 7 preprocess: type, dest, source, ...
|
||||
man = os.path.join(objdir, "_build_manifests", "install", "dist_bin")
|
||||
if os.path.exists(man):
|
||||
with open(man, encoding="utf-8", errors="replace") as f:
|
||||
for line in f.read().splitlines()[1:]:
|
||||
p = line.split("\x1f")
|
||||
if len(p) >= 3 and p[0] in ("1", "2", "7"):
|
||||
add(p[2], p[1])
|
||||
# 2. the preprocessor's records: "<target>: <source> <includes...>"
|
||||
distbin = os.path.normpath(os.path.join(objdir, "dist", "bin")) + "/"
|
||||
for pp in glob.glob(os.path.join(objdir, "**", ".deps", "*.pp"), recursive=True):
|
||||
try:
|
||||
first = open(pp, encoding="utf-8", errors="replace").readline()
|
||||
except OSError:
|
||||
continue
|
||||
if ":" not in first:
|
||||
continue
|
||||
target, deps = first.split(":", 1)
|
||||
tgt = os.path.normpath(os.path.join(os.path.dirname(os.path.dirname(pp)), target.strip()))
|
||||
if not tgt.startswith(distbin):
|
||||
continue
|
||||
for d in deps.split():
|
||||
add(d, tgt[len(distbin):])
|
||||
# 3. dist/bin's links into the source (chrome files, jar.mn)
|
||||
root = os.path.join(objdir, "dist", "bin")
|
||||
for dp, dirs, files in os.walk(root):
|
||||
for n in files:
|
||||
fp = os.path.join(dp, n)
|
||||
if os.path.islink(fp):
|
||||
add(os.readlink(fp), os.path.relpath(fp, root))
|
||||
return m
|
||||
|
||||
|
||||
def main():
|
||||
a = argparse.ArgumentParser()
|
||||
a.add_argument("--objdir", required=True)
|
||||
a.add_argument("--uxp", required=True)
|
||||
a.add_argument("--base", required=True)
|
||||
a.add_argument("--head", required=True)
|
||||
a.add_argument("--bundle")
|
||||
a.add_argument("--why", action="store_true", help="also print the source each path comes from")
|
||||
o = a.parse_args()
|
||||
changed = changed_files(o.uxp, o.base, o.head)
|
||||
m = dist_map(o.objdir, o.uxp)
|
||||
if not m:
|
||||
sys.exit("engine-loose-files: no install records in " + o.objdir)
|
||||
out = []
|
||||
for src in sorted(changed):
|
||||
for dest in sorted(m.get(src, ())):
|
||||
if o.bundle and not os.path.exists(os.path.join(o.bundle, dest)):
|
||||
continue
|
||||
out.append((dest, src))
|
||||
for dest, src in out:
|
||||
print(dest + (" <- " + src if o.why else ""))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
# Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
# install-components.sh DEST REPO — cavekit-engine-update.md R6 (T-387): Jihad's own XPCOM
|
||||
# components into an engine dist, the one script both the device bundle (make-device-bundle.sh)
|
||||
# and the desktop dist (build/desktop/build-goanna.sh) use. Every render/goanna/components/*.manifest
|
||||
# is copied with the JS files its "component" lines name and registered from DEST/chrome.manifest
|
||||
# (idempotent); the engine shim the components import goes to DEST/modules/JihadEngine.jsm. A
|
||||
# manifest naming a file that is not there fails the build, not the device.
|
||||
set -eu
|
||||
DEST="${1:?usage: install-components.sh DEST REPO}"
|
||||
SRC="${2:?usage: install-components.sh DEST REPO}/render/goanna"
|
||||
mkdir -p "$DEST/components" "$DEST/modules"
|
||||
touch "$DEST/chrome.manifest"
|
||||
n=0
|
||||
for m in "$SRC"/components/*.manifest; do
|
||||
[ -f "$m" ] || continue
|
||||
name=$(basename "$m")
|
||||
for js in $(awk '$1 == "component" { print $3 }' "$m" | sort -u); do
|
||||
[ -s "$SRC/components/$js" ] || { echo "ERROR: $name names components/$js, which is missing" >&2; exit 1; }
|
||||
cp -L "$SRC/components/$js" "$DEST/components/$js"
|
||||
done
|
||||
cp -L "$m" "$DEST/components/$name"
|
||||
grep -q "^manifest components/$name\$" "$DEST/chrome.manifest" || echo "manifest components/$name" >> "$DEST/chrome.manifest"
|
||||
n=$((n + 1))
|
||||
done
|
||||
[ "$n" -gt 0 ] || { echo "ERROR: no component manifests in $SRC/components" >&2; exit 1; }
|
||||
cp -L "$SRC/modules/JihadEngine.jsm" "$DEST/modules/JihadEngine.jsm"
|
||||
echo " components: $n manifests registered, modules/JihadEngine.jsm"
|
||||
@@ -236,6 +236,10 @@ fi
|
||||
# prompt above) resolving both about: names to chrome://jihad-prefs/content/preferences.html.
|
||||
# chrome://, not resource://, because the page writes prefs and therefore needs the system
|
||||
# principal; content cannot reach it because the module does not set URI_SAFE_FOR_UNTRUSTED_CONTENT.
|
||||
# Jihad's own XPCOM components and the engine shim, by the script the desktop dist uses too
|
||||
# (cavekit-engine-update.md R6): every render/goanna/components/*.manifest with the files it names.
|
||||
# The blocks below add each component's data and check what landed.
|
||||
sh "$REPO/build/install-components.sh" "$OUT" "$REPO"
|
||||
PREFSUI="$REPO/packaging/prefsui"
|
||||
ABOUT_JS="$REPO/render/goanna/components/jihadAboutPreferences.js"
|
||||
if [ -d "$PREFSUI" ] && [ -f "$ABOUT_JS" ]; then
|
||||
@@ -243,23 +247,10 @@ if [ -d "$PREFSUI" ] && [ -f "$ABOUT_JS" ]; then
|
||||
mkdir -p "$OUT/prefsui" "$OUT/components"
|
||||
cp -R "$PREFSUI/content" "$OUT/prefsui/"
|
||||
cp -L "$PREFSUI/jihad-prefsui.manifest" "$OUT/"
|
||||
cp -L "$ABOUT_JS" "$OUT/components/jihadAboutPreferences.js"
|
||||
cat > "$OUT/components/jihad-about-preferences.manifest" <<'MANIFEST'
|
||||
# Jihad Browser — about:preferences / about:settings. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
# Both names resolve to the same document (chrome://jihad-prefs/content/preferences.html):
|
||||
# about:preferences is what Basilisk users type, about:settings is the other name asked for.
|
||||
component {0f2b8f4e-6c31-4a7b-9f2d-5c1e73a4b806} jihadAboutPreferences.js
|
||||
contract @mozilla.org/network/protocol/about;1?what=preferences {0f2b8f4e-6c31-4a7b-9f2d-5c1e73a4b806}
|
||||
contract @mozilla.org/network/protocol/about;1?what=settings {0f2b8f4e-6c31-4a7b-9f2d-5c1e73a4b806}
|
||||
contract @mozilla.org/network/protocol/about;1?what=personas {0f2b8f4e-6c31-4a7b-9f2d-5c1e73a4b806}
|
||||
MANIFEST
|
||||
# Idempotent, same reason as the branding line above.
|
||||
if ! grep -q '^manifest jihad-prefsui.manifest$' "$OUT/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest jihad-prefsui.manifest' >> "$OUT/chrome.manifest"
|
||||
fi
|
||||
if ! grep -q '^manifest components/jihad-about-preferences.manifest$' "$OUT/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest components/jihad-about-preferences.manifest' >> "$OUT/chrome.manifest"
|
||||
fi
|
||||
# Fail the BUILD, not the device: a half-landed package means about:preferences resolves to
|
||||
# nothing, and "unknown protocol" points nowhere near this block.
|
||||
for f in jihad-prefsui.manifest prefsui/content/preferences.html \
|
||||
@@ -290,18 +281,7 @@ fi
|
||||
PROMPT_JS="$REPO/render/goanna/components/jihadInstallPrompt.js"
|
||||
if [ -f "$PROMPT_JS" ]; then
|
||||
mkdir -p "$OUT/components"
|
||||
cp -L "$PROMPT_JS" "$OUT/components/jihadInstallPrompt.js"
|
||||
# Registered from the GRE root manifest, next to the engine's own component manifests.
|
||||
cat > "$OUT/components/jihad-install-prompt.manifest" <<'MANIFEST'
|
||||
# Jihad Browser — XPI web-install confirm. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
# Claims the hook amWebInstallListener checks before falling back to its own modal XUL
|
||||
# window, which cannot open in this headless embedding.
|
||||
component {5cf8e2a6-91a1-44f5-9d33-8ab6f2c40d17} jihadInstallPrompt.js
|
||||
contract @mozilla.org/addons/web-install-prompt;1 {5cf8e2a6-91a1-44f5-9d33-8ab6f2c40d17}
|
||||
MANIFEST
|
||||
if ! grep -q '^manifest components/jihad-install-prompt.manifest$' "$OUT/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest components/jihad-install-prompt.manifest' >> "$OUT/chrome.manifest"
|
||||
fi
|
||||
for f in components/jihadInstallPrompt.js components/jihad-install-prompt.manifest; do
|
||||
[ -s "$OUT/$f" ] || { echo "ERROR: xpi-prompt file missing/empty in bundle: $f" >&2; exit 1; }
|
||||
done
|
||||
@@ -313,15 +293,6 @@ fi
|
||||
PRINT_JS="$REPO/render/goanna/components/jihadPrint.js"
|
||||
if [ -f "$PRINT_JS" ]; then
|
||||
mkdir -p "$OUT/components"
|
||||
cp -L "$PRINT_JS" "$OUT/components/jihadPrint.js"
|
||||
cat > "$OUT/components/jihad-print.manifest" <<'MANIFEST'
|
||||
# Jihad Browser — printing over IPP. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {b7f1a6f0-0c5e-4d0b-9a55-3f6f2f7c1d42} jihadPrint.js
|
||||
contract @riverstonerelay.net/jihad/print;1 {b7f1a6f0-0c5e-4d0b-9a55-3f6f2f7c1d42}
|
||||
MANIFEST
|
||||
if ! grep -q '^manifest components/jihad-print.manifest$' "$OUT/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest components/jihad-print.manifest' >> "$OUT/chrome.manifest"
|
||||
fi
|
||||
for f in components/jihadPrint.js components/jihad-print.manifest; do
|
||||
[ -s "$OUT/$f" ] || { echo "ERROR: print component file missing/empty in bundle: $f" >&2; exit 1; }
|
||||
done
|
||||
@@ -333,15 +304,6 @@ fi
|
||||
PAGECMD_JS="$REPO/render/goanna/components/jihadPageCommands.js"
|
||||
if [ -f "$PAGECMD_JS" ]; then
|
||||
mkdir -p "$OUT/components"
|
||||
cp -L "$PAGECMD_JS" "$OUT/components/jihadPageCommands.js"
|
||||
cat > "$OUT/components/jihad-page-commands.manifest" <<'MANIFEST'
|
||||
# Jihad Browser — the page's half of the long-press menu. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {4e0c1b7a-6a53-4a5e-9d0f-0f6a3d1b8c27} jihadPageCommands.js
|
||||
contract @riverstonerelay.net/jihad/page-commands;1 {4e0c1b7a-6a53-4a5e-9d0f-0f6a3d1b8c27}
|
||||
MANIFEST
|
||||
if ! grep -q '^manifest components/jihad-page-commands.manifest$' "$OUT/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest components/jihad-page-commands.manifest' >> "$OUT/chrome.manifest"
|
||||
fi
|
||||
for f in components/jihadPageCommands.js components/jihad-page-commands.manifest; do
|
||||
[ -s "$OUT/$f" ] || { echo "ERROR: page-commands component file missing/empty in bundle: $f" >&2; exit 1; }
|
||||
done
|
||||
@@ -364,15 +326,6 @@ if [ -f "$BUNDLED_JS" ]; then
|
||||
mkdir -p "$OUT/bundled-addons" "$OUT/components"
|
||||
cp -L "$PALEFILL_XPI" "$OUT/bundled-addons/palefill@addons.martoks-place.de.xpi"
|
||||
cp -L "$REPO/third_party/palefill/README.md" "$OUT/bundled-addons/palefill-README.md"
|
||||
cp -L "$BUNDLED_JS" "$OUT/components/jihadBundledAddons.js"
|
||||
cat > "$OUT/components/jihad-bundled-addons.manifest" <<'MANIFEST'
|
||||
# Jihad Browser — offers the add-ons that come with the browser. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {0b9f6a4e-2f3d-4c6e-8f0a-6d2c9b1e7a55} jihadBundledAddons.js
|
||||
contract @riverstonerelay.net/jihad/bundled-addons;1 {0b9f6a4e-2f3d-4c6e-8f0a-6d2c9b1e7a55}
|
||||
MANIFEST
|
||||
if ! grep -q '^manifest components/jihad-bundled-addons.manifest$' "$OUT/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest components/jihad-bundled-addons.manifest' >> "$OUT/chrome.manifest"
|
||||
fi
|
||||
for f in components/jihadBundledAddons.js components/jihad-bundled-addons.manifest \
|
||||
"bundled-addons/palefill@addons.martoks-place.de.xpi"; do
|
||||
[ -s "$OUT/$f" ] || { echo "ERROR: bundled add-on file missing/empty in bundle: $f" >&2; exit 1; }
|
||||
@@ -399,62 +352,26 @@ done
|
||||
mkdir -p "$OUT/blocklists" "$OUT/components"
|
||||
cp -L "$EASYLIST_DIR/easylist.txt" "$EASYLIST_DIR/easyprivacy.txt" "$OUT/blocklists/"
|
||||
cp -L "$EASYLIST_DIR/README.md" "$OUT/blocklists/README.md"
|
||||
cp -L "$BLOCKER_JS" "$OUT/components/jihadContentBlocker.js"
|
||||
cat > "$OUT/components/jihad-content-blocker.manifest" <<'MANIFEST'
|
||||
# Jihad Browser — ads and trackers blocked in the engine (EasyList, EasyPrivacy). Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {3c7e1b9a-5d24-4f6e-8a0b-9e2d4c6f1a37} jihadContentBlocker.js
|
||||
contract @riverstonerelay.net/jihad/content-blocker;1 {3c7e1b9a-5d24-4f6e-8a0b-9e2d4c6f1a37}
|
||||
category content-policy @riverstonerelay.net/jihad/content-blocker;1 @riverstonerelay.net/jihad/content-blocker;1
|
||||
MANIFEST
|
||||
if ! grep -q '^manifest components/jihad-content-blocker.manifest$' "$OUT/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest components/jihad-content-blocker.manifest' >> "$OUT/chrome.manifest"
|
||||
fi
|
||||
echo " content blocking: components/jihadContentBlocker.js + blocklists/ (EasyList, EasyPrivacy; sha256 verified)"
|
||||
# The browser's identity, adjusted to the page (cavekit-modern-web.md R3): components/jihadUserAgent.js
|
||||
# learns the sites whose bot check turns the page away and gives them a current Firefox identity.
|
||||
UA_JS="$REPO/render/goanna/components/jihadUserAgent.js"
|
||||
[ -f "$UA_JS" ] || { echo "ERROR: $UA_JS is missing" >&2; exit 1; }
|
||||
mkdir -p "$OUT/components"
|
||||
cp -L "$UA_JS" "$OUT/components/jihadUserAgent.js"
|
||||
cat > "$OUT/components/jihad-user-agent.manifest" <<'MANIFEST'
|
||||
# Jihad Browser — the browser's identity, adjusted to the needs of the page. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {8d4f2c6a-1b7e-4e3a-9f5c-2a6d8e0b4c71} jihadUserAgent.js
|
||||
contract @riverstonerelay.net/jihad/user-agent;1 {8d4f2c6a-1b7e-4e3a-9f5c-2a6d8e0b4c71}
|
||||
MANIFEST
|
||||
if ! grep -q '^manifest components/jihad-user-agent.manifest$' "$OUT/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest components/jihad-user-agent.manifest' >> "$OUT/chrome.manifest"
|
||||
fi
|
||||
echo " identity: components/jihadUserAgent.js (52.9 by default; a learned current identity per site)"
|
||||
# Web compatibility shims (cavekit-modern-web.md R1/R6): components/jihadWebCompat.js supplies the
|
||||
# few platform pieces the engine lacks (CSSStyleSheet.replace, popover, inert, Intl.ListFormat …)
|
||||
# to each page before its scripts run; jihad.compat.trace is the diagnosis mode.
|
||||
COMPAT_JS="$REPO/render/goanna/components/jihadWebCompat.js"
|
||||
[ -f "$COMPAT_JS" ] || { echo "ERROR: $COMPAT_JS is missing" >&2; exit 1; }
|
||||
cp -L "$COMPAT_JS" "$OUT/components/jihadWebCompat.js"
|
||||
cat > "$OUT/components/jihad-web-compat.manifest" <<'MANIFEST'
|
||||
# Jihad Browser — web compatibility shims. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {5a1c7e2d-9b3f-4c6e-8d21-7f0a4b9c2e13} jihadWebCompat.js
|
||||
contract @riverstonerelay.net/jihad/web-compat;1 {5a1c7e2d-9b3f-4c6e-8d21-7f0a4b9c2e13}
|
||||
MANIFEST
|
||||
if ! grep -q '^manifest components/jihad-web-compat.manifest$' "$OUT/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest components/jihad-web-compat.manifest' >> "$OUT/chrome.manifest"
|
||||
fi
|
||||
echo " web compat: components/jihadWebCompat.js (shims for what the engine lacks, per page)"
|
||||
[ -s "$OUT/modules/JihadEngine.jsm" ] || { echo "ERROR: the engine shim is missing from the bundle" >&2; exit 1; }
|
||||
# Lightweight themes ("personas" — Firefox 52 / Pale Moon / Basilisk): the browser's half of
|
||||
# the engine's LightweightThemeManager (components/jihadPersonas.js). Required: the kit promises
|
||||
# the feature (cavekit-addons-extensions.md R10).
|
||||
PERSONAS_JS="$REPO/render/goanna/components/jihadPersonas.js"
|
||||
[ -f "$PERSONAS_JS" ] || { echo "ERROR: $PERSONAS_JS is missing" >&2; exit 1; }
|
||||
mkdir -p "$OUT/components"
|
||||
cp -L "$PERSONAS_JS" "$OUT/components/jihadPersonas.js"
|
||||
cat > "$OUT/components/jihad-personas.manifest" <<'MANIFEST'
|
||||
# Jihad Browser — lightweight themes (personas): web install, apply, tell the cards. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {6f0d2b1c-7a3e-4b58-9c21-3e9a5d7f1b02} jihadPersonas.js
|
||||
contract @riverstonerelay.net/jihad/personas;1 {6f0d2b1c-7a3e-4b58-9c21-3e9a5d7f1b02}
|
||||
MANIFEST
|
||||
if ! grep -q '^manifest components/jihad-personas.manifest$' "$OUT/chrome.manifest" 2>/dev/null; then
|
||||
echo 'manifest components/jihad-personas.manifest' >> "$OUT/chrome.manifest"
|
||||
fi
|
||||
# The manager's modules travel with libxul (modules/LightweightThemeManager.jsm,
|
||||
# modules/addons/LightweightThemeImageOptimizer.jsm — both in the dist tree the loop above copied).
|
||||
for f in modules/LightweightThemeManager.jsm modules/addons/LightweightThemeImageOptimizer.jsm; do
|
||||
@@ -476,24 +393,7 @@ done
|
||||
python3 -c 'import json,sys; j=json.load(open(sys.argv[1])); assert j["hpStock"]["count"]>100 and j["mozillaRemoved"]["count"]>50 and len(j["relay"]["sha256"])==64' "$CERT_LISTS" \
|
||||
|| { echo "ERROR: $CERT_LISTS is not a complete list (rebuild it with make-cert-lists.py)" >&2; exit 1; }
|
||||
mkdir -p "$OUT/components" "$OUT/certs"
|
||||
cp -L "$CERTS_JS" "$OUT/components/jihadCerts.js"
|
||||
cp -L "$PROXY_JS" "$OUT/components/jihadProxy.js"
|
||||
cp -L "$CERT_LISTS" "$OUT/certs/jihad-cert-lists.json"
|
||||
cat > "$OUT/components/jihad-certs.manifest" <<'MANIFEST'
|
||||
# Jihad Browser — certificates beyond the engine's own, and the CA question. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {3c8e2f1a-5d47-4b9e-a2c6-7f0b1d9e4a63} jihadCerts.js
|
||||
contract @riverstonerelay.net/jihad/certs;1 {3c8e2f1a-5d47-4b9e-a2c6-7f0b1d9e4a63}
|
||||
component {8a1d5e3b-2c94-4f07-b6e1-0d7c3a9f5b28} jihadCerts.js
|
||||
contract @mozilla.org/nsCertificateDialogs;1 {8a1d5e3b-2c94-4f07-b6e1-0d7c3a9f5b28}
|
||||
component {5b7e2c94-1d3a-4f68-8e0b-a49c6d2f7e13} jihadCerts.js
|
||||
contract @riverstonerelay.net/jihad/cert-policy;1 {5b7e2c94-1d3a-4f68-8e0b-a49c6d2f7e13}
|
||||
category content-policy JihadCertPolicy @riverstonerelay.net/jihad/cert-policy;1
|
||||
MANIFEST
|
||||
cat > "$OUT/components/jihad-proxy.manifest" <<'MANIFEST'
|
||||
# Jihad Browser — following webOS's proxy setting. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {d47b9c02-6e15-4a8f-93b1-5c2e8f0a7d14} jihadProxy.js
|
||||
contract @riverstonerelay.net/jihad/proxy;1 {d47b9c02-6e15-4a8f-93b1-5c2e8f0a7d14}
|
||||
MANIFEST
|
||||
for m in jihad-certs jihad-proxy; do
|
||||
grep -q "^manifest components/$m.manifest$" "$OUT/chrome.manifest" 2>/dev/null || echo "manifest components/$m.manifest" >> "$OUT/chrome.manifest"
|
||||
done
|
||||
|
||||
@@ -76,9 +76,17 @@ PREFS_MD5=$(md5sum "$TMP/goanna.js" | cut -c1-32)
|
||||
say "libxul $(du -h "$TMP/libxul.so" | cut -f1) md5=$XUL_MD5; daemon $(du -h "$TMP/jihad-browserserver" | cut -f1) md5=$BS_MD5; goanna.js md5=$PREFS_MD5"
|
||||
|
||||
# This project's own chrome scripts, relative to the bundle root.
|
||||
# ...and the engine's own script modules that this project's engine branch changes (they are
|
||||
# loose files in the bundle, so a libxul push does not carry them): the add-on manager's provider.
|
||||
OWN="chrome.manifest jihad-bundle.id jihad-prefsui.manifest modules/addons/XPIProvider.jsm modules/addons/XPIProviderUtils.js $(cd "$BUNDLE" && { ls components/jihad* prefsui/content/* bundled-addons/* personas/* certs/* 2>/dev/null || true; } | tr '\n' ' ')"
|
||||
# ...and the engine's own loose files (script modules, components, chrome bindings) that this
|
||||
# project's engine branch changes, which a libxul push does not carry. DERIVED (cavekit-engine-update
|
||||
# R6, T-387): the files the branch changes since its release (UXP_TAG..UXP_PIN) mapped to where the
|
||||
# build put them by the objdir's install records (build/engine/engine-loose-files.py); goanna.js,
|
||||
# which all.js becomes, is pushed above with the prefs.
|
||||
. "$HERE/../engine/engine-version.env"
|
||||
ENGINE_LOOSE=$(python3 "$HERE/../engine/engine-loose-files.py" --objdir "$(dirname "$DIST")" --uxp "$HERE/../../third_party/uxp" \
|
||||
--base "$UXP_TAG" --head "$UXP_PIN" --bundle "$BUNDLE") || die "could not derive the engine's loose files (engine-loose-files.py)"
|
||||
ENGINE_LOOSE=$(echo "$ENGINE_LOOSE" | grep -v '^goanna\.js$' | tr '\n' ' ')
|
||||
say "the engine's loose files this branch changes: ${ENGINE_LOOSE:-none}"
|
||||
OWN="chrome.manifest jihad-bundle.id jihad-prefsui.manifest modules/JihadEngine.jsm $ENGINE_LOOSE $(cd "$BUNDLE" && { ls components/jihad* prefsui/content/* bundled-addons/* personas/* certs/* 2>/dev/null || true; } | tr '\n' ' ')"
|
||||
[ -f "$BUNDLE/chrome.manifest" ] || die "no chrome.manifest in $BUNDLE — run make-device-bundle.sh"
|
||||
[ -f "$BUNDLE/jihad-bundle.id" ] || die "no jihad-bundle.id in $BUNDLE — run make-device-bundle.sh"
|
||||
# The add-on manager's provider (modules/addons/*) belongs to the engine it was staged with.
|
||||
@@ -147,10 +155,10 @@ for v in $VARIANTS; do
|
||||
[ -e "$BUNDLE/personas/$f" ] || { novacom run file://bin/rm -- -f "$HL/personas/$f" >/dev/null 2>&1 || true; say "[$v] removed personas/$f (no longer bundled)"; }
|
||||
done
|
||||
for f in $OWN; do
|
||||
# The add-on manager's provider belongs to the engine it was built with. A daemon-only
|
||||
# push leaves libxul alone, so the provider goes only when the device already runs the
|
||||
# bundle's libxul (else it waits for the next full push with it).
|
||||
case "$f" in modules/addons/*)
|
||||
# The engine's loose files belong to the engine they were built with. A daemon-only push
|
||||
# leaves libxul alone, so they go only when the device already runs the bundle's libxul
|
||||
# (else they wait for the next full push with it).
|
||||
case " $ENGINE_LOOSE " in *" $f "*)
|
||||
# Compared with the BUNDLE's libxul (what the provider was staged with), not dist's:
|
||||
# a bundle staged from an older engine build must not pair its provider with a newer
|
||||
# libxul either. A full push already proved bundle == dist above; a daemon-only push
|
||||
|
||||
@@ -9,3 +9,4 @@ Build site: context/plans/build-site.md
|
||||
| Task | Status | Notes |
|
||||
|------|--------|-------|
|
||||
| T-385 | DONE | `render/goanna/EngineCalls.h` (Tier 81) wraps ElementFromPoint, GetViewportInfo, LoadURI, LoadURIWithOptions, NodesFromRect, ResizeReflowIgnoreOverride, SendKeyEvent, SendMouseEventToWindow, SendTouchEventToWindow. `build/engine/check-engine-calls.sh` (new) fails on a direct call in `render/` (proved by a planted call); fixed `render/goanna/test/embed_load.cpp:290`. 19 call sites. |
|
||||
| T-386 | DONE | `render/goanna/modules/JihadEngine.jsm` (SURFACE JSON + accessors), `components/jihadPersonas.js` and `jihadBundledAddons.js` through it, installed by `make-device-bundle.sh`, `build-goanna.sh`, `push-engine-update.sh`; `check-engine-api.py` derives JS_NEEDS from the surface and adds check 4b; `test/jihad-engine-shim-test.js`; device Enyo about:addons + Personas view verified (screenshots). Not in scope: `packaging/prefsui/content/addons.css` also names about:addons ids (a style sheet, not the components). |
|
||||
@@ -74,8 +74,10 @@ Reference: `docs/ENGINE.md` (the guide), `docs/ENGINE-SOURCE.md` (the MPL record
|
||||
|
||||
### R4b: The engine's JS surface
|
||||
**Acceptance Criteria:**
|
||||
- [ ] The engine modules the components import (LightweightThemeManager, AddonManager) and the
|
||||
about:addons markup the Personas pane decorates are reached through one shim.
|
||||
- [x] The engine modules the components import (LightweightThemeManager, AddonManager) and the
|
||||
about:addons markup the Personas pane decorates are reached through one shim. *(2026-10-18,
|
||||
T-386: `render/goanna/modules/JihadEngine.jsm`; its SURFACE block is also the checker's list;
|
||||
device: about:addons and its Personas view on Enyo.)*
|
||||
**Dependencies:** R4
|
||||
|
||||
### R5: Written for a human
|
||||
|
||||
@@ -727,7 +727,7 @@ Owner: "lay the groundwork for a uxp/goanna update by making our interaction wit
|
||||
| T-383 | check-engine-api.py + baseline | cavekit-engine-update.md | R4 | T-382 | no | M | done |
|
||||
| T-384 | docs/ENGINE.md; ENGINE-SOURCE.md brought up to date | cavekit-engine-update.md | R5 | T-380..T-383 | no | M | done |
|
||||
| T-385 | Wrappers for the positional engine calls | cavekit-engine-update.md | R3 | T-382 | yes | M | DONE 2026-10-18 (the wrappers landed with Tier 81 as `render/goanna/EngineCalls.h`; `build/engine/check-engine-calls.sh` now guards them: the wrapped methods are read from the header and any direct call in `render/` fails it; it found one, `test/embed_load.cpp`'s `LoadURI`, now `engine_calls::LoadURIPlain`, compiled in the desktop container; 19 call sites) |
|
||||
| T-386 | JS shim for engine modules and about:addons markup | cavekit-engine-update.md | R4b | — | yes | M | pending |
|
||||
| T-386 | JS shim for engine modules and about:addons markup | cavekit-engine-update.md | R4b | — | yes | M | DONE 2026-10-18 (`render/goanna/modules/JihadEngine.jsm`, installed as `resource://gre/modules/JihadEngine.jsm` by the bundle, the desktop build and the engine-update push: LightweightThemeManager, AddonManager, PermissionsUtils and about:addons' ids, row anonids/classes, view controller and URLs; `jihadPersonas.js` and `jihadBundledAddons.js` go through it; `check-engine-api.py` reads its SURFACE block (one list) and fails a component importing an engine module around it; `test/jihad-engine-shim-test.js` passes; device (Enyo): about:addons shows "Personas" and "Uninstall", the Personas view lists "the 14 you have" and Default look in use, no errors) |
|
||||
| T-387 | Shared component install; derived push list; desktop tests for the uncovered paths | cavekit-engine-update.md | R6 | T-380 | no | L | pending |
|
||||
|
||||
## Tier 73 — Personas UI on about:addons (2026-10-08; added to the site, not a regeneration)
|
||||
|
||||
+1
-2
@@ -135,6 +135,7 @@ version add-ons are judged against — it follows the engine's compatibility lev
|
||||
|---|---|
|
||||
| `build/engine/update-engine.sh [TAG] / --finish / --abort / --pins` | moves the engine to an upstream release: the queue carried across with a three-way merge, the overlaps listed, the API checked, every pin rewritten (§6) |
|
||||
| `build/engine/check-engine-pins.sh` | every pin and version string agrees with `engine-version.env` |
|
||||
| `render/goanna/modules/JihadEngine.jsm` | the engine's JS surface the components use (modules, about:addons markup), in one place; `check-engine-api.py` checks its SURFACE block and that no component imports an engine module around it |
|
||||
| `build/engine/check-engine-calls.sh` | the positional engine calls go through `render/goanna/EngineCalls.h` only (the wrapped methods are read from the header; a direct call anywhere in `render/` fails it) |
|
||||
| `build/engine/patch-queue.sh --check` | the patch queue reproduces the engine branch exactly |
|
||||
| `build/engine/check-engine-api.py [--uxp TREE]` | what in TREE no longer matches what Jihad uses: our exports (§4.1), IDL signatures and implemented interfaces (§4.2) against `engine-api-baseline.json`, the engine JS and about:addons markup (§4.3), prefs (§4.4), mozconfig options and the ICU file (§4.5). Run it on a candidate before building. |
|
||||
@@ -209,8 +210,6 @@ about:settings open; printing finds the test sink (`render/goanna/test/ipp-sink.
|
||||
|
||||
## 8. Next steps (not done yet)
|
||||
|
||||
- A JS shim for the engine modules the components import (LightweightThemeManager,
|
||||
AddonManager), and for the about:addons markup the Personas pane relies on.
|
||||
- One component-install script shared by `build-goanna.sh` and `make-device-bundle.sh` (their
|
||||
manifest blocks duplicate each other).
|
||||
- Derive `push-engine-update.sh`'s loose-file list from the engine branch's diff instead of
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Jihad Browser — about:preferences / about:settings. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
# Both names resolve to the same document (chrome://jihad-prefs/content/preferences.html):
|
||||
# about:preferences is what Basilisk users type, about:settings is the other name asked for.
|
||||
component {0f2b8f4e-6c31-4a7b-9f2d-5c1e73a4b806} jihadAboutPreferences.js
|
||||
contract @mozilla.org/network/protocol/about;1?what=preferences {0f2b8f4e-6c31-4a7b-9f2d-5c1e73a4b806}
|
||||
contract @mozilla.org/network/protocol/about;1?what=settings {0f2b8f4e-6c31-4a7b-9f2d-5c1e73a4b806}
|
||||
contract @mozilla.org/network/protocol/about;1?what=personas {0f2b8f4e-6c31-4a7b-9f2d-5c1e73a4b806}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Jihad Browser — offers the add-ons that come with the browser. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {0b9f6a4e-2f3d-4c6e-8f0a-6d2c9b1e7a55} jihadBundledAddons.js
|
||||
contract @riverstonerelay.net/jihad/bundled-addons;1 {0b9f6a4e-2f3d-4c6e-8f0a-6d2c9b1e7a55}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Jihad Browser — certificates beyond the engine's own, and the CA question. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {3c8e2f1a-5d47-4b9e-a2c6-7f0b1d9e4a63} jihadCerts.js
|
||||
contract @riverstonerelay.net/jihad/certs;1 {3c8e2f1a-5d47-4b9e-a2c6-7f0b1d9e4a63}
|
||||
component {8a1d5e3b-2c94-4f07-b6e1-0d7c3a9f5b28} jihadCerts.js
|
||||
contract @mozilla.org/nsCertificateDialogs;1 {8a1d5e3b-2c94-4f07-b6e1-0d7c3a9f5b28}
|
||||
component {5b7e2c94-1d3a-4f68-8e0b-a49c6d2f7e13} jihadCerts.js
|
||||
contract @riverstonerelay.net/jihad/cert-policy;1 {5b7e2c94-1d3a-4f68-8e0b-a49c6d2f7e13}
|
||||
category content-policy JihadCertPolicy @riverstonerelay.net/jihad/cert-policy;1
|
||||
@@ -0,0 +1,4 @@
|
||||
# Jihad Browser — ads and trackers blocked in the engine (EasyList, EasyPrivacy). Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {3c7e1b9a-5d24-4f6e-8a0b-9e2d4c6f1a37} jihadContentBlocker.js
|
||||
contract @riverstonerelay.net/jihad/content-blocker;1 {3c7e1b9a-5d24-4f6e-8a0b-9e2d4c6f1a37}
|
||||
category content-policy @riverstonerelay.net/jihad/content-blocker;1 @riverstonerelay.net/jihad/content-blocker;1
|
||||
@@ -0,0 +1,5 @@
|
||||
# Jihad Browser — XPI web-install confirm. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
# Claims the hook amWebInstallListener checks before falling back to its own modal XUL
|
||||
# window, which cannot open in this headless embedding.
|
||||
component {5cf8e2a6-91a1-44f5-9d33-8ab6f2c40d17} jihadInstallPrompt.js
|
||||
contract @mozilla.org/addons/web-install-prompt;1 {5cf8e2a6-91a1-44f5-9d33-8ab6f2c40d17}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Jihad Browser — the page's half of the long-press menu. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {4e0c1b7a-6a53-4a5e-9d0f-0f6a3d1b8c27} jihadPageCommands.js
|
||||
contract @riverstonerelay.net/jihad/page-commands;1 {4e0c1b7a-6a53-4a5e-9d0f-0f6a3d1b8c27}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Jihad Browser — lightweight themes (personas): web install, apply, tell the cards. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {6f0d2b1c-7a3e-4b58-9c21-3e9a5d7f1b02} jihadPersonas.js
|
||||
contract @riverstonerelay.net/jihad/personas;1 {6f0d2b1c-7a3e-4b58-9c21-3e9a5d7f1b02}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Jihad Browser — printing over IPP. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {b7f1a6f0-0c5e-4d0b-9a55-3f6f2f7c1d42} jihadPrint.js
|
||||
contract @riverstonerelay.net/jihad/print;1 {b7f1a6f0-0c5e-4d0b-9a55-3f6f2f7c1d42}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Jihad Browser — following webOS's proxy setting. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {d47b9c02-6e15-4a8f-93b1-5c2e8f0a7d14} jihadProxy.js
|
||||
contract @riverstonerelay.net/jihad/proxy;1 {d47b9c02-6e15-4a8f-93b1-5c2e8f0a7d14}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Jihad Browser — the browser's identity, adjusted to the needs of the page. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {8d4f2c6a-1b7e-4e3a-9f5c-2a6d8e0b4c71} jihadUserAgent.js
|
||||
contract @riverstonerelay.net/jihad/user-agent;1 {8d4f2c6a-1b7e-4e3a-9f5c-2a6d8e0b4c71}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Jihad Browser — web compatibility shims. Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
component {5a1c7e2d-9b3f-4c6e-8d21-7f0a4b9c2e13} jihadWebCompat.js
|
||||
contract @riverstonerelay.net/jihad/web-compat;1 {5a1c7e2d-9b3f-4c6e-8d21-7f0a4b9c2e13}
|
||||
@@ -27,6 +27,8 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
// The engine's modules through one shim (cavekit-engine-update.md R4b)
|
||||
Cu.import("resource://gre/modules/JihadEngine.jsm");
|
||||
|
||||
// What ships. `version` is the version of the bundled FILE (the package's make-device-bundle.sh
|
||||
// pins the file by SHA-256, so this cannot drift from it unnoticed).
|
||||
@@ -94,7 +96,7 @@ function install(AddonManager, b, why) {
|
||||
function offerAll(attempt) {
|
||||
let AddonManager = null;
|
||||
try {
|
||||
AddonManager = Cu.import("resource://gre/modules/AddonManager.jsm", {}).AddonManager;
|
||||
AddonManager = JihadEngine.AddonManager;
|
||||
// Throws until the manager has started; that is the readiness test.
|
||||
AddonManager.getAddonByID(BUNDLED[0].id, function() {});
|
||||
} catch (e) {
|
||||
|
||||
@@ -35,6 +35,9 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
// The engine's modules and about:addons' markup, through one shim (cavekit-engine-update.md R4b)
|
||||
Cu.import("resource://gre/modules/JihadEngine.jsm");
|
||||
const ADDONS = JihadEngine.addons;
|
||||
|
||||
const PREF_CURRENT = "jihad.theme.current";
|
||||
// Where a static theme's images are unpacked (under the profile), served to the theme manager
|
||||
@@ -56,7 +59,7 @@ function notify(category, text) {
|
||||
}
|
||||
|
||||
function manager() {
|
||||
return Cu.import("resource://gre/modules/LightweightThemeManager.jsm", {}).LightweightThemeManager;
|
||||
return JihadEngine.LightweightThemeManager;
|
||||
}
|
||||
|
||||
function hostOf(url) {
|
||||
@@ -797,7 +800,7 @@ function previewAllowed(origin) {
|
||||
// R11); the add-on manager imports it only when an install is checked, so it is imported here too
|
||||
// (once per run; it does nothing after that).
|
||||
try {
|
||||
Cu.import("resource://gre/modules/PermissionsUtils.jsm", {}).PermissionsUtils.importFromPrefs("xpinstall.", "install");
|
||||
JihadEngine.PermissionsUtils.importFromPrefs("xpinstall.", "install");
|
||||
} catch (e) {}
|
||||
try { return Services.perms.testPermission(uri, "install") === Services.perms.ALLOW_ACTION; }
|
||||
catch (e) { return false; }
|
||||
@@ -899,12 +902,12 @@ function themeIdOf(addonId) {
|
||||
const XPI_DIRS = ["/media/internal/downloads", "/media/internal"];
|
||||
const PANE_CSS =
|
||||
"@namespace url(\"" + XUL_NS + "\");\n" +
|
||||
"#addons-page button:not(.button-link) { min-height: 44px !important; min-width: 64px !important; }\n" +
|
||||
"#addons-page .category { min-height: 52px; }\n" +
|
||||
"#addons-page[jihad-themes-view=\"true\"] #addon-list-empty { display: none; }\n" +
|
||||
ADDONS.selector("page") + " button:not(.button-link) { min-height: 44px !important; min-width: 64px !important; }\n" +
|
||||
ADDONS.selector("page") + " .category { min-height: 52px; }\n" +
|
||||
ADDONS.selector("page") + "[jihad-themes-view=\"true\"] " + ADDONS.selector("addonListEmpty") + " { display: none; }\n" +
|
||||
// Only the worn Persona here: the list lives in the gallery, which the card can scroll (the owner,
|
||||
// 2026-10-12: "I cant scroll down in personas in about:addons").
|
||||
"#addons-page[jihad-themes-view=\"true\"] #addon-list > richlistitem:not([active=\"true\"]) { display: none !important; }\n" +
|
||||
ADDONS.selector("page") + "[jihad-themes-view=\"true\"] " + ADDONS.selector("addonList") + " > richlistitem:not([active=\"true\"]) { display: none !important; }\n" +
|
||||
"#jihad-themes-box { padding: 10px 12px 4px; }\n" +
|
||||
"#jihad-themes-box .jihad-themes-intro { margin: 0 0 8px; font-size: 1.05em; }\n" +
|
||||
"#jihad-themes-box .jihad-row { min-height: 64px; padding: 6px 10px; margin: 0 0 8px;\n" +
|
||||
@@ -946,9 +949,7 @@ function isDiscoverDocument(doc) {
|
||||
}
|
||||
|
||||
function isAddonsDocument(doc) {
|
||||
let u = "";
|
||||
try { u = String(doc.documentURI || ""); } catch (e) { return false; }
|
||||
return u.indexOf("about:addons") === 0 || u.indexOf("chrome://mozapps/content/extensions/extensions.xul") === 0;
|
||||
return ADDONS.isDocument(doc);
|
||||
}
|
||||
|
||||
function xul(doc, tag, attrs) {
|
||||
@@ -1439,7 +1440,7 @@ function decorateRow(doc, item, theme, isCurrent) {
|
||||
item.style.setProperty("color", text || "#ffffff", "important");
|
||||
item.style.setProperty("min-height", "96px", "important");
|
||||
item.style.MozBoxOrdinalGroup = isCurrent ? "0" : "1";
|
||||
let anon = function(attr, val) { return doc.getAnonymousElementByAttribute(item, attr, val); };
|
||||
let anon = function(attr, val) { return ADDONS.part(doc, item, attr, val); };
|
||||
// The text sits on a pane in the opposite of its colour, so the name reads over any picture.
|
||||
let pane = anon("class", "content-inner-container");
|
||||
if (pane) {
|
||||
@@ -1487,14 +1488,14 @@ function decorateRow(doc, item, theme, isCurrent) {
|
||||
// the tall page as it pans any other (cavekit-chrome-look.md R3).
|
||||
function growToList(win) {
|
||||
let doc = win.document, root = doc.documentElement;
|
||||
let gVC = win.gViewController;
|
||||
let gVC = ADDONS.viewController(win);
|
||||
// Every view of the manager is laid out at full size inside its deck, shown or not: only the one
|
||||
// shown counts (inspector 2026-10-12).
|
||||
let panel = gVC && gVC.viewPort ? gVC.viewPort.selectedPanel : null;
|
||||
root.style.minHeight = ""; // measured as it lays out without the stretch
|
||||
let need = 0;
|
||||
if (panel) {
|
||||
let list = panel.id === "detail-view" ? null : panel.querySelector("richlistbox");
|
||||
let list = panel.id === ADDONS.ids.detailView ? null : panel.querySelector("richlistbox");
|
||||
if (list) {
|
||||
// The list's items, not its box (a stretched box measures its own stretch).
|
||||
let rect = list.getBoundingClientRect(), first = null, last = null;
|
||||
@@ -1516,19 +1517,18 @@ function growToList(win) {
|
||||
function decoratePane(win) {
|
||||
let doc = win.document;
|
||||
later(200, function() { try { growToList(win); } catch (e) { log("about:addons height: " + e); } });
|
||||
let gVC = win.gViewController;
|
||||
let viewId = String((gVC && gVC.currentViewId) || "");
|
||||
let isThemes = viewId.indexOf("addons://list/theme") === 0;
|
||||
let viewId = ADDONS.currentViewId(win);
|
||||
let isThemes = viewId.indexOf(ADDONS.themeView) === 0;
|
||||
doc.documentElement.setAttribute("jihad-themes-view", isThemes ? "true" : "false");
|
||||
// The category is called Personas, as everywhere else in this browser (owner 2026-10-08).
|
||||
let cat = doc.getElementById("category-theme");
|
||||
let cat = ADDONS.element(doc, "categoryTheme");
|
||||
if (cat && cat.getAttribute("name") !== "Personas") { cat.setAttribute("name", "Personas"); }
|
||||
let listView = doc.getElementById("list-view"), list = doc.getElementById("addon-list");
|
||||
let listView = ADDONS.element(doc, "listView"), list = ADDONS.element(doc, "addonList");
|
||||
let box = doc.getElementById("jihad-themes-box");
|
||||
// "Uninstall", not "Remove", on every add-on, in every category (owner 2026-10-08).
|
||||
if (list) {
|
||||
for (let i = 0; i < list.children.length; i++) {
|
||||
let rm = doc.getAnonymousElementByAttribute(list.children[i], "anonid", "remove-btn");
|
||||
let rm = ADDONS.part(doc, list.children[i], "anonid", "remove-btn");
|
||||
if (rm && rm.getAttribute("label") !== "Uninstall") { rm.setAttribute("label", "Uninstall"); }
|
||||
}
|
||||
}
|
||||
@@ -1613,8 +1613,8 @@ function attachPane(win) {
|
||||
// The pane's window may be gone by the time a deferred call runs (a card closing while a
|
||||
// page loads logged "themes pane: TypeError: can't access dead object", round 2, 2026-10-15).
|
||||
let again = function() { try { if (Cu.isDeadWrapper(win) || win.closed) { return; } decoratePane(win); } catch (e) { log("themes pane: " + e); } };
|
||||
doc.addEventListener("ViewChanged", again, true);
|
||||
let list = doc.getElementById("addon-list");
|
||||
doc.addEventListener(ADDONS.viewChangedEvent, again, true);
|
||||
let list = ADDONS.element(doc, "addonList");
|
||||
if (list) {
|
||||
let pending = 0;
|
||||
new win.MutationObserver(function() {
|
||||
@@ -1624,7 +1624,7 @@ function attachPane(win) {
|
||||
}
|
||||
// An add-on's preferences arrive in its page after the view has changed (the manager loads
|
||||
// them): the page's height is measured again when they do.
|
||||
let rows = doc.getElementById("detail-rows");
|
||||
let rows = ADDONS.element(doc, "detailRows");
|
||||
if (rows) {
|
||||
let measuring = 0;
|
||||
new win.MutationObserver(function() {
|
||||
@@ -1859,7 +1859,7 @@ function galleryRequest(bag) {
|
||||
let id = get("id");
|
||||
if (!installed[id]) { reply({ok: false}); return; }
|
||||
try {
|
||||
let AM = Cu.import("resource://gre/modules/AddonManager.jsm", {}).AddonManager;
|
||||
let AM = JihadEngine.AddonManager;
|
||||
AM.getAddonByID(id + ID_SUFFIX, function(addon) {
|
||||
try { if (addon) { addon.uninstall(); } } catch (e) { log("remove " + id + ": " + e); }
|
||||
try { Services.obs.notifyObservers(null, "jihad-personas-mine", id); } catch (e) {}
|
||||
@@ -1930,7 +1930,7 @@ function JihadPersonas() {
|
||||
Services.obs.addObserver(this, "content-document-global-created", false);
|
||||
Services.obs.addObserver(this, "chrome-document-global-created", false);
|
||||
try {
|
||||
let AM = Cu.import("resource://gre/modules/AddonManager.jsm", {}).AddonManager;
|
||||
let AM = JihadEngine.AddonManager;
|
||||
AM.addAddonListener({
|
||||
onUninstalled: function(addon) {
|
||||
if (!addon || addon.type !== "theme") { return; }
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/* 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/.
|
||||
*
|
||||
* Jihad Browser — JihadEngine.jsm: the engine's JS surface Jihad's components rely on, in one place
|
||||
* (cavekit-engine-update.md R4b, T-386).
|
||||
*
|
||||
* The engine modules the components import (LightweightThemeManager, AddonManager,
|
||||
* PermissionsUtils) and the about:addons markup the Personas pane decorates (element ids, the
|
||||
* anonids of an add-on's row, classes, the view controller) change between UXP releases without the
|
||||
* compiler saying so. Every component reaches them through here, and
|
||||
* build/engine/check-engine-api.py reads the SURFACE block below (the same JSON) to check a
|
||||
* candidate engine against exactly what the components use: one list, not two.
|
||||
*
|
||||
* Installed as resource://gre/modules/JihadEngine.jsm (modules/ in the GRE).
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["JihadEngine"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
// SURFACE-BEGIN (JSON: check-engine-api.py parses the text between the markers)
|
||||
const SURFACE = {
|
||||
"modules": {
|
||||
"LightweightThemeManager": {
|
||||
"url": "resource://gre/modules/LightweightThemeManager.jsm",
|
||||
"source": "toolkit/mozapps/extensions/LightweightThemeManager.jsm",
|
||||
"needs": ["parseTheme", "previewTheme", "resetPreview", "currentTheme", "usedThemes", "forgetUsedTheme",
|
||||
"\"lightweightThemes.\"", "lightweight-theme-styling-update", "@personas.mozilla.org"]
|
||||
},
|
||||
"AddonManager": {
|
||||
"url": "resource://gre/modules/AddonManager.jsm",
|
||||
"source": "toolkit/mozapps/extensions/AddonManager.jsm",
|
||||
"needs": ["getInstallForFile", "getAddonByID", "addAddonListener"]
|
||||
},
|
||||
"PermissionsUtils": {
|
||||
"url": "resource://gre/modules/PermissionsUtils.jsm",
|
||||
"source": "toolkit/modules/PermissionsUtils.jsm",
|
||||
"needs": ["importFromPrefs"]
|
||||
}
|
||||
},
|
||||
"addons": {
|
||||
"urls": ["about:addons", "chrome://mozapps/content/extensions/extensions.xul"],
|
||||
"ids": {
|
||||
"page": "addons-page",
|
||||
"listView": "list-view",
|
||||
"addonList": "addon-list",
|
||||
"addonListEmpty": "addon-list-empty",
|
||||
"detailView": "detail-view",
|
||||
"detailRows": "detail-rows",
|
||||
"categoryTheme": "category-theme"
|
||||
},
|
||||
"themeView": "addons://list/theme",
|
||||
"anonids": ["name", "description", "date-updated", "version", "details-btn", "enable-btn", "disable-btn", "remove-btn"],
|
||||
"classes": ["content-inner-container", "disabled-postfix"],
|
||||
"markup": {
|
||||
"toolkit/mozapps/extensions/content/extensions.xul": ["id=\"addons-page\"", "id=\"list-view\"", "id=\"addon-list\"",
|
||||
"id=\"addon-list-empty\"", "id=\"detail-view\"", "id=\"detail-rows\""],
|
||||
"toolkit/mozapps/extensions/content/extensions.xml": ["anonid=\"name\"", "anonid=\"description\"", "anonid=\"date-updated\"",
|
||||
"anonid=\"version\"", "anonid=\"details-btn\"", "anonid=\"enable-btn\"", "anonid=\"disable-btn\"", "anonid=\"remove-btn\"",
|
||||
"anonid=\"control-container\"", "class=\"content-inner-container\"", "class=\"disabled-postfix\""],
|
||||
"toolkit/mozapps/extensions/content/extensions.js": ["gViewController", "currentViewId", "viewPort", "\"ViewChanged\"",
|
||||
"extensions.ui.lastCategory", "\"category-\" + aId", "addons://list/"]
|
||||
}
|
||||
}
|
||||
};
|
||||
// SURFACE-END
|
||||
|
||||
this.JihadEngine = {
|
||||
surface: SURFACE,
|
||||
|
||||
// An engine module by its name in SURFACE.modules (throws for a name not listed there, so a new
|
||||
// dependency is added to the surface, and so to the checker, first).
|
||||
module(name) {
|
||||
const m = SURFACE.modules[name];
|
||||
if (!m) { throw new Error("JihadEngine: " + name + " is not in the surface"); }
|
||||
return Cu.import(m.url, {})[name];
|
||||
},
|
||||
get LightweightThemeManager() { return this.module("LightweightThemeManager"); },
|
||||
get AddonManager() { return this.module("AddonManager"); },
|
||||
get PermissionsUtils() { return this.module("PermissionsUtils"); },
|
||||
|
||||
// about:addons
|
||||
addons: {
|
||||
ids: SURFACE.addons.ids,
|
||||
themeView: SURFACE.addons.themeView,
|
||||
// is this document the add-on manager?
|
||||
isDocument(doc) {
|
||||
let u = "";
|
||||
try { u = String(doc.documentURI || ""); } catch (e) { return false; }
|
||||
return SURFACE.addons.urls.some(function(p) { return u.indexOf(p) === 0; });
|
||||
},
|
||||
// an element by its key in SURFACE.addons.ids
|
||||
element(doc, key) {
|
||||
const id = SURFACE.addons.ids[key];
|
||||
if (!id) { throw new Error("JihadEngine: no about:addons id " + key); }
|
||||
return doc.getElementById(id);
|
||||
},
|
||||
// "#id" for a style sheet
|
||||
selector(key) {
|
||||
const id = SURFACE.addons.ids[key];
|
||||
if (!id) { throw new Error("JihadEngine: no about:addons id " + key); }
|
||||
return "#" + id;
|
||||
},
|
||||
// a row's part by anonid or class (both listed in the surface)
|
||||
part(doc, item, attr, value) {
|
||||
const listed = attr === "anonid" ? SURFACE.addons.anonids : attr === "class" ? SURFACE.addons.classes : [];
|
||||
if (listed.indexOf(value) < 0) { throw new Error("JihadEngine: about:addons " + attr + " " + value + " is not in the surface"); }
|
||||
return doc.getAnonymousElementByAttribute(item, attr, value);
|
||||
},
|
||||
// the manager's view controller and the view shown
|
||||
viewController(win) { return win.gViewController || null; },
|
||||
currentViewId(win) {
|
||||
const c = win.gViewController;
|
||||
return String((c && c.currentViewId) || "");
|
||||
},
|
||||
viewChangedEvent: "ViewChanged",
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2026 NotAlexNoyle. MPL-2.0.
|
||||
// jihad-engine-shim-test.js — T-386 (cavekit-engine-update R4b): JihadEngine.jsm outside the engine,
|
||||
// with a fake Components and document. Run: node render/goanna/test/jihad-engine-shim-test.js render/goanna/modules/JihadEngine.jsm
|
||||
// unit test of the shim outside the engine: a fake Components and document
|
||||
const fs = require("fs");
|
||||
const src = fs.readFileSync(process.argv[2], "utf8");
|
||||
const imported = [];
|
||||
const Components = { utils: { import(url, scope) { imported.push(url); const n = url.replace(/.*\//, "").replace(".jsm", ""); return { [n]: { name: n } }; } } };
|
||||
const ctx = {};
|
||||
new Function("Components", src + "\nreturn this;").call(ctx, Components);
|
||||
const E = ctx.JihadEngine;
|
||||
let fails = 0; const ok = (c, m) => { if (!c) { fails++; console.log("FAIL", m); } };
|
||||
ok(E.LightweightThemeManager.name === "LightweightThemeManager", "LTM by name");
|
||||
ok(E.AddonManager.name === "AddonManager", "AM by name");
|
||||
ok(imported[1] === "resource://gre/modules/AddonManager.jsm", "AM url");
|
||||
let threw = false; try { E.module("Nope"); } catch (e) { threw = true; } ok(threw, "unknown module throws");
|
||||
ok(E.addons.selector("page") === "#addons-page", "selector");
|
||||
ok(E.addons.isDocument({ documentURI: "about:addons" }) && !E.addons.isDocument({ documentURI: "about:blank" }), "isDocument");
|
||||
const doc = { getElementById: id => ({ id }), getAnonymousElementByAttribute: (i, a, v) => ({ a, v }) };
|
||||
ok(E.addons.element(doc, "categoryTheme").id === "category-theme", "element");
|
||||
ok(E.addons.part(doc, {}, "anonid", "remove-btn").v === "remove-btn", "part");
|
||||
threw = false; try { E.addons.part(doc, {}, "anonid", "not-listed"); } catch (e) { threw = true; } ok(threw, "unlisted part throws");
|
||||
ok(E.addons.currentViewId({ gViewController: { currentViewId: "addons://list/theme" } }) === "addons://list/theme", "view id");
|
||||
console.log(fails ? fails + " FAILED" : "shim test: all passed");
|
||||
process.exit(fails ? 1 : 0);
|
||||
Reference in new issue
Block a user