no-bug: Implement fast paths and lint

This commit is contained in:
mr. m
2026-03-24 13:48:04 +01:00
parent 5707c8d058
commit 8b3c015f06
21 changed files with 842 additions and 407 deletions

View File

@@ -23,6 +23,7 @@ export class nsZenBoostStyles {
* Caches styles to optimize performance.
*
* @param {object} boostData - The boost configuration data.
* @param {string} domain - The domain associated with the boost.
* @returns {string} The generated CSS style string.
*/
getStyleForBoost(boostData, domain) {
@@ -59,7 +60,9 @@ export class nsZenBoostStyles {
let style = ``;
const fontFamily =
boostData.fontFamily != "" ? `font-family: ${boostData.fontFamily} !important;` : ``;
boostData.fontFamily != ""
? `font-family: ${boostData.fontFamily} !important;`
: ``;
const fontCase = `text-transform: ${boostData.textCaseOverride} !important;`;
let zapBlocks = "";
@@ -107,7 +110,6 @@ export class nsZenBoostStyles {
*
* @param {string} styleUri - The data URI of the CSS style.
* @param {string} domain - The domain associated with the boost.
* @returns {string} The cached style sheet URI.
* @private
*/
#cacheStyle(styleUri, domain) {

View File

@@ -46,7 +46,7 @@ export class nsZenBoostEditor {
this.killOtherEditorInstances();
nsZenBoostEditor.OBSERVERS.forEach((observe) => {
nsZenBoostEditor.OBSERVERS.forEach(observe => {
Services.obs.addObserver(this, observe);
});
@@ -60,10 +60,13 @@ export class nsZenBoostEditor {
* Initializes the boost editor by setting up event listeners for all UI controls.
*/
init() {
this.editorWindow.addEventListener("unload", () => this.handleClose(), { once: true });
this.editorWindow.addEventListener("unload", () => this.handleClose(), {
once: true,
});
this.doc.getElementById("zen-boost-editor-root").style.display = "flex";
this.doc.getElementById("zen-boost-code-editor-root").style.display = "none";
this.doc.getElementById("zen-boost-code-editor-root").style.display =
"none";
this.doc
.getElementById("zen-boost-color-contrast")
@@ -95,7 +98,7 @@ export class nsZenBoostEditor {
.addEventListener("click", this.onToggleInvert.bind(this));
this.doc
.getElementById("zen-boost-controls")
.addEventListener("click", (event) => this.openAdvancedColorOptions(event));
.addEventListener("click", event => this.openAdvancedColorOptions(event));
this.doc
.getElementById("zen-boost-save")
.addEventListener("click", this.onSaveBoostClick.bind(this));
@@ -118,8 +121,11 @@ export class nsZenBoostEditor {
.getElementById("zen-boost-css-inspector")
.addEventListener("click", this.onInspectorButtonPressed.bind(this));
this.doc.addEventListener("keydown", (event) => {
if (event.key === "Escape" || (event.key === "w" && (event.ctrlKey || event.metaKey))) {
this.doc.addEventListener("keydown", event => {
if (
event.key === "Escape" ||
(event.key === "w" && (event.ctrlKey || event.metaKey))
) {
this.onClosePressed();
}
});
@@ -133,7 +139,7 @@ export class nsZenBoostEditor {
uninit() {
this.uninitColorPicker();
nsZenBoostEditor.OBSERVERS.forEach((observe) => {
nsZenBoostEditor.OBSERVERS.forEach(observe => {
Services.obs.removeObserver(this, observe);
});
}
@@ -328,7 +334,9 @@ export class nsZenBoostEditor {
* @returns {Array<AString>} An array with names of available fonts.
*/
fetchFontList() {
const enumerator = Cc["@mozilla.org/gfx/fontenumerator;1"].createInstance(Ci.nsIFontEnumerator);
const enumerator = Cc["@mozilla.org/gfx/fontenumerator;1"].createInstance(
Ci.nsIFontEnumerator
);
return enumerator.EnumerateFonts(null, null);
}
@@ -339,7 +347,8 @@ export class nsZenBoostEditor {
onCodeButtonPressed() {
const offset = 265;
const openRightAligned =
this.openerWindow.screenX + this.openerWindow.outerWidth / 2 < this.editorWindow.screenX;
this.openerWindow.screenX + this.openerWindow.outerWidth / 2 <
this.editorWindow.screenX;
const windowElem = this.doc.getElementById("zenBoostWindow");
if (windowElem.getAttribute("editor") == "code") {
@@ -353,13 +362,20 @@ export class nsZenBoostEditor {
// being smaller than it should be
this._boostEditorWidth = this.editorWindow.outerWidth;
this.editorWindow.resizeTo(this._codeEditorWidth, this.editorWindow.outerHeight);
this.editorWindow.resizeTo(
this._codeEditorWidth,
this.editorWindow.outerHeight
);
if (openRightAligned) {
this.editorWindow.moveTo(this.editorWindow.screenX - offset, this.editorWindow.screenY);
this.editorWindow.moveTo(
this.editorWindow.screenX - offset,
this.editorWindow.screenY
);
}
this.doc.getElementById("zen-boost-editor-root").style.display = "none";
this.doc.getElementById("zen-boost-code-editor-root").style.display = "initial";
this.doc.getElementById("zen-boost-code-editor-root").style.display =
"initial";
}
/**
@@ -368,7 +384,8 @@ export class nsZenBoostEditor {
onCodeBackButtonPressed() {
const offset = 265;
const openRightAligned =
this.openerWindow.screenX + this.openerWindow.outerWidth / 2 < this.editorWindow.screenX;
this.openerWindow.screenX + this.openerWindow.outerWidth / 2 <
this.editorWindow.screenX;
const windowElem = this.doc.getElementById("zenBoostWindow");
if (windowElem.getAttribute("editor") == "boost") {
@@ -376,13 +393,20 @@ export class nsZenBoostEditor {
}
windowElem.setAttribute("editor", "boost");
this.editorWindow.resizeTo(this._boostEditorWidth, this.editorWindow.outerHeight);
this.editorWindow.resizeTo(
this._boostEditorWidth,
this.editorWindow.outerHeight
);
if (openRightAligned) {
this.editorWindow.moveTo(this.editorWindow.screenX + offset, this.editorWindow.screenY);
this.editorWindow.moveTo(
this.editorWindow.screenX + offset,
this.editorWindow.screenY
);
}
this.doc.getElementById("zen-boost-editor-root").style.display = "flex";
this.doc.getElementById("zen-boost-code-editor-root").style.display = "none";
this.doc.getElementById("zen-boost-code-editor-root").style.display =
"none";
// Disable picker mode
this.disableAllPickers();
@@ -390,7 +414,8 @@ export class nsZenBoostEditor {
async onZapButtonPressed() {
const linkedBrowser = this.openerWindow.gBrowser.selectedTab.linkedBrowser;
const actor = linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts");
const actor =
linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts");
actor.sendQuery("ZenBoost:ToggleZapMode");
// Focus the parent browser window
@@ -399,7 +424,8 @@ export class nsZenBoostEditor {
async onPickerButtonPressed() {
const linkedBrowser = this.openerWindow.gBrowser.selectedTab.linkedBrowser;
const actor = linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts");
const actor =
linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts");
actor.sendQuery("ZenBoost:TogglePickerMode");
}
@@ -425,13 +451,15 @@ ${cssSelector} {
onInspectorButtonPressed() {
const linkedBrowser = this.openerWindow.gBrowser.selectedTab.linkedBrowser;
const actor = linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts");
const actor =
linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts");
actor.sendQuery("ZenBoost:OpenInspector");
}
async onUpdateZapButtonVisual() {
const linkedBrowser = this.openerWindow.gBrowser.selectedTab.linkedBrowser;
const actor = linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts");
const actor =
linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts");
const zapButton = this.doc.getElementById("zen-boost-zap");
const zapEnabled = await actor.sendQuery("ZenBoost:ZapModeEnabled");
@@ -440,10 +468,13 @@ ${cssSelector} {
async onUpdatePickerButtonVisual() {
const linkedBrowser = this.openerWindow.gBrowser.selectedTab.linkedBrowser;
const actor = linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts");
const actor =
linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts");
const pickerButton = this.doc.getElementById("zen-boost-css-picker");
const selectEnabled = await actor.sendQuery("ZenBoost:SelectorPickerModeEnabled");
const selectEnabled = await actor.sendQuery(
"ZenBoost:SelectorPickerModeEnabled"
);
pickerButton.setAttribute("enabled", selectEnabled ? "true" : "false");
}
@@ -479,7 +510,9 @@ ${cssSelector} {
* interactive color selection on the gradient picker.
*/
initColorPicker() {
const themePicker = this.doc.querySelector(".zen-boost-color-picker-gradient");
const themePicker = this.doc.querySelector(
".zen-boost-color-picker-gradient"
);
this._onMouseMove = this.onMouseMove.bind(this);
this._onMouseUp = this.onMouseUp.bind(this);
this._onMouseDown = this.onMouseDown.bind(this);
@@ -494,7 +527,9 @@ ${cssSelector} {
* Uninitializes the color picker by removing all mouse event listeners.
*/
uninitColorPicker() {
const themePicker = this.doc.querySelector(".zen-boost-color-picker-gradient");
const themePicker = this.doc.querySelector(
".zen-boost-color-picker-gradient"
);
this.doc.removeEventListener("mousemove", this._onMouseMove);
this.doc.removeEventListener("mouseup", this._onMouseUp);
themePicker.removeEventListener("mousedown", this._onMouseDown);
@@ -579,9 +614,15 @@ ${cssSelector} {
* and updates the current boost data accordingly.
*/
onColorOptionChange() {
this.currentBoostData.contrast = this.doc.getElementById("zen-boost-color-contrast").value;
this.currentBoostData.brightness = this.doc.getElementById("zen-boost-color-brightness").value;
this.currentBoostData.saturation = this.doc.getElementById("zen-boost-color-saturation").value;
this.currentBoostData.contrast = this.doc.getElementById(
"zen-boost-color-contrast"
).value;
this.currentBoostData.brightness = this.doc.getElementById(
"zen-boost-color-brightness"
).value;
this.currentBoostData.saturation = this.doc.getElementById(
"zen-boost-color-saturation"
).value;
this.updateCurrentBoost();
}
@@ -592,7 +633,9 @@ ${cssSelector} {
* @param {Event} event - The click event that triggered this action.
*/
openAdvancedColorOptions(event) {
const panel = this.doc.getElementById("zen-boost-advanced-color-options-panel");
const panel = this.doc.getElementById(
"zen-boost-advanced-color-options-panel"
);
panel.openPopup(event.target, "bottomcenter topcenter", 0, 2);
}
@@ -646,7 +689,8 @@ ${cssSelector} {
if (!animate) {
let nDistance = Math.sqrt(
(pixelX - this.lastDotSetPos.x) ** 2 + (pixelY - this.lastDotSetPos.y) ** 2
(pixelX - this.lastDotSetPos.x) ** 2 +
(pixelY - this.lastDotSetPos.y) ** 2
);
if (nDistance > 15) {
@@ -667,7 +711,9 @@ ${cssSelector} {
this.currentBoostData.dotAngleDeg = 0;
this.currentBoostData.dotDistance = 0;
} else {
let distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2);
let distance = Math.sqrt(
(pixelX - centerX) ** 2 + (pixelY - centerY) ** 2
);
distance = Math.min(distance, radius); // Clamp distance
const angle = Math.atan2(pixelY - centerY, pixelX - centerX);
@@ -677,7 +723,9 @@ ${cssSelector} {
// Rad to degree
this.currentBoostData.dotAngleDeg =
((Math.atan2(pixelY - centerY, pixelX - centerX) * 180) / Math.PI + 100) % 360;
((Math.atan2(pixelY - centerY, pixelX - centerX) * 180) / Math.PI +
100) %
360;
if (this.currentBoostData.dotAngleDeg < 0) {
this.currentBoostData.dotAngleDeg += 360;
}
@@ -723,6 +771,8 @@ ${cssSelector} {
/**
* Updates the radius of the circle based on the dot's position.
*
* @param {boolean} animate - Whether to animate the radius change (default: true).
*/
updateCircleRadius(animate = true) {
const gradient = this.doc.querySelector(".zen-boost-color-picker-gradient");
@@ -743,7 +793,8 @@ ${cssSelector} {
* @param {boolean} userAction - Whether this was triggered by a user action (default: true).
*/
onToggleDisable(userAction = true) {
this.currentBoostData.enableColorBoost = !this.currentBoostData.enableColorBoost;
this.currentBoostData.enableColorBoost =
!this.currentBoostData.enableColorBoost;
if (userAction) {
this.currentBoostData.changeWasMade = true;
@@ -816,7 +867,10 @@ ${cssSelector} {
// Give the gradient a grayscale effect
// when the color boosting is disabled
// or the theme is set automatically
if (!this.currentBoostData.enableColorBoost || this.currentBoostData.autoTheme) {
if (
!this.currentBoostData.enableColorBoost ||
this.currentBoostData.autoTheme
) {
gradient.classList.add("zen-boost-panel-disabled");
} else {
gradient.classList.remove("zen-boost-panel-disabled");
@@ -828,8 +882,12 @@ ${cssSelector} {
*/
updateColorControlSliderVisuals() {
const contrastSlider = this.doc.getElementById("zen-boost-color-contrast");
const brightnessSlider = this.doc.getElementById("zen-boost-color-brightness");
const saturationSlider = this.doc.getElementById("zen-boost-color-saturation");
const brightnessSlider = this.doc.getElementById(
"zen-boost-color-brightness"
);
const saturationSlider = this.doc.getElementById(
"zen-boost-color-saturation"
);
contrastSlider.value = this.currentBoostData.contrast;
brightnessSlider.value = this.currentBoostData.brightness;
@@ -882,7 +940,9 @@ ${cssSelector} {
const fontButtonGroup = this.doc.getElementById("zen-boost-font-grid");
for (let i = 0; i < fontButtonGroup.children.length; i++) {
const fontButton = fontButtonGroup.children[i];
if (fontButton.getAttribute("font-data") == this.currentBoostData.fontFamily) {
if (
fontButton.getAttribute("font-data") == this.currentBoostData.fontFamily
) {
fontButton.classList.add("zen-boost-font-button-active");
} else {
fontButton.classList.remove("zen-boost-font-button-active");
@@ -904,7 +964,10 @@ ${cssSelector} {
* This triggers notifications to observers but does not persist to disk.
*/
updateCurrentBoost() {
const boost = gZenBoostsManager.loadBoostFromStore(this.boostInfo.domain, this.boostInfo.id);
const boost = gZenBoostsManager.loadBoostFromStore(
this.boostInfo.domain,
this.boostInfo.id
);
boost.boostEntry.boostData = this.currentBoostData;
gZenBoostsManager.updateBoost(boost);
}
@@ -913,7 +976,10 @@ ${cssSelector} {
* Deletes the current boost for the domain and closes the editor window.
*/
deleteBoost() {
const boost = gZenBoostsManager.loadBoostFromStore(this.boostInfo.domain, this.boostInfo.id);
const boost = gZenBoostsManager.loadBoostFromStore(
this.boostInfo.domain,
this.boostInfo.id
);
gZenBoostsManager.deleteBoost(boost);
this.currentBoostData = null;
@@ -954,7 +1020,9 @@ ${cssSelector} {
async editBoostName() {
const nameText = this.doc.getElementById("zen-boost-name-text");
const [title] = await this.doc.l10n.formatMessages(["zen-boost-rename-boost-prompt"]);
const [title] = await this.doc.l10n.formatMessages([
"zen-boost-rename-boost-prompt",
]);
let input = {
value: this.currentBoostData.boostName, // Default value and also output
@@ -996,11 +1064,16 @@ ${cssSelector} {
const loadButton = this.doc.getElementById("zen-boost-save");
loadButton.setAttribute("mode", "blue");
const success = await gZenBoostsManager.exportBoost(this.editorWindow, this.currentBoostData);
const success = await gZenBoostsManager.exportBoost(
this.editorWindow,
this.currentBoostData
);
loadButton.setAttribute("mode", "");
if (success) {
this.openerWindow.gZenUIManager.showToast("zen-panel-ui-boosts-exported-message");
this.openerWindow.gZenUIManager.showToast(
"zen-panel-ui-boosts-exported-message"
);
}
}
@@ -1074,11 +1147,14 @@ ${cssSelector} {
}
);
const anim3 = elementShadow.animate([{ opacity: 0 }, { opacity: 1 }, { opacity: 0 }], {
duration: 460,
fill: "forwards",
easing: "ease-out",
});
const anim3 = elementShadow.animate(
[{ opacity: 0 }, { opacity: 1 }, { opacity: 0 }],
{
duration: 460,
fill: "forwards",
easing: "ease-out",
}
);
Promise.all([anim1.finished, anim2.finished, anim3.finished]).then(() => {
element.remove();
@@ -1138,8 +1214,14 @@ ${cssSelector} {
this.uninit();
if (this.currentBoostData != null && this.currentBoostData.changeWasMade) {
this.saveBoost();
} else if (this.currentBoostData != null && !this.currentBoostData.changeWasMade) {
const boost = gZenBoostsManager.loadBoostFromStore(this.boostInfo.domain, this.boostInfo.id);
} else if (
this.currentBoostData != null &&
!this.currentBoostData.changeWasMade
) {
const boost = gZenBoostsManager.loadBoostFromStore(
this.boostInfo.domain,
this.boostInfo.id
);
gZenBoostsManager.deleteBoost(boost);
}
@@ -1166,13 +1248,19 @@ ${cssSelector} {
}
updateAllVisuals() {
this.doc.getElementById("zen-boost-name-text").textContent = this.currentBoostData.boostName;
this.doc.getElementById("zen-boost-name-text").textContent =
this.currentBoostData.boostName;
const dot = this.doc.querySelector(".zen-boost-color-picker-dot");
if (this.currentBoostData.dotPos.x == null || this.currentBoostData.dotPos.y == null) {
if (
this.currentBoostData.dotPos.x == null ||
this.currentBoostData.dotPos.y == null
) {
this.resetDotPosition();
} else {
const gradient = this.doc.querySelector(".zen-boost-color-picker-gradient");
const gradient = this.doc.querySelector(
".zen-boost-color-picker-gradient"
);
const rect = gradient.getBoundingClientRect();
// Test if the stored position is a non-normalized dot position
@@ -1183,8 +1271,10 @@ ${cssSelector} {
this.currentBoostData.dotPos.y < 0
) {
// Normalize position
this.currentBoostData.dotPos.x = this.currentBoostData.dotPos.x / rect.width;
this.currentBoostData.dotPos.y = this.currentBoostData.dotPos.y / rect.height;
this.currentBoostData.dotPos.x =
this.currentBoostData.dotPos.x / rect.width;
this.currentBoostData.dotPos.y =
this.currentBoostData.dotPos.y / rect.height;
}
// Convert normalized position to relative position
@@ -1215,7 +1305,10 @@ ${cssSelector} {
return;
}
const boost = gZenBoostsManager.loadBoostFromStore(this.boostInfo.domain, this.boostInfo.id);
const boost = gZenBoostsManager.loadBoostFromStore(
this.boostInfo.domain,
this.boostInfo.id
);
boost.boostEntry.boostData = this.currentBoostData;
gZenBoostsManager.saveBoostToStore(boost);

View File

@@ -231,7 +231,9 @@ class nsZenBoostsManager {
}
if (domainEntry.boostEntries.has(domainEntry.activeBoostId)) {
const boostEntry = domainEntry.boostEntries.get(domainEntry.activeBoostId);
const boostEntry = domainEntry.boostEntries.get(
domainEntry.activeBoostId
);
return { id: domainEntry.activeBoostId, domain, boostEntry };
}
}
@@ -323,13 +325,17 @@ class nsZenBoostsManager {
if (domainEntry.boostEntries.has(id)) {
if (domainEntry.activeBoostId === id) {
domainEntry.activeBoostId = null;
Services.obs.notifyObservers(null, "zen-boosts-active-change", { id: null });
Services.obs.notifyObservers(null, "zen-boosts-active-change", {
id: null,
});
this.#stylesManager.invalidateStyleForDomain(domain);
this.notify(true);
} else {
domainEntry.activeBoostId = id;
Services.obs.notifyObservers(null, "zen-boosts-active-change", { id });
Services.obs.notifyObservers(null, "zen-boosts-active-change", {
id,
});
this.#stylesManager.invalidateStyleForDomain(domain);
this.notify();
@@ -374,6 +380,8 @@ class nsZenBoostsManager {
/**
* Notifies all observers that boost data has been updated.
* This triggers a 'zen-boosts-update' notification event.
*
* @param {boolean} unloadStyles - Whether to unload styles during the update.
*/
notify(unloadStyles = false) {
Services.obs.notifyObservers(null, "zen-boosts-update", { unloadStyles });
@@ -400,7 +408,7 @@ class nsZenBoostsManager {
* @private
*/
#readBoostsFromStore(done) {
this.#readFromDisk().then((data) => {
this.#readFromDisk().then(data => {
this.registeredDomains = data;
done();
});
@@ -587,7 +595,9 @@ class nsZenBoostsManager {
*/
openBoostWindow(parentWindow, boost, domainUri) {
if (!this.canBoostSite(domainUri)) {
console.error("[ZenBoostsManager] Cannot open editor for boost with invalid domain.");
console.error(
"[ZenBoostsManager] Cannot open editor for boost with invalid domain."
);
return null;
}
@@ -624,12 +634,16 @@ class nsZenBoostsManager {
);
// Close the editor if the tab is switched
parentWindow.gBrowser.tabContainer.addEventListener("TabSelect", editor.close.bind(editor), {
once: true,
});
parentWindow.gBrowser.tabContainer.addEventListener(
"TabSelect",
editor.close.bind(editor),
{
once: true,
}
);
const progressListener = {
onLocationChange: (webProgress) => {
onLocationChange: webProgress => {
if (webProgress.isTopLevel) {
editor.close();
parentWindow.gBrowser.removeTabsProgressListener(progressListener);
@@ -654,6 +668,7 @@ class nsZenBoostsManager {
* Will spawn a file save dialog and export the selected boost
*
* @param {Window} parentWindow The window that will instance the file picker
* @param {object} boostData The data of the boost to be exported
* @returns {Promise<void>} Returns a promise which will be resolved after the export action is complete
*/
exportBoost(parentWindow, boostData) {
@@ -693,8 +708,8 @@ class nsZenBoostsManager {
fp.defaultExtension = "json";
fp.appendFilters(nsIFilePicker.filterAll);
return new Promise((resolve) => {
fp.open(async (result) => {
return new Promise(resolve => {
fp.open(async result => {
if (result === nsIFilePicker.returnOK && fp.file) {
try {
const boostJSON = JSON.stringify(boostData);
@@ -721,12 +736,16 @@ class nsZenBoostsManager {
const nsIFilePicker = Ci.nsIFilePicker;
const fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(parentWindow.browsingContext, "Importing Boost from JSON", nsIFilePicker.modeOpen);
fp.init(
parentWindow.browsingContext,
"Importing Boost from JSON",
nsIFilePicker.modeOpen
);
fp.appendFilters(nsIFilePicker.filterAll);
return new Promise((resolve) => {
fp.open(async (result) => {
return new Promise(resolve => {
fp.open(async result => {
if (result === nsIFilePicker.returnOK && fp.file) {
try {
const fileContent = await IOUtils.readUTF8(fp.file.path);

View File

@@ -89,13 +89,19 @@ export class SelectorComponent {
this.cancelButton.addEventListener("click", this.#cancelSelect.bind(this));
this.selectThisButton = this.getElementById("select-this");
this.selectThisButton.addEventListener("click", this.#handleSelect.bind(this));
this.selectThisButton.addEventListener(
"click",
this.#handleSelect.bind(this)
);
this.selectRelatedSlider = this.getElementById("select-related");
this.selectRelatedSlider.addEventListener("click", this.#handleSelect.bind(this));
this.selectRelatedSlider.addEventListener(
"click",
this.#handleSelect.bind(this)
);
// Initialize the related elements button
this.selectRelatedSlider.addEventListener("mousemove", (e) => {
this.selectRelatedSlider.addEventListener("mousemove", e => {
const r = e.currentTarget.getBoundingClientRect();
const mouseX = e.clientX;
@@ -113,7 +119,7 @@ export class SelectorComponent {
}
});
this.selectRelatedSlider.addEventListener("mouseout", (e) => {
this.selectRelatedSlider.addEventListener("mouseout", e => {
e.currentTarget.style.removeProperty("--related-elements-value");
this.#relatedValueIndex = 0;
@@ -159,6 +165,8 @@ export class SelectorComponent {
/**
* Helper for getting an anonymous element by id
*
* @param {string} id The id of the element
*/
getElementById(id) {
return this.content.root.getElementById(id);
@@ -166,9 +174,8 @@ export class SelectorComponent {
get markup() {
// Fetch localizations
let [thisElement, relatedElements, cancelAction] = lazy.overlayLocalization.formatMessagesSync(
this.#localizationArray
);
let [thisElement, relatedElements, cancelAction] =
lazy.overlayLocalization.formatMessagesSync(this.#localizationArray);
return `
<template>
@@ -194,7 +201,10 @@ export class SelectorComponent {
if (!this.template) {
let parser = new DOMParser();
let doc = parser.parseFromString(this.markup, "text/html");
this.template = this.document.importNode(doc.querySelector("template"), true);
this.template = this.document.importNode(
doc.querySelector("template"),
true
);
}
let fragment = this.template.content.cloneNode(true);
return fragment;
@@ -389,12 +399,18 @@ export class SelectorComponent {
const top = this.clamp(
bounds.top + bounds.height + distance,
windowPadding + this.safeAreaPadding.top,
windowHeight - zapComponentHeight - windowPadding - this.safeAreaPadding.bottom
windowHeight -
zapComponentHeight -
windowPadding -
this.safeAreaPadding.bottom
);
const left = this.clamp(
bounds.left + bounds.width / 2 - zapComponentWidth / 2,
windowPadding + this.safeAreaPadding.left,
windowWidth - zapComponentWidth - windowPadding - this.safeAreaPadding.right
windowWidth -
zapComponentWidth -
windowPadding -
this.safeAreaPadding.right
);
Object.assign(this.selectorComponent.style, {
@@ -491,7 +507,10 @@ export class SelectorComponent {
this.#hideHoverDiv();
}
if (this.#currentState !== SelectorComponent.STATES.SELECTING || !event.target) {
if (
this.#currentState !== SelectorComponent.STATES.SELECTING ||
!event.target
) {
return;
}
@@ -522,7 +541,10 @@ export class SelectorComponent {
return;
}
if (this.#currentState === SelectorComponent.STATES.SELECTING && !isZenContent) {
if (
this.#currentState === SelectorComponent.STATES.SELECTING &&
!isZenContent
) {
this.setState(SelectorComponent.STATES.SELECTED, event.target);
}
}
@@ -560,14 +582,18 @@ export class SelectorComponent {
/**
* Used for retreiving the css path from the selected element and taking
* the related objects into account
*
* @param {Element} document
* @param {Element} relatedValueIndex
* @param {Element} selectedElement
*/
getSelectionPath(document, relatedValueIndex, selectedElement) {
let path = [];
const cssescape = (str) => CSS.escape(str);
const cssescape = str => CSS.escape(str);
// Body and Html nodes are not considered valid here
const isValidNode = (element) => {
const isValidNode = element => {
if (!element) {
return false;
} else if (element.tagName.toLowerCase() === "body") {
@@ -578,7 +604,7 @@ export class SelectorComponent {
return true;
};
const nthChild = (element) => {
const nthChild = element => {
if (!element) {
return "";
}
@@ -604,7 +630,7 @@ export class SelectorComponent {
const id = specifity < 2 && element.id ? `#${cssescape(element.id)}` : "";
const cls =
specifity < 1 && element.classList.length
? "." + [...element.classList].map((c) => cssescape(c)).join(".")
? "." + [...element.classList].map(c => cssescape(c)).join(".")
: "";
const tag = element.tagName ? element.tagName.toLowerCase() : "";
return `${tag}${id}${cls}`;
@@ -637,7 +663,7 @@ export class SelectorComponent {
}
};
const build = (pathArray) => pathArray.toReversed().join("");
const build = pathArray => pathArray.toReversed().join("");
const findBestExactSelector = (element, doc) => {
let buildMap = new Map();
@@ -675,7 +701,9 @@ export class SelectorComponent {
const pathParentElementBuilt = build(parentExactElement);
const pathParentElementLength = (
pathParentElementBuilt ? doc.querySelectorAll(pathParentElementBuilt) : []
pathParentElementBuilt
? doc.querySelectorAll(pathParentElementBuilt)
: []
).length;
if (!buildMap.has(pathParentElementLength)) {

View File

@@ -239,10 +239,16 @@ void main() {
*/
async #createProgram(gl, vertContent, fragContent) {
const vertexShader = this.#compileShader(gl, vertContent, gl.VERTEX_SHADER);
const fragmentShader = this.#compileShader(gl, fragContent, gl.FRAGMENT_SHADER);
const fragmentShader = this.#compileShader(
gl,
fragContent,
gl.FRAGMENT_SHADER
);
if (!vertexShader || !fragmentShader) {
console.error("Program creation aborted: One or more shaders failed to compile.");
console.error(
"Program creation aborted: One or more shaders failed to compile."
);
return null;
}
@@ -257,7 +263,10 @@ void main() {
gl.deleteShader(fragmentShader);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error("Shader program initializiation failure:", gl.getProgramInfoLog(program));
console.error(
"Shader program initializiation failure:",
gl.getProgramInfoLog(program)
);
}
return program;
@@ -292,8 +301,9 @@ void main() {
* @returns {ImageData} Image data
*/
#getImageData(image) {
const canvas =
this.getElementById("zen-zap-dissolve-canvas").ownerDocument.createElement("canvas");
const canvas = this.getElementById(
"zen-zap-dissolve-canvas"
).ownerDocument.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
@@ -337,7 +347,12 @@ void main() {
this.#webglContext.NEAREST
);
if (image && image instanceof Ci.nsIImageLoadingContent && image.width && image.height) {
if (
image &&
image instanceof Ci.nsIImageLoadingContent &&
image.width &&
image.height
) {
this.#webglContext.texImage2D(
this.#webglContext.TEXTURE_2D,
0,
@@ -422,7 +437,7 @@ void main() {
* Requests an animation frame
*/
#requestDraw() {
this.#rafId = this.window.requestAnimationFrame((t) => this.#draw(t));
this.#rafId = this.window.requestAnimationFrame(t => this.#draw(t));
}
/**
@@ -487,7 +502,14 @@ void main() {
const canvas = this.getElementById("zen-zap-dissolve-canvas");
this.#resizeCanvasToClientSize(canvas);
ctx.drawWindow(this.window, rect.left, rect.top, rect.width, rect.height, "rgba(0,0,0,0)");
ctx.drawWindow(
this.window,
rect.left,
rect.top,
rect.width,
rect.height,
"rgba(0,0,0,0)"
);
this.#loadTexture(captureCanvas);
this.#bindParameters(element);
@@ -505,6 +527,8 @@ void main() {
/**
* Helper for getting an anonymous element by id
*
* @param {string} id The id of the element
*/
getElementById(id) {
return this.content.root.getElementById(id);
@@ -523,7 +547,10 @@ void main() {
if (!this.template) {
let parser = new DOMParser();
let doc = parser.parseFromString(this.markup, "text/html");
this.template = this.document.importNode(doc.querySelector("template"), true);
this.template = this.document.importNode(
doc.querySelector("template"),
true
);
}
let fragment = this.template.content.cloneNode(true);
return fragment;

View File

@@ -6,7 +6,8 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ZapDissolve: "resource:///modules/zen/boosts/ZenZapDissolve.sys.mjs",
SelectorComponent: "resource:///modules/zen/boosts/ZenSelectorComponent.sys.mjs",
SelectorComponent:
"resource:///modules/zen/boosts/ZenSelectorComponent.sys.mjs",
});
ChromeUtils.defineLazyGetter(lazy, "overlayLocalization", () => {
@@ -40,7 +41,11 @@ export class ZapOverlay {
zenBoostsChild,
this.#zapContentIDs,
this.handleSelectComponentSelect.bind(this),
[{ id: "zen-zap-this" }, { id: "zen-zap-related" }, { id: "zen-zap-cancel" }]
[
{ id: "zen-zap-this" },
{ id: "zen-zap-related" },
{ id: "zen-zap-cancel" },
]
);
// Remove the bottom unzap bar to the safe area
@@ -52,7 +57,9 @@ export class ZapOverlay {
*/
async initialize() {
if (this.#initialized) {
console.warn("[ZenZapOverlayChild]: Skipping initialize because initialized.");
console.warn(
"[ZenZapOverlayChild]: Skipping initialize because initialized."
);
return;
}
@@ -70,7 +77,10 @@ export class ZapOverlay {
*/
#initializeElements() {
this.zapDoneButton = this.getElementById("zap-done");
this.zapDoneButton.addEventListener("click", this.#disableZapMode.bind(this));
this.zapDoneButton.addEventListener(
"click",
this.#disableZapMode.bind(this)
);
this.#updateZappedList();
}
@@ -91,7 +101,8 @@ export class ZapOverlay {
// Capture current index and increment for next call
const returnIndex = this.#currentDissolveIndex;
this.#currentDissolveIndex = (this.#currentDissolveIndex + 1) % this.#dissolvePoolSize;
this.#currentDissolveIndex =
(this.#currentDissolveIndex + 1) % this.#dissolvePoolSize;
return this.#dissolveEffectPool[returnIndex];
}
@@ -105,6 +116,8 @@ export class ZapOverlay {
/**
* Helper for getting an anonymous element by id
*
* @param {string} id The id of the element
*/
getElementById(id) {
return this.content.root.getElementById(id);
@@ -112,7 +125,9 @@ export class ZapOverlay {
get markup() {
// Fetch localizations
let [done] = lazy.overlayLocalization.formatMessagesSync([{ id: "zen-zap-done" }]);
let [done] = lazy.overlayLocalization.formatMessagesSync([
{ id: "zen-zap-done" },
]);
return `
<template>
@@ -131,7 +146,10 @@ export class ZapOverlay {
if (!this.template) {
let parser = new DOMParser();
let doc = parser.parseFromString(this.markup, "text/html");
this.template = this.document.importNode(doc.querySelector("template"), true);
this.template = this.document.importNode(
doc.querySelector("template"),
true
);
}
let fragment = this.template.content.cloneNode(true);
return fragment;
@@ -139,6 +157,8 @@ export class ZapOverlay {
/**
* Handles the onSelect callback from the SelectComponent
*
* @param {string} cssSelector The CSS selector of the selected element
*/
handleSelectComponentSelect(cssSelector) {
this.#handleZap(cssSelector);
@@ -154,20 +174,24 @@ export class ZapOverlay {
/**
* Handles the addition of the given zap selector
*
* @param {string} cssPath The css selector of the zap
*/
#handleZap(cssPath) {
const useDissolve = Services.prefs.getBoolPref("zen.boosts.dissolve-on-zap");
const useDissolve = Services.prefs.getBoolPref(
"zen.boosts.dissolve-on-zap"
);
if (!this.window.gReduceMotion && useDissolve) {
const elements = this.document.querySelectorAll(cssPath);
let counter = 0;
elements.forEach(async (element) => {
elements.forEach(async element => {
if (counter > this.#dissolvePoolSize) {
return;
}
counter++;
this.#getNextDissolveEffect().then((dissolve) => {
this.#getNextDissolveEffect().then(dissolve => {
dissolve.dissolve(element);
});
});
@@ -211,7 +235,7 @@ export class ZapOverlay {
const boost = await this.zenBoostsChild.getWebsiteBoost();
const { boostData } = boost.boostEntry;
boostData.zapSelectors.forEach((selector) => {
boostData.zapSelectors.forEach(selector => {
const unzapButton = zapList.ownerDocument.createElement("input");
unzapButton.type = "button";
unzapButton.id = "zen-zap-unzap";
@@ -236,10 +260,11 @@ export class ZapOverlay {
});
// Fetch localizations
let [addZapHelper, removeZapHelper] = lazy.overlayLocalization.formatMessagesSync([
{ id: "zen-add-zap-helper" },
{ id: "zen-remove-zap-helper" },
]);
let [addZapHelper, removeZapHelper] =
lazy.overlayLocalization.formatMessagesSync([
{ id: "zen-add-zap-helper" },
{ id: "zen-remove-zap-helper" },
]);
if (!boostData.zapSelectors.length) {
const addZapHelperText = zapList.ownerDocument.createElement("p");
@@ -318,7 +343,7 @@ export class ZapOverlay {
this.#selectorComponent.tearDown();
this.#selectorComponent = null;
this.#dissolveEffectPool.forEach((dissolve) => {
this.#dissolveEffectPool.forEach(dissolve => {
dissolve.tearDown();
});

View File

@@ -8,7 +8,8 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ZapOverlay: "resource:///modules/zen/boosts/ZenZapOverlayChild.sys.mjs",
SelectorComponent: "resource:///modules/zen/boosts/ZenSelectorComponent.sys.mjs",
SelectorComponent:
"resource:///modules/zen/boosts/ZenSelectorComponent.sys.mjs",
});
export class ZenBoostsChild extends JSWindowActorChild {
@@ -25,7 +26,14 @@ export class ZenBoostsChild extends JSWindowActorChild {
PICKER: "picker",
};
static OVERLAY_EVENTS = ["click", "pointerdown", "pointermove", "pointerup", "scroll", "resize"];
static OVERLAY_EVENTS = [
"click",
"pointerdown",
"pointermove",
"pointerup",
"scroll",
"resize",
];
// A list of events that will be prevented from
// reaching the document
@@ -175,7 +183,10 @@ export class ZenBoostsChild extends JSWindowActorChild {
handleZapEvent(event) {
if (ZenBoostsChild.ALL_EVENTS_SET.has(event.type)) {
this.#overlay.handleEvent(event, ZenBoostsChild.PREVENTABLE_SET.has(event.type));
this.#overlay.handleEvent(
event,
ZenBoostsChild.PREVENTABLE_SET.has(event.type)
);
}
}
@@ -263,6 +274,10 @@ export class ZenBoostsChild extends JSWindowActorChild {
/**
* From ZenGradientGenerator.mjs
* Helper function for hslToRgb conversion
*
* @param {number} p
* @param {number} q
* @param {number} t
*/
#hueToRgb(p, q, t) {
if (t < 0) {
@@ -327,10 +342,16 @@ export class ZenBoostsChild extends JSWindowActorChild {
if (boostData.enableColorBoost) {
if (boostData.autoTheme) {
// Workspace color is converted to the HSL color space
let primaryGradientColor = boost.workspaceGradient[0]?.c ?? [0, 0, 0.6];
boost.workspaceGradient.forEach((color) => {
let primaryGradientColor = boost.workspaceGradient[0]?.c ?? [
0, 0, 0.6,
];
boost.workspaceGradient.forEach(color => {
if (color.isPrimary) {
primaryGradientColor = this.#rgbToHsl(color.c[0], color.c[1], color.c[2]);
primaryGradientColor = this.#rgbToHsl(
color.c[0],
color.c[1],
color.c[2]
);
}
});
@@ -343,7 +364,10 @@ export class ZenBoostsChild extends JSWindowActorChild {
);
const rgbColor = primaryGradientColor;
const nsColor = this.#rgbToNSColor(rgbColor, (1 - boostData.contrast) * 255);
const nsColor = this.#rgbToNSColor(
rgbColor,
(1 - boostData.contrast) * 255
);
browsingContext.zenBoostsData = nsColor;
} else {
let colorWheelColor = this.#hslToRgb(
@@ -355,7 +379,10 @@ export class ZenBoostsChild extends JSWindowActorChild {
);
const rgbColor = colorWheelColor;
const nsColor = this.#rgbToNSColor(rgbColor, (1 - boostData.contrast) * 255);
const nsColor = this.#rgbToNSColor(
rgbColor,
(1 - boostData.contrast) * 255
);
browsingContext.zenBoostsData = nsColor;
}
return;
@@ -454,7 +481,7 @@ export class ZenBoostsChild extends JSWindowActorChild {
}
async tempShowZappedElement(selector) {
this.document.querySelectorAll(selector).forEach((element) => {
this.document.querySelectorAll(selector).forEach(element => {
element.setAttribute("zen-zap-unhide", "true");
});
@@ -464,8 +491,8 @@ export class ZenBoostsChild extends JSWindowActorChild {
}
async tempHideZappedElement() {
this.#zappedElementsTempShown.forEach((selector) => {
this.document.querySelectorAll(selector).forEach((element) => {
this.#zappedElementsTempShown.forEach(selector => {
this.document.querySelectorAll(selector).forEach(element => {
element.removeAttribute("zen-zap-unhide");
});
});

View File

@@ -24,7 +24,7 @@ export class ZenBoostsParent extends JSWindowActorParent {
super();
this._observe = this.observe.bind(this);
ZenBoostsParent.OBSERVERS.forEach((observe) => {
ZenBoostsParent.OBSERVERS.forEach(observe => {
Services.obs.addObserver(this._observe, observe);
});
}
@@ -33,7 +33,7 @@ export class ZenBoostsParent extends JSWindowActorParent {
* Called when the actor is destroyed. Cleans up the observer.
*/
didDestroy() {
ZenBoostsParent.OBSERVERS.forEach((observe) => {
ZenBoostsParent.OBSERVERS.forEach(observe => {
Services.obs.removeObserver(this._observe, observe);
});
}
@@ -49,7 +49,9 @@ export class ZenBoostsParent extends JSWindowActorParent {
switch (topic) {
case "zen-boosts-update":
case "zen-space-gradient-update":
this.sendAsyncMessage("ZenBoost:BoostDataUpdated", { unloadStyles: true });
this.sendAsyncMessage("ZenBoost:BoostDataUpdated", {
unloadStyles: true,
});
break;
case "zen-boosts-disable-zap":
this.sendAsyncMessage("ZenBoost:DisableZapMode");
@@ -89,7 +91,11 @@ export class ZenBoostsParent extends JSWindowActorParent {
break;
}
case "ZenBoost:Notify": {
Services.obs.notifyObservers(null, message.data.topic, message.data.msg);
Services.obs.notifyObservers(
null,
message.data.topic,
message.data.msg
);
break;
}
case "ZenBoost:ZapSelector": {
@@ -106,9 +112,15 @@ export class ZenBoostsParent extends JSWindowActorParent {
}
if (data.action == "add") {
lazy.gZenBoostsManager.addZapSelectorToActive(data.selector, data.domain);
lazy.gZenBoostsManager.addZapSelectorToActive(
data.selector,
data.domain
);
} else if (data.action == "remove") {
lazy.gZenBoostsManager.removeZapSelectorToActive(data.selector, data.domain);
lazy.gZenBoostsManager.removeZapSelectorToActive(
data.selector,
data.domain
);
} else if (data.action == "clear") {
lazy.gZenBoostsManager.clearZapSelectorsForActive(data.domain);
}
@@ -128,13 +140,15 @@ export class ZenBoostsParent extends JSWindowActorParent {
}
const topWindowIsDarkMode =
embedder.ownerGlobal.getComputedStyle(embedder).colorScheme === "dark";
embedder.ownerGlobal.getComputedStyle(embedder).colorScheme ===
"dark";
const boost = lazy.gZenBoostsManager.loadActiveBoostFromStore(domain);
const currentWorkspace =
await this.browsingContext.topChromeWindow.gZenWorkspaces.getActiveWorkspace();
const styleData = await lazy.gZenBoostsManager.getStyleSheetForBoost(domain);
const styleData =
await lazy.gZenBoostsManager.getStyleSheetForBoost(domain);
return {
...boost,

View File

@@ -1,7 +1,7 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
content/browser/zen-styles/zen-boosts.css (../../zen/boosts/zen-boosts.css)
content/browser/zen-styles/content/zen-zap.css (../../zen/boosts/zen-zap.css)
content/browser/zen-styles/content/zen-selector.css (../../zen/boosts/zen-selector.css)

View File

@@ -25,16 +25,15 @@
// serialization/deserialization between parent and content processes.
#define NS_GET_CONTRAST(_c) NS_GET_A(_c)
#define MARK_MEDIA_FEATURE_CHANGED(_pc) \
(_pc)->MediaFeatureValuesChanged( \
{mozilla::RestyleHint::RecascadeSubtree(), NS_STYLE_HINT_VISUAL, \
mozilla::MediaFeatureChangeReason::PreferenceChange}, \
#define MARK_MEDIA_FEATURE_CHANGED(_pc) \
(_pc)->MediaFeatureValuesChanged( \
{mozilla::RestyleHint::RecascadeSubtree(), NS_STYLE_HINT_VISUAL, \
mozilla::MediaFeatureChangeReason::PreferenceChange}, \
mozilla::MediaFeatureChangePropagation::All);
#define TRIGGER_PRES_CONTEXT_RESTYLE() \
WalkPresContexts([&](nsPresContext* aPc) { \
MARK_MEDIA_FEATURE_CHANGED(aPc); \
});
#define TRIGGER_PRES_CONTEXT_RESTYLE() \
WalkPresContexts( \
[&](nsPresContext* aPc) { MARK_MEDIA_FEATURE_CHANGED(aPc); });
using BrowsingContext = mozilla::dom::BrowsingContext;
@@ -80,47 +79,18 @@ void BrowsingContext::DidSet(FieldIndex<IDX_IsZenBoostsInverted>,
}
namespace zen {
nsZenAccentOklab nsZenBoostsBackend::mCachedAccent{0};
namespace {
/**
* Inherited from the Oklab blog
* Source: https://bottosson.github.io/posts/oklab/
*/
struct Lab {
float L, a, b;
};
struct Lab {float L; float a; float b;};
struct RGB {float r; float g; float b;};
Lab rgb2oklab(RGB c) {
float l = 0.4122214708f * c.r + 0.5363325363f * c.g + 0.0514459929f * c.b;
float m = 0.2119034982f * c.r + 0.6806995451f * c.g + 0.1073969566f * c.b;
float s = 0.0883024619f * c.r + 0.2817188376f * c.g + 0.6299787005f * c.b;
float l_ = cbrtf(l);
float m_ = cbrtf(m);
float s_ = cbrtf(s);
return {
0.2104542553f*l_ + 0.7936177850f*m_ - 0.0040720468f*s_,
1.9779984951f*l_ - 2.4285922050f*m_ + 0.4505937099f*s_,
0.0259040371f*l_ + 0.7827717662f*m_ - 0.8086757660f*s_,
};
}
RGB oklab2rgb(Lab c) {
float l_ = c.L + 0.3963377774f * c.a + 0.2158037573f * c.b;
float m_ = c.L - 0.1055613458f * c.a - 0.0638541728f * c.b;
float s_ = c.L - 0.0894841775f * c.a - 1.2914855480f * c.b;
float l = l_*l_*l_;
float m = m_*m_*m_;
float s = s_*s_*s_;
return {
+4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s,
-1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s,
-0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s,
};
}
struct RGB {
float r, g, b;
};
/**
* @brief Clamps a value to the range [0, 255] using branchless operations.
@@ -133,6 +103,47 @@ static __inline int32_t clamp255(int32_t v) {
return (v | ((255 - v) >> 31)) & 255;
}
/**
* @brief A fast approximation of the cube root function using bit manipulation
* and two Newton-Raphson iterations. This is used to optimize the Oklab color
* conversion in the color filtering process.
*/
inline static float fast_cbrt(float x) {
// Bit-level initial approximation (works for positive floats only — fine
// here)
uint32_t bits;
memcpy(&bits, &x, 4);
bits = (bits / 3) + 0x2A512400u; // magic constant for cube root
float y;
memcpy(&y, &bits, 4);
// Two Newton-Raphson iterations: y = y - (y³ - x) / (3y²)
y = (2.0f / 3.0f) * y + (1.0f / 3.0f) * x / (y * y);
y = (2.0f / 3.0f) * y + (1.0f / 3.0f) * x / (y * y);
return y;
}
/**
* @brief Converts an Oklab color back to the RGB color space.
* @param c The Oklab color to convert.
* @return The corresponding RGB color.
*/
[[nodiscard]]
static inline auto oklab2rgb(Lab c) -> RGB {
float l_ = c.L + 0.3963377774f * c.a + 0.2158037573f * c.b;
float m_ = c.L - 0.1055613458f * c.a - 0.0638541728f * c.b;
float s_ = c.L - 0.0894841775f * c.a - 1.2914855480f * c.b;
// Cubing is just 2 multiplies — no cbrtf needed on the way back
return {
4.0767416621f * (l_ * l_ * l_) - 3.3077115913f * (m_ * m_ * m_) +
0.2309699292f * (s_ * s_ * s_),
-1.2684380046f * (l_ * l_ * l_) + 2.6097574011f * (m_ * m_ * m_) -
0.3413193965f * (s_ * s_ * s_),
-0.0041960863f * (l_ * l_ * l_) - 0.7034186147f * (m_ * m_ * m_) +
1.7076147010f * (s_ * s_ * s_),
};
}
/**
* @brief Applies a color filter to transform an original color toward an accent
* color. Preserves the original color's perceived luminance while shifting
@@ -143,68 +154,86 @@ static __inline int32_t clamp255(int32_t v) {
* contrast value).
* @return The filtered color with transformations applied.
*/
[[nodiscard]]
static inline Lab rgb2oklab_fast(RGB c) {
float l = 0.4122214708f * c.r + 0.5363325363f * c.g + 0.0514459929f * c.b;
float m = 0.2119034982f * c.r + 0.6806995451f * c.g + 0.1073969566f * c.b;
float s = 0.0883024619f * c.r + 0.2817188376f * c.g + 0.6299787005f * c.b;
float l_ = fast_cbrt(l), m_ = fast_cbrt(m), s_ = fast_cbrt(s);
return {
0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_,
1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_,
0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_,
};
}
inline static auto zenPrecomputeAccent(nscolor aAccentColor)
-> nsZenAccentOklab {
constexpr float kInv255 = 1.0f / 255.0f;
RGB rgb = {NS_GET_R(aAccentColor) * kInv255, NS_GET_G(aAccentColor) * kInv255,
NS_GET_B(aAccentColor) * kInv255};
auto lab = rgb2oklab_fast(rgb);
float contrast = NS_GET_CONTRAST(aAccentColor);
float vibranceBase = 1.0f - ((contrast - 128.0f) * (1.0f / 128.0f));
return {lab.L, lab.a, lab.b, vibranceBase, 0.25f + lab.L};
}
/**
* @brief Applies a color filter to transform an original color toward an accent
* color. Preserves the original color's perceived luminance while shifting
* hue/chroma toward the accent. Uses the alpha channel of the accent color to
* store contrast information.
* @param aOriginalColor The original color to filter.
* @param aAccentColor The accent color to filter toward (alpha channel contains
* contrast value).
* @return The filtered color with transformations applied.
*/
[[nodiscard]]
static nscolor zenFilterColorChannel(nscolor aOriginalColor,
nscolor aAccentColor) {
const auto r1 = NS_GET_R(aOriginalColor);
const auto g1 = NS_GET_G(aOriginalColor);
const auto b1 = NS_GET_B(aOriginalColor);
const auto a1 = NS_GET_A(aOriginalColor);
if (a1 == 0) {
// Skip processing fully transparent colors since they won't be visible and
// we want to avoid unnecessary computations with the accent color's alpha
// channel used for contrast information.
return aOriginalColor;
}
const nsZenAccentOklab& aAccent) {
const uint8_t a1 = NS_GET_A(aOriginalColor);
if (a1 == 0) return aOriginalColor;
const auto r2 = NS_GET_R(aAccentColor);
const auto g2 = NS_GET_G(aAccentColor);
const auto b2 = NS_GET_B(aAccentColor);
constexpr float kInv255 = 1.0f / 255.0f;
constexpr float kTint = 0.6f;
constexpr float kInvTint = 1.0f - kTint;
// It's a bit of a hacky solution, but instead of using alpha as what it is
// (opacity), we use it to store contrast information for now.
// We do this primarily to avoid having to deal with WebIDL structs and
// serialization/deserialization between parent and content processes.
const auto contrast = NS_GET_CONTRAST(aAccentColor);
RGB orig = {
NS_GET_R(aOriginalColor) * kInv255,
NS_GET_G(aOriginalColor) * kInv255,
NS_GET_B(aOriginalColor) * kInv255,
};
const auto lab = rgb2oklab_fast(orig);
RGB originalRgb(r1 / 255.0, g1 / 255.0, b1 / 255.0);
const auto originalOklab = rgb2oklab(originalRgb);
const float aBlend = kInvTint * lab.a + kTint * aAccent.a;
const float bBlend = kInvTint * lab.b + kTint * aAccent.b;
RGB accentRgb(r2 / 255.0, g2 / 255.0, b2 / 255.0);
const auto accentOklab = rgb2oklab(accentRgb);
double tintStrength = 0.6; // Decides how strongly the accent should influence the original color
double aBlend = (1.0 - tintStrength) * originalOklab.a + tintStrength * accentOklab.a;
double bBlend = (1.0 - tintStrength) * originalOklab.b + tintStrength * accentOklab.b;
// Avoid sqrt: compare squared chroma against squared threshold (0.4^2 = 0.16)
// vibranceFactor = chromaMixed/chromaBlend which simplifies to just
// vibranceFactor since the sqrt cancels in the normalize+scale round-trip
// (see below)
const float chromaSq = aBlend * aBlend + bBlend * bBlend;
const float saturation =
(chromaSq < 0.16f) ? chromaSq * (1.0f / 0.16f) : 1.0f;
const float vibranceFactor =
1.0f + aAccent.vibranceBase * (1.0f - saturation);
// Calculating chroma with the length of the vector of (a b)
double chromaBlend = sqrt(aBlend * aBlend + bBlend * bBlend);
// Normalizing against 0.4 since usually Oklab chroma maxes out around there
double vibranceAmount = 1 - ((contrast - 128.0) / 128.0);
double vibranceFactor = 1.0 + vibranceAmount * (1.0 - std::clamp(chromaBlend / 0.4, 0.0, 1.0));
// sqrt cancellation: chromaMixed/chromaBlend =
// (chromaBlend*vibranceFactor)/chromaBlend = vibranceFactor, so we multiply
// directly with no sqrt needed
const float aMixed = aBlend * vibranceFactor;
const float bMixed = bBlend * vibranceFactor;
// Essentially the equivalent of 'hue' for Oklab
double chromaMixed = chromaBlend * vibranceFactor;
double scale = (chromaBlend > 1e-6) ? (chromaMixed / chromaBlend) : 1.0;
float LMixed = 0.5f + (lab.L - 0.5f) * (1.0f + aAccent.vibranceBase * 0.5f);
LMixed *= aAccent.accentLOffset;
if (LMixed < 0.0f) LMixed = 0.0f;
if (LMixed > 1.0f) LMixed = 1.0f;
double aMixed = aBlend * scale;
double bMixed = bBlend * scale;
const auto rgb = oklab2rgb({LMixed, aMixed, bMixed});
// Lightness contrast
double contrastFactor = 1.0 + vibranceAmount * 0.5;
// Lightness factor
double LMixed = 0.5 + (originalOklab.L - 0.5) * contrastFactor;
LMixed = std::clamp(LMixed * (0.25 + accentOklab.L), 0.0, 1.0);
Lab tintedOklab(LMixed, aMixed, bMixed);
auto tintedRgb = oklab2rgb(tintedOklab);
const uint8_t fr8 = clamp255(tintedRgb.r * 255);
const uint8_t fg8 = clamp255(tintedRgb.g * 255);
const uint8_t fb8 = clamp255(tintedRgb.b * 255);
return NS_RGBA(fr8, fg8, fb8, a1);
return NS_RGBA(clamp255((int32_t)(rgb.r * 255.0f + 0.5f)),
clamp255((int32_t)(rgb.g * 255.0f + 0.5f)),
clamp255((int32_t)(rgb.b * 255.0f + 0.5f)), a1);
}
/**
@@ -243,9 +272,9 @@ inline static nscolor zenInvertColorChannel(nscolor aColor) {
/**
* @brief Retrieves the current boost data from the browsing context.
*/
inline static void GetZenBoostsDataFromBrowsingContext(ZenBoostData* aData,
bool* aIsInverted,
nsPresContext* aPresContext = nullptr) {
inline static void GetZenBoostsDataFromBrowsingContext(
ZenBoostData* aData, bool* aIsInverted,
nsPresContext* aPresContext = nullptr) {
auto zenBoosts = nsZenBoostsBackend::GetInstance();
if (!zenBoosts || (zenBoosts->mCurrentFrameIsAnonymousContent)) {
return;
@@ -257,7 +286,8 @@ inline static void GetZenBoostsDataFromBrowsingContext(ZenBoostData* aData,
*aIsInverted = browsingContext->IsZenBoostsInverted();
}
}
} else if (auto currentBrowsingContext = zenBoosts->GetCurrentBrowsingContext()) {
} else if (auto currentBrowsingContext =
zenBoosts->GetCurrentBrowsingContext()) {
*aData = currentBrowsingContext->ZenBoostsData();
*aIsInverted = currentBrowsingContext->IsZenBoostsInverted();
}
@@ -268,8 +298,8 @@ inline static void GetZenBoostsDataFromBrowsingContext(ZenBoostData* aData,
auto nsZenBoostsBackend::GetInstance() -> nsZenBoostsBackend* {
static nsZenBoostsBackend* zenBoosts;
if (!XRE_IsContentProcess()) {
// Zen boosts are only supported in content, so if we're in the parent process,
// just return null.
// Zen boosts are only supported in content, so if we're in the parent
// process, just return null.
return nullptr;
}
if (!zenBoosts) {
@@ -283,9 +313,8 @@ auto nsZenBoostsBackend::onPresShellEntered(mozilla::dom::Document* aDocument)
// Note that aDocument can be null when entering anonymous content frames.
// We explicitly do this to prevent applying boosts to anonymous content, such
// as devtools or screenshots.
mozilla::dom::BrowsingContext* browsingContext = aDocument
? aDocument->GetBrowsingContext()
: nullptr;
mozilla::dom::BrowsingContext* browsingContext =
aDocument ? aDocument->GetBrowsingContext() : nullptr;
if (!browsingContext) {
return;
}
@@ -293,7 +322,8 @@ auto nsZenBoostsBackend::onPresShellEntered(mozilla::dom::Document* aDocument)
}
auto nsZenBoostsBackend::FilterColorFromPresContext(nscolor aColor,
nsPresContext* aPresContext) -> nscolor {
nsPresContext* aPresContext)
-> nscolor {
if (!XRE_IsContentProcess()) {
// Zen boosts are only supported in content, so if we somehow end up here
// without a prescontext or in the parent process, just return the original
@@ -304,13 +334,16 @@ auto nsZenBoostsBackend::FilterColorFromPresContext(nscolor aColor,
bool invertColors = false;
GetZenBoostsDataFromBrowsingContext(&accentNS, &invertColors, aPresContext);
if (accentNS) {
if (mCachedAccent.accentNS != accentNS) {
mCachedAccent = zenPrecomputeAccent(accentNS);
}
// Apply a filter-like tint:
// - Preserve the original color's perceived luminance
// - Map hue/chroma toward the accent by scaling the accent's RGB
// to match the original luminance
// - Keep the original alpha
// Convert both colors to nscolor to access channels
aColor = zenFilterColorChannel(aColor, (nscolor)accentNS);
aColor = zenFilterColorChannel(aColor, mCachedAccent);
}
if (invertColors) {
aColor = zenInvertColorChannel(aColor);

View File

@@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_ZenBoostsBackend_h__
#define mozilla_ZenBoostsBackend_h__
#ifndef mozilla_ZenBoostsBackend_h_
#define mozilla_ZenBoostsBackend_h_
#include "nsColor.h"
#include "nsPresContext.h"
@@ -16,6 +16,13 @@ using ZenBoostData = nscolor; // For now, Zen boosts data is just a color.
namespace zen {
struct nsZenAccentOklab {
float L, a, b;
float vibranceBase; // 1.0f - ((contrast - 128) / 128)
float accentLOffset; // 0.25f + L, precomputed
nscolor accentNS; // Used to keep track of the original accent color
};
class nsZenBoostsBackend final {
public:
explicit nsZenBoostsBackend() = default;
@@ -42,8 +49,9 @@ class nsZenBoostsBackend final {
* @param aPresContext The presentation context to use for filtering.
* @return The filtered color.
*/
static auto FilterColorFromPresContext(nscolor aColor,
nsPresContext* aPresContext = nullptr) -> nscolor;
static auto FilterColorFromPresContext(nscolor aColor,
nsPresContext* aPresContext = nullptr)
-> nscolor;
/**
* @brief Called when a presshell is entered during rendering.
@@ -64,6 +72,8 @@ class nsZenBoostsBackend final {
*/
RefPtr<mozilla::dom::BrowsingContext> mCurrentBrowsingContext;
static nsZenAccentOklab mCachedAccent;
public:
/**
* @brief Get the singleton instance of the ZenBoostsBackend.

View File

@@ -1,10 +1,7 @@
#filter substitution
<?xml version="1.0"?>
# -*- Mode: HTML -*-
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#filter substitution <?xml version="1.0"?> # -*- Mode: HTML -*- # # This Source
Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a
copy of the MPL was not distributed with this # file, You can obtain one at
http://mozilla.org/MPL/2.0/.
<!DOCTYPE window>
@@ -20,132 +17,264 @@
scrolling="false"
macanimationtype="document"
windowsmica="true"
data-l10n-sync="true">
<head>
<link rel="stylesheet" href="chrome://browser/skin/" />
<link rel="stylesheet" href="chrome://global/skin/global.css" />
data-l10n-sync="true"
>
<head>
<link rel="stylesheet" href="chrome://browser/skin/" />
<link rel="stylesheet" href="chrome://global/skin/global.css" />
<link rel="stylesheet" href="chrome://browser/content/zen-styles/zen-boosts.css" />
<link rel="stylesheet" href="chrome://browser/content/zen-styles/zen-buttons.css" />
<link rel="stylesheet" href="chrome://browser/content/zen-styles/zen-theme.css" />
<link rel="stylesheet" href="chrome://browser/content/zen-styles/zen-animations.css" />
<link rel="stylesheet" href="chrome://browser/content/zen-styles/zen-panel-ui.css" />
<link rel="stylesheet" href="chrome://browser/skin/zen-icons/icons.css" />
<link rel="stylesheet" href="chrome://browser/content/zen-styles/zen-advanced-color-options.css" />
<link
rel="stylesheet"
href="chrome://browser/content/zen-styles/zen-boosts.css"
/>
<link
rel="stylesheet"
href="chrome://browser/content/zen-styles/zen-buttons.css"
/>
<link
rel="stylesheet"
href="chrome://browser/content/zen-styles/zen-theme.css"
/>
<link
rel="stylesheet"
href="chrome://browser/content/zen-styles/zen-animations.css"
/>
<link
rel="stylesheet"
href="chrome://browser/content/zen-styles/zen-panel-ui.css"
/>
<link rel="stylesheet" href="chrome://browser/skin/zen-icons/icons.css" />
<link
rel="stylesheet"
href="chrome://browser/content/zen-styles/zen-advanced-color-options.css"
/>
<link rel="localization" href="browser/zen-boosts.ftl"/>
<link rel="localization" href="browser/zen-boosts.ftl" />
<!-- Loading in the window module -->
<script>
const { nsZenBoostEditor } = ChromeUtils.importESModule( "resource:///modules/zen/boosts/ZenBoostsEditor.mjs" );
window.addEventListener("load", () => {
window.boostEditor = new nsZenBoostEditor(document, window.domain, window, window.openerWindow);
});
</script>
</head>
<html:body xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<vbox flex="1" id="zen-boost-editor-root">
<hbox id="zen-boost-head-wrapper">
<button id="zen-boost-close" class="subviewbutton mod-button title-button"></button>
<hbox id="zen-boost-name" flex="1">
<hbox id="zen-boost-name-container">
<html:p id="zen-boost-name-text"></html:p>
<!-- Loading in the window module -->
<script>
const { nsZenBoostEditor } = ChromeUtils.importESModule(
"resource:///modules/zen/boosts/ZenBoostsEditor.mjs"
);
window.addEventListener("load", () => {
window.boostEditor = new nsZenBoostEditor(
document,
window.domain,
window,
window.openerWindow
);
});
</script>
</head>
<html:body
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
>
<vbox flex="1" id="zen-boost-editor-root">
<hbox id="zen-boost-head-wrapper">
<button
id="zen-boost-close"
class="subviewbutton mod-button title-button"
></button>
<hbox id="zen-boost-name" flex="1">
<hbox id="zen-boost-name-container">
<html:p id="zen-boost-name-text"></html:p>
</hbox>
</hbox>
<button
data-l10n-id="zen-boost-shuffle"
id="zen-boost-shuffle"
class="subviewbutton mod-button title-button"
></button>
</hbox>
<button data-l10n-id="zen-boost-shuffle" id="zen-boost-shuffle" class="subviewbutton mod-button title-button"></button>
</hbox>
<vbox flex="1" id="zen-boost-filter-wrapper">
<hbox class="zen-boost-color-picker-gradient zen-boost-panel-disabled">
<button data-l10n-id="zen-boost-magic-theme" id="zen-boost-magic-theme" class="subviewbutton mod-button"></button>
<html:div class="zen-boost-color-picker-dot"></html:div>
<html:div class="zen-boost-color-picker-circle"></html:div>
</hbox>
<hbox flex="1" id="zen-boost-toolbar-wrapper-colors">
<button data-l10n-id="zen-boost-invert" id="zen-boost-invert" class="subviewbutton mod-button small"></button>
<button data-l10n-id="zen-boost-controls" id="zen-boost-controls" class="subviewbutton mod-button small"></button>
<button data-l10n-id="zen-boost-disable" id="zen-boost-disable" class="subviewbutton mod-button small"></button>
</hbox>
<html:div id="zen-boost-font-wrapper">
<vbox id="zen-boost-font-grid">
<!-- Font buttons will be injected here -->
</vbox>
<html:div class="visible-separator"></html:div>
<hbox flex="1" id="zen-boost-font-toolbar">
<html:select name="font" id="zen-boost-font-select" class="mod-button">
<!-- Additional font options will be injected here -->
</html:select>
<button data-l10n-id="zen-boost-text-case-toggle" id="zen-boost-text-case-toggle" class="subviewbutton mod-button"></button>
<vbox flex="1" id="zen-boost-filter-wrapper">
<hbox class="zen-boost-color-picker-gradient zen-boost-panel-disabled">
<button
data-l10n-id="zen-boost-magic-theme"
id="zen-boost-magic-theme"
class="subviewbutton mod-button"
></button>
<html:div class="zen-boost-color-picker-dot"></html:div>
<html:div class="zen-boost-color-picker-circle"></html:div>
</hbox>
</html:div>
<button id="zen-boost-zap" class="subviewbutton mod-button big-button toggleable-button">
<html:p data-l10n-id="zen-boost-zap" id="zen-boost-zap-text"></html:p>
<html:p id="zen-boost-zap-value"></html:p>
</button>
<hbox flex="1" id="zen-boost-toolbar-wrapper-colors">
<button
data-l10n-id="zen-boost-invert"
id="zen-boost-invert"
class="subviewbutton mod-button small"
></button>
<button
data-l10n-id="zen-boost-controls"
id="zen-boost-controls"
class="subviewbutton mod-button small"
></button>
<button
data-l10n-id="zen-boost-disable"
id="zen-boost-disable"
class="subviewbutton mod-button small"
></button>
</hbox>
<button id="zen-boost-code" class="subviewbutton mod-button big-button">
<html:p data-l10n-id="zen-boost-code" id="zen-boost-code-text"></html:p>
</button>
<hbox flex="1" id="zen-boost-toolbar-wrapper">
<button data-l10n-id="zen-boost-save" id="zen-boost-save" class="subviewbutton mod-button med"></button>
<button data-l10n-id="zen-boost-load" id="zen-boost-load" class="subviewbutton mod-button med"></button>
<html:div id="zen-boost-font-wrapper">
<vbox id="zen-boost-font-grid">
<!-- Font buttons will be injected here -->
</vbox>
<html:div class="visible-separator"></html:div>
<hbox flex="1" id="zen-boost-font-toolbar">
<html:select
name="font"
id="zen-boost-font-select"
class="mod-button"
>
<!-- Additional font options will be injected here -->
</html:select>
<button
data-l10n-id="zen-boost-text-case-toggle"
id="zen-boost-text-case-toggle"
class="subviewbutton mod-button"
></button>
</hbox>
</html:div>
<button
id="zen-boost-zap"
class="subviewbutton mod-button big-button toggleable-button"
>
<html:p data-l10n-id="zen-boost-zap" id="zen-boost-zap-text"></html:p>
<html:p id="zen-boost-zap-value"></html:p>
</button>
<button id="zen-boost-code" class="subviewbutton mod-button big-button">
<html:p
data-l10n-id="zen-boost-code"
id="zen-boost-code-text"
></html:p>
</button>
<hbox flex="1" id="zen-boost-toolbar-wrapper">
<button
data-l10n-id="zen-boost-save"
id="zen-boost-save"
class="subviewbutton mod-button med"
></button>
<button
data-l10n-id="zen-boost-load"
id="zen-boost-load"
class="subviewbutton mod-button med"
></button>
</hbox>
</vbox>
</vbox>
<vbox flex="1" id="zen-boost-code-editor-root">
<hbox id="zen-boost-code-top-bar">
<button id="zen-boost-back" class="subviewbutton mod-button big-button">
<html:p
data-l10n-id="zen-boost-back"
id="zen-boost-back-text"
></html:p>
</button>
</hbox>
<vbox flex="1" id="zen-boost-code-editor"> </vbox>
<hbox id="zen-boost-code-bottom-bar">
<button
data-l10n-id="zen-boost-css-picker"
id="zen-boost-css-picker"
class="subviewbutton mod-button big-button toggleable-button"
></button>
<button
data-l10n-id="zen-boost-css-inspector"
id="zen-boost-css-inspector"
class="subviewbutton mod-button big-button"
></button>
</hbox>
</vbox>
</vbox>
<vbox flex="1" id="zen-boost-code-editor-root">
<hbox id="zen-boost-code-top-bar">
<button id="zen-boost-back" class="subviewbutton mod-button big-button">
<html:p data-l10n-id="zen-boost-back" id="zen-boost-back-text"></html:p>
</button>
</hbox>
<vbox flex="1" id="zen-boost-code-editor">
</vbox>
<hbox id="zen-boost-code-bottom-bar">
<button data-l10n-id="zen-boost-css-picker" id="zen-boost-css-picker" class="subviewbutton mod-button big-button toggleable-button"></button>
<button data-l10n-id="zen-boost-css-inspector" id="zen-boost-css-inspector" class="subviewbutton mod-button big-button"></button>
</hbox>
</vbox>
<popupset id="mainPopupSet">
<panel
type="arrow"
popupalign="topmiddle"
id="zen-boost-advanced-color-options-panel"
>
<html:div flex="1" id="zen-boost-advanced-color-options-container">
<p data-l10n-id="zen-bootst-color-contrast"></p>
<html:input
id="zen-boost-color-contrast"
type="range"
min="0"
max="1"
value="0.5"
step="0.01"
/>
<separator />
<p data-l10n-id="zen-bootst-color-brightness"></p>
<html:input
id="zen-boost-color-brightness"
type="range"
min="0"
max="1"
value="0.5"
step="0.01"
/>
<separator />
<p data-l10n-id="zen-bootst-color-original-saturation"></p>
<html:input
id="zen-boost-color-saturation"
type="range"
min="0"
max="1"
value="0.5"
step="0.01"
/>
</html:div>
</panel>
</popupset>
<popupset id="mainPopupSet">
<panel type="arrow" popupalign="topmiddle" id="zen-boost-advanced-color-options-panel">
<html:div flex="1" id="zen-boost-advanced-color-options-container">
<p data-l10n-id="zen-bootst-color-contrast"></p>
<html:input id="zen-boost-color-contrast" type="range" min="0" max="1" value="0.5" step="0.01"/>
<separator />
<p data-l10n-id="zen-bootst-color-brightness"></p>
<html:input id="zen-boost-color-brightness" type="range" min="0" max="1" value="0.5" step="0.01"/>
<separator />
<p data-l10n-id="zen-bootst-color-original-saturation"></p>
<html:input id="zen-boost-color-saturation" type="range" min="0" max="1" value="0.5" step="0.01"/>
</html:div>
</panel>
</popupset>
<menupopup id="zenBoostContextMenu">
<menuitem
data-l10n-id="zen-boost-edit-rename"
id="zen-boost-edit-rename"
command="cmd_zenBoostEditName"
/>
<menuitem
data-l10n-id="zen-boost-edit-shuffle"
id="zen-boost-edit-shuffle"
command="cmd_zenBoostShuffle"
/>
<menuitem
data-l10n-id="zen-boost-edit-reset"
id="zen-boost-edit-reset"
command="cmd_zenBoostReset"
/>
<menuseparator />
<menuitem
data-l10n-id="zen-boost-edit-delete"
id="zen-boost-edit-delete"
command="cmd_zenBoostDelete"
/>
</menupopup>
<menupopup id="zenBoostContextMenu">
<menuitem data-l10n-id="zen-boost-edit-rename" id="zen-boost-edit-rename" command="cmd_zenBoostEditName" />
<menuitem data-l10n-id="zen-boost-edit-shuffle" id="zen-boost-edit-shuffle" command="cmd_zenBoostShuffle" />
<menuitem data-l10n-id="zen-boost-edit-reset" id="zen-boost-edit-reset" command="cmd_zenBoostReset" />
<menuseparator/>
<menuitem data-l10n-id="zen-boost-edit-delete" id="zen-boost-edit-delete" command="cmd_zenBoostDelete" />
</menupopup>
<commandset id="zenBoostCommandSet">
<command
id="cmd_zenBoostEditName"
oncommand="window.boostEditor.editBoostName()"
/>
<commandset id="zenBoostCommandSet">
<command id="cmd_zenBoostEditName"
oncommand="window.boostEditor.editBoostName();" />
<command
id="cmd_zenBoostShuffle"
oncommand="window.boostEditor.shuffleBoost()"
/>
<command id="cmd_zenBoostShuffle"
oncommand="window.boostEditor.shuffleBoost();" />
<command
id="cmd_zenBoostReset"
oncommand="window.boostEditor.resetBoost()"
/>
<command id="cmd_zenBoostReset"
oncommand="window.boostEditor.resetBoost();" />
<command id="cmd_zenBoostDelete"
oncommand="window.boostEditor.deleteBoost();" />
</commandset>
</html:body>
<command
id="cmd_zenBoostDelete"
oncommand="window.boostEditor.deleteBoost()"
/>
</commandset>
</html:body>
</html>

View File

@@ -31,7 +31,7 @@
background-color: #f6f6f8c0;
}
border: solid 0px #ededef;
border: solid 0 #ededef;
border-bottom-width: 1px;
}
@@ -70,7 +70,7 @@
background-color: #f6f6f8c0;
}
border: solid 0px #ededef;
border: solid 0 #ededef;
border-top-width: 1px;
display: flex;
@@ -100,8 +100,8 @@ body {
width: 100%;
height: 100%;
padding: 0px;
margin: 0px;
padding: 0;
margin: 0;
& button {
border-radius: 6px;
@@ -216,7 +216,7 @@ body {
#zen-boost-font-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 0px;
gap: 0;
width: 100%;
padding: 2px;
@@ -299,7 +299,7 @@ body {
list-style-type: none;
& hbox {
margin-right: 0px;
margin-right: 0;
}
}
@@ -375,20 +375,20 @@ body {
}
#zen-boost-close {
margin-left: 0px;
margin-left: 0;
margin-right: 6px;
@media (-moz-platform: macos) {
margin-left: 6px;
margin-right: 0px;
margin-right: 0;
}
}
#zen-boost-shuffle {
margin-right: 0px;
margin-right: 0;
margin-left: 4px;
@media (-moz-platform: macos) {
margin-right: 10px;
margin-left: 0px;
margin-left: 0;
}
}
@@ -415,7 +415,7 @@ body {
margin-top: 12px;
z-index: 4;
box-shadow: 0px 2px 4px #00000010;
box-shadow: 0 2px 4px #00000010;
position: relative;
margin-left: auto;
@@ -456,12 +456,11 @@ body {
transition: 0.4s opacity cubic-bezier(0.075, 0.82, 0.165, 1) !important;
color: #727272;
background-color: transparent;
background: none;
font-size: 9pt;
padding: 0px;
padding: 0;
text-indent: 2px;
margin-left: 8px;
@@ -492,7 +491,7 @@ body {
}
#zen-boost-font-wrapper {
box-shadow: 0px 2px 8px #00000010;
box-shadow: 0 2px 8px #00000010;
background-color: #ffffff;
border-radius: 6px;
@@ -659,10 +658,10 @@ body {
position: relative;
overflow: hidden;
border-radius: 16px;
box-shadow: 0px 4px 12px #00000021;
box-shadow: 0 4px 12px #00000021;
width: 100%;
aspect-ratio: 1 /1;
margin: 10px 0px 4px 0px;
margin: 10px 0 4px 0;
min-height: calc(var(--panel-width) - var(--panel-padding) * 2 - 2px);
@@ -687,7 +686,7 @@ body {
}
& .zen-boost-color-picker-dot {
box-shadow: 0px 2px 4px #00000022;
box-shadow: 0 2px 4px #00000022;
position: absolute;
z-index: 4;
width: 24px;

View File

@@ -37,7 +37,7 @@
border-radius: 24px;
}
box-shadow: 0px 0px 20px#00000050;
box-shadow: 0 0 20px#00000050;
scale: 1;
opacity: 1;
@@ -130,16 +130,16 @@
border: none;
color: #dadada;
box-shadow: 0px 0px 15px #00000052;
box-shadow: 0 0 15px #00000052;
&:hover {
box-shadow: 0px 0px 14px #00000066;
box-shadow: 0 0 14px #00000066;
background:
linear-gradient(to right, transparent var(--related-elements-value), gray var(--related-elements-value)),
linear-gradient(to top, rgb(247, 66, 0), rgb(245, 134, 86));
@media not (-moz-platform: windows) {
box-shadow: 0px 0px 20px #00000077;
box-shadow: 0 0 20px #00000077;
}
}
}
@@ -151,7 +151,7 @@
border: none;
color: #dadada;
box-shadow: 0px 0px 15px #00000052;
box-shadow: 0 0 15px #00000052;
}
#select-cancel {
@@ -165,7 +165,7 @@
border: none;
color: #dadada;
box-shadow: 0px 0px 15px #00000052;
box-shadow: 0 0 15px #00000052;
}
#selector-preview {

View File

@@ -16,10 +16,10 @@
@keyframes zap-border-in {
0% {
left: 0px;
top: 0px;
bottom: 0px;
right: 0px;
left: 0;
top: 0;
bottom: 0;
right: 0;
}
100% {

View File

@@ -8,8 +8,7 @@
#include "nsIZenDragAndDrop.h"
#include "nsCOMPtr.h"
#define ZEN_DND_MANAGER_CONTRACTID \
"@mozilla.org/zen/drag-and-drop;1"
#define ZEN_DND_MANAGER_CONTRACTID "@mozilla.org/zen/drag-and-drop;1"
namespace zen {

View File

@@ -33,7 +33,8 @@ nsZenModsBackend::nsZenModsBackend() { (void)CheckEnabled(); }
auto nsZenModsBackend::CheckEnabled() -> void {
// Check if the mods backend is enabled based on the preference.
bool inSafeMode = false;
if (nsCOMPtr<nsIXULRuntime> appInfo = do_GetService("@mozilla.org/xre/app-info;1")) {
if (nsCOMPtr<nsIXULRuntime> appInfo =
do_GetService("@mozilla.org/xre/app-info;1")) {
appInfo->GetInSafeMode(&inSafeMode);
}
mEnabled = !inSafeMode &&

View File

@@ -23,7 +23,10 @@ add_task(async function test_getSelectionPath_basic() {
for (let i = 0; i <= 7; i++) {
const path = component.getSelectionPath(doc, i, child2);
ok(path, `getSelectionPath should return a path for relatedValueIndex=${i}`);
ok(
path,
`getSelectionPath should return a path for relatedValueIndex=${i}`
);
const selectedElements = doc.querySelectorAll(path);
@@ -33,6 +36,9 @@ add_task(async function test_getSelectionPath_basic() {
"For relatedValueIndex=1 there should be exactly one queried element"
);
ok(selectedElements.length >= 1, "CSS path should select at least one element");
ok(
selectedElements.length >= 1,
"CSS path should select at least one element"
);
}
});

View File

@@ -8,7 +8,11 @@ add_task(async function test_getSelectionPath_invalidNode() {
const component = new SelectorComponent(doc, null, [], () => {});
// Null element
Assert.equal(component.getSelectionPath(doc, 0, null), null, "Null element should return null");
Assert.equal(
component.getSelectionPath(doc, 0, null),
null,
"Null element should return null"
);
// Body element
Assert.equal(

View File

@@ -34,6 +34,10 @@ add_task(async function test_getSelectionPath_nthchild() {
"Selector must include the selected node"
);
Assert.equal(selectedElements.length, 1, "Selector should uniquely identify the element");
Assert.equal(
selectedElements.length,
1,
"Selector should uniquely identify the element"
);
}
});

View File

@@ -101,7 +101,7 @@ export class nsZenSiteDataPanel {
#initBrowserListeners() {
Services.obs.addObserver(this, "zen-boosts-update");
this.window.gBrowser.addProgressListener({
onLocationChange: (aWebProgress) => {
onLocationChange: aWebProgress => {
if (aWebProgress.isTopLevel) {
this.checkIfTabIsBoosted();
}
@@ -266,7 +266,7 @@ export class nsZenSiteDataPanel {
if (boosts) {
const activeBoostId = lazy.gZenBoostsManager.getActiveBoostId(domain);
boosts.forEach((boost) => {
boosts.forEach(boost => {
const boostData = boost.boostEntry.boostData;
if (!boostData.changeWasMade) {
return;
@@ -296,7 +296,14 @@ export class nsZenSiteDataPanel {
this.#setSiteBoost();
}
#createBoostPanelItem(iconClass, title, description, actionId, boost = null, enabled = false) {
#createBoostPanelItem(
iconClass,
title,
description,
actionId,
boost = null,
enabled = false
) {
const container = this.document.createXULElement("hbox");
container.classList.add("permission-popup-boost-item");
@@ -311,7 +318,10 @@ export class nsZenSiteDataPanel {
}
const img = this.document.createXULElement("toolbarbutton");
img.classList.add("permission-popup-boost-icon", "zen-site-data-boost-icon");
img.classList.add(
"permission-popup-boost-icon",
"zen-site-data-boost-icon"
);
img.setAttribute("closemenu", "none");
img.classList.add(iconClass);
@@ -341,7 +351,7 @@ export class nsZenSiteDataPanel {
editorButton.classList.add("zen-permission-popup-boost-editor-button");
container.appendChild(editorButton);
editorButton.addEventListener("click", (event) => {
editorButton.addEventListener("click", event => {
event.stopPropagation(); // Prevents the container event
this.#onBoostClick(event);
});
@@ -425,7 +435,9 @@ export class nsZenSiteDataPanel {
}
#resetSiteOptionsList() {
const settingsList = this.document.getElementById("zen-site-data-settings-list");
const settingsList = this.document.getElementById(
"zen-site-data-settings-list"
);
settingsList.innerHTML = "";
const boostList = this.document.getElementById("zen-site-data-boost-list");
boostList.innerHTML = "";
@@ -865,7 +877,10 @@ export class nsZenSiteDataPanel {
case "zen-site-data-edit-boost": {
const boostId = target.getAttribute("data-boost-id");
const uri = this.window.gBrowser.currentURI;
const boost = lazy.gZenBoostsManager.loadBoostFromStore(domain, boostId);
const boost = lazy.gZenBoostsManager.loadBoostFromStore(
domain,
boostId
);
lazy.gZenBoostsManager.openBoostWindow(this.window, boost, uri);
this.unifiedPanel.hidePopup();
break;