mobile-config-firefox/src/mobile-config-autoconfig.js

219 lines
7 KiB
JavaScript
Raw Normal View History

// Copyright 2023 Arnaud Ferraris, Oliver Smith
// SPDX-License-Identifier: MPL-2.0
// This is a Firefox autoconfig file:
// https://support.mozilla.org/en-US/kb/customizing-firefox-using-autoconfig
// Import custom userChrome.css on startup or new profile creation
// Log file: $(find ~/.mozilla -name mobile-config-firefox.log)
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
var updated = false;
var fragments_cache = {}; // cache for css_file_get_fragments()
// Create <profile>/chrome/ directory if not already present
var chromeDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
chromeDir.append("chrome");
if (!chromeDir.exists()) {
2021-12-04 15:36:56 +00:00
chromeDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
}
var logFile = chromeDir.clone();
logFile.append("mobile-config-firefox.log");
var mode = FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_APPEND;
var logFileStream = FileUtils.openFileOutputStream(logFile, mode);
function log(line) {
var date = new Date().toISOString().replace("T", " ").slice(0, 19);
line = "[" + date + "] " + line + "\n";
logFileStream.write(line, line.length);
}
// Debug function for logging object attributes
function log_obj(obj) {
var prop;
var value;
for (var prop in obj) {
try {
value = obj[prop];
} catch(e) {
value = e;
}
log(" - " + prop + ": " + value);
}
}
function get_firefox_version() {
try {
return Services.appinfo.lastAppVersion;
} catch(e) {
log("Failed to get FF version: " + e);
return 0;
}
}
function trigger_firefox_restart() {
log("Triggering Firefox restart");
var appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
appStartup.quit(Ci.nsIAppStartup.eForceQuit | Ci.nsIAppStartup.eRestart);
}
function set_default_prefs() {
log("Setting default preferences");
// Select a mobile user agent for firefox (same as tor browser on android)
defaultPref('general.useragent.override', 'Mozilla/5.0 (Android 10; Mobile; rv:110.0) Gecko/110.0 Firefox/110.0');
// Do not suggest facebook, ebay, reddit etc. in the urlbar. Same as
// Settings -> Privacy & Security -> Address Bar -> Shortcuts. As
// side-effect, the urlbar results are not immediatelly opened once
// clicking the urlbar.
defaultPref('browser.urlbar.suggest.topsites', false);
// Do not suggest search engines. Even though amazon is removed via
// policies.json, it gets installed shortly after the browser is opened.
// With this option, at least there is no big "Search with Amazon" message
// in the urlbar results as soon as typing the letter "a".
defaultPref('browser.urlbar.suggest.engines', false);
// Show about:home in new tabs, so it's not just a weird looking completely
// empty page.
defaultPref('browser.newtabpage.enabled', true);
}
// Get an array of paths to the fragments for one CSS file
// name: either "userChrome" or "userContent"
function css_file_get_fragments(name) {
if (name in fragments_cache)
return fragments_cache[name];
var ret = [];
var path = "/etc/mobile-config-firefox/" + name + ".files";
log("Reading fragments from file: " + path);
var file = new FileUtils.File(path);
var istream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);
istream.init(file, 0x01, 0444, 0);
istream.QueryInterface(Components.interfaces.nsILineInputStream);
var has_more;
do {
var line = {};
has_more = istream.readLine(line);
ret.push("/etc/mobile-config-firefox/" + line.value);
} while (has_more);
istream.close();
fragments_cache[name] = ret;
return ret;
}
// Create a nsIFile object with one of the CSS files in the user's profile as
// path. The file doesn't need to exist at this point.
// name: either "userChrome" or "userContent"
function css_file_get(name) {
var ret = chromeDir.clone();
ret.append(name + ".css");
return ret;
}
// Delete either userChrome.css or userContent.css inside the user's profile if
// they have an older timestamp than the CSS fragments (or list of CSS
// fragments) installed system-wide.
// name: either "userChrome" or "userContent"
// file: return of css_file_get()
function css_file_delete_outdated(name, file) {
var depends = css_file_get_fragments(name).slice(); /* copy the array */
depends.push("/etc/mobile-config-firefox/" + name + ".files");
for (var i in depends) {
var depend = depends[i];
var file_depend = new FileUtils.File(depend);
if (file.lastModifiedTime < file_depend.lastModifiedTime) {
log("Removing outdated file: " + file.path + " (newer: "
+ depend + ")");
file.remove(false);
return;
}
}
log("File is up-to-date: " + file.path);
return;
}
// Create userChrome.css / userContent.css in the user's profile, based on the
// CSS fragments stored in /etc/mobile-config-firefox.
// name: either "userChrome" or "userContent"
// file: return of css_file_get()
function css_file_merge(name, file) {
log("Creating CSS file from fragments: " + file.path);
var ostream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
ostream.init(file, 0x02 | 0x08 | 0x20, 0644, 0);
var fragments = css_file_get_fragments(name);
for (var i in fragments) {
var line;
var fragment = fragments[i];
log("- " + fragment);
line = "/* === " + fragment + " === */\n"
ostream.write(line, line.length);
var file_fragment = new FileUtils.File(fragment);
var istream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);
istream.init(file_fragment, 0x01, 0444, 0);
istream.QueryInterface(Components.interfaces.nsILineInputStream);
var has_more;
do {
var line = {};
has_more = istream.readLine(line);
line = line.value + "\n";
ostream.write(line, line.length);
} while (has_more);
istream.close();
}
ostream.close();
updated = true;
}
2021-05-15 18:33:29 +00:00
function css_files_update() {
var ff_version = get_firefox_version();
log("Firefox version: " + ff_version);
2021-05-15 18:33:29 +00:00
var names = ["userChrome", "userContent"];
for (var i in names) {
var name = names[i];
var file = css_file_get(name);
2021-05-15 18:33:29 +00:00
if (file.exists()) {
css_file_delete_outdated(name, file);
}
if (!file.exists()) {
css_file_merge(name, file);
}
}
}
log("Running mobile-config-autoconfig.js");
css_files_update();
// Restart Firefox immediately if one of the files got updated
if (updated == true)
trigger_firefox_restart();
else
set_default_prefs();
log("Done");
logFileStream.close();