# HG changeset patch # User Geoff Lankow # Date 1561537659 -7200 # Node ID b58f19c12f2270fd3d828f68882477a1e1cd0304 # Parent 246e8ec143c3bf472b5a0eaf848f20e42dda4774 Bug 1558384 - Use CSS variables for calendar item colours rather than dynamically adding CSS rules. r=philipp DONTBUILD diff --git a/calendar/base/content/agenda-listbox.xml b/calendar/base/content/agenda-listbox.xml --- a/calendar/base/content/agenda-listbox.xml +++ b/calendar/base/content/agenda-listbox.xml @@ -267,22 +267,19 @@ durationbox.textContent += " " + aItem.title; } this.refreshColor(); ]]> diff --git a/calendar/base/content/calendar-chrome-startup.js b/calendar/base/content/calendar-chrome-startup.js --- a/calendar/base/content/calendar-chrome-startup.js +++ b/calendar/base/content/calendar-chrome-startup.js @@ -40,16 +40,19 @@ function commonInitCalendar() { // Start alarm service Cc["@mozilla.org/calendar/alarm-service;1"].getService(Ci.calIAlarmService).startup(); document.getElementById("calsidebar_splitter").addEventListener("command", onCalendarViewResize); window.addEventListener("resize", onCalendarViewResize, true); // Set up the category colors categoryManagement.initCategories(); + // Set calendar color CSS on this window + cal.view.colorTracker.registerWindow(window); + // Set up window pref observers calendarWindowPrefs.init(); // Set up the available modifiers for each platform. let keys = document.querySelectorAll("#calendar-keys > key"); let platform = AppConstants.platform; for (let key of keys) { if (key.hasAttribute("modifiers-" + platform)) { diff --git a/calendar/base/content/calendar-month-view.xml b/calendar/base/content/calendar-month-view.xml --- a/calendar/base/content/calendar-month-view.xml +++ b/calendar/base/content/calendar-month-view.xml @@ -12,17 +12,16 @@ xmlns:xbl="http://www.mozilla.org/xbl"> @@ -236,22 +235,23 @@ @@ -159,18 +158,19 @@ locationLabel.value = showLocation && location ? location : ""; setBooleanAttribute(locationLabel, "hidden", !showLocation || !location); ]]> 0) { let cssClassesArray = categoriesArray.map(cal.view.formatStringForCSSRule); this.setAttribute("categories", cssClassesArray.join(" ")); } // Add alarm icons as needed. let alarms = item.getAlarms({}); diff --git a/calendar/base/content/calendar-views.js b/calendar/base/content/calendar-views.js --- a/calendar/base/content/calendar-views.js +++ b/calendar/base/content/calendar-views.js @@ -1,14 +1,14 @@ /* 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/. */ /* exported switchToView, getSelectedDay, scheduleMidnightUpdate, - * updateStyleSheetForViews, observeViewDaySelect, toggleOrientation, + * observeViewDaySelect, toggleOrientation, * toggleWorkdaysOnly, toggleTasksInView, toggleShowCompletedInView, * goToDate, getLastCalendarView, deleteSelectedEvents, * editSelectedEvents, selectAllEvents */ ChromeUtils.import("resource://calendar/modules/calUtils.jsm"); ChromeUtils.import("resource://calendar/modules/calRecurrenceUtils.jsm"); ChromeUtils.import("resource://gre/modules/Services.jsm"); @@ -387,43 +387,16 @@ function getViewStyleSheet() { break; } } } return getViewStyleSheet.sheet; } /** - * Updates the view stylesheet to contain rules that give all boxes with class - * .calendar-color-box and an attribute calendar-id="" the - * background color of the specified calendar. - * - * @param aCalendar The calendar to update the stylesheet for. - */ -function updateStyleSheetForViews(aCalendar) { - if (!updateStyleSheetForViews.ruleCache) { - updateStyleSheetForViews.ruleCache = {}; - } - let ruleCache = updateStyleSheetForViews.ruleCache; - - if (!(aCalendar.id in ruleCache)) { - // We haven't create a rule for this calendar yet, do so now. - let sheet = getViewStyleSheet(); - let ruleString = '.calendar-color-box[calendar-id="' + aCalendar.id + '"] {} '; - let ruleIndex = sheet.insertRule(ruleString, sheet.cssRules.length); - - ruleCache[aCalendar.id] = sheet.cssRules[ruleIndex]; - } - - let color = aCalendar.getProperty("color") || "#A8C2E1"; - ruleCache[aCalendar.id].style.backgroundColor = color; - ruleCache[aCalendar.id].style.color = cal.view.getContrastingTextColor(color); -} - -/** * Category preferences observer. Used to update the stylesheets for category * colors. * * Note we need to keep the categoryPrefBranch variable outside of * initCategories since branch observers only live as long as the branch object * is alive, and out of categoryManagement to avoid cyclic references. */ var categoryPrefBranch; diff --git a/calendar/base/content/widgets/calendar-list-tree.xml b/calendar/base/content/widgets/calendar-list-tree.xml --- a/calendar/base/content/widgets/calendar-list-tree.xml +++ b/calendar/base/content/widgets/calendar-list-tree.xml @@ -264,20 +264,16 @@ onAddItem: function(aItem) { }, onModifyItem: function(aNewItem, aOldItem) { }, onDeleteItem: function(aDeletedItem) { }, onError: function(aCalendar, aErrNo, aMessage) { }, onPropertyChanged: function(aCalendar, aName, aValue, aOldValue) { switch (aName) { case "color": - // TODO See other TODO in this file about updateStyleSheetForViews - if ("updateStyleSheetForViews" in window) { - updateStyleSheetForViews(aCalendar); - } this.listTree.updateCalendarColor(aCalendar); // Fall through, update item in any case case "name": case "currentStatus": case "readOnly": case "disabled": this.listTree.updateCalendar(aCalendar); // Fall through, update commands in any cases. @@ -487,25 +483,16 @@ if (!composite.defaultCalendar || aCalendar.id == composite.defaultCalendar.id) { this.tree.view.selection.select(this.mCalendarList.length - 1); } this.updateCalendarColor(aCalendar); - // TODO This should be done only once outside of this binding, but to - // do that right, we need to have an easy way to register an observer - // all calendar properties. This could be the calendar manager that - // holds an observer on every calendar anyway, which would then use the - // global observer service which clients can register with. - if ("updateStyleSheetForViews" in window) { - updateStyleSheetForViews(aCalendar); - } - // Watch the calendar for changes, i.e color. aCalendar.addObserver(this.calObserver); // Adding a calendar causes the sortorder to be changed. this.sortOrderChanged(); // Re-assign defaultCalendar, sometimes it is not the right one after // remove & add calendar. diff --git a/calendar/base/modules/utils/calViewUtils.jsm b/calendar/base/modules/utils/calViewUtils.jsm --- a/calendar/base/modules/utils/calViewUtils.jsm +++ b/calendar/base/modules/utils/calViewUtils.jsm @@ -282,8 +282,83 @@ var calview = { if (cmp != 0) { return cmp; } cmp = (a.title > b.title) - (a.title < b.title); return cmp; } }; + +/** + * Adds CSS variables for each calendar to registered windows for coloring + * UI elements. Automatically tracks calendar creation, changes, and deletion. + */ +calview.colorTracker = { + calendars: null, + windows: new Set(), + QueryInterface: XPCOMUtils.generateQI([ + Ci.calICalendarManagerObserver, + Ci.calIObserver + ]), + + // The only public method. Deregistration is not required. + registerWindow(aWindow) { + if (this.calendars === null) { + let manager = cal.getCalendarManager(); + this.calendars = new Set(manager.getCalendars({})); + manager.addObserver(this); + manager.addCalendarObserver(this); + } + + this.windows.add(aWindow); + aWindow.addEventListener("unload", () => this.windows.delete(aWindow)); + for (let calendar of this.calendars) { + this._addToWindow(aWindow, calendar); + } + }, + _addToWindow(aWindow, aCalendar) { + let cssSafeId = cal.view.formatStringForCSSRule(aCalendar.id); + let style = aWindow.document.documentElement.style; + let backColor = aCalendar.getProperty("color") || "#a8c2e1"; + let foreColor = calview.getContrastingTextColor(backColor); + style.setProperty(`--calendar-${cssSafeId}-backcolor`, backColor); + style.setProperty(`--calendar-${cssSafeId}-forecolor`, foreColor); + }, + _removeFromWindow(aWindow, aCalendar) { + let cssSafeId = cal.view.formatStringForCSSRule(aCalendar.id); + let style = aWindow.document.documentElement.style; + style.removeProperty(`--calendar-${cssSafeId}-backcolor`); + style.removeProperty(`--calendar-${cssSafeId}-forecolor`); + }, + + // calICalendarManagerObserver methods + onCalendarRegistered(aCalendar) { + this.calendars.add(aCalendar); + for (let window of this.windows) { + this._addToWindow(window, aCalendar); + } + }, + onCalendarUnregistering(aCalendar) { + this.calendars.delete(aCalendar); + for (let window of this.windows) { + this._removeFromWindow(window, aCalendar); + } + }, + onCalendarDeleting(aCalendar) {}, + + // calIObserver methods + onStartBatch() {}, + onEndBatch() {}, + onLoad() {}, + onAddItem(aItem) {}, + onModifyItem(aNewItem, aOldItem) {}, + onDeleteItem(aDeletedItem) {}, + onError(aCalendar, aErrNo, aMessage) {}, + onPropertyChanged(aCalendar, aName, aValue, aOldValue) { + if (aName == "color") { + for (let window of this.windows) { + this._addToWindow(window, aCalendar); + } + } + }, + onPropertyDeleting(aCalendar, aName) {}, +}; diff --git a/calendar/base/themes/common/calendar-views.css b/calendar/base/themes/common/calendar-views.css --- a/calendar/base/themes/common/calendar-views.css +++ b/calendar/base/themes/common/calendar-views.css @@ -684,16 +684,18 @@ calendar-month-day-box-item { } .calendar-color-box { /* This rule should be adopted if the alarm image size is changed */ min-height: 13px; background-image: linear-gradient(rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.01) 50%, rgba(0, 0, 0, 0.05)); border: 1px solid transparent; border-radius: 2px; + background-color: var(--item-backcolor); + color: var(--item-forecolor); } calendar-month-day-box calendar-month-day-box-item[allday="true"] .calendar-color-box { border-color: rgba(0, 0, 0, 0.5); box-shadow: inset -1px -1px 0 rgba(255, 255, 255, 0.7), inset 1px 1px 0 rgba(255, 255, 255, 0.7); } .calendar-month-day-box-item-label { diff --git a/calendar/providers/gdata/content/gdata-calendar-creation.js b/calendar/providers/gdata/content/gdata-calendar-creation.js --- a/calendar/providers/gdata/content/gdata-calendar-creation.js +++ b/calendar/providers/gdata/content/gdata-calendar-creation.js @@ -225,18 +225,14 @@ ChromeUtils.import("resource://gdata-pro document.addEventListener("DOMContentLoaded", () => { // Older versions of Lightning don't set the onselect attribute at all. let calendarFormat = document.getElementById("calendar-format"); if (!calendarFormat.hasAttribute("onselect")) { calendarFormat.setAttribute("onselect", "gdataSelectProvider(this.value)"); } - if (!("updateStyleSheetForViews" in window)) { - window.updateStyleSheetForViews = function() {}; - } - if (document.getElementById("gdata-session").pageIndex == -1) { let wizard = document.documentElement; wizard._initPages(); } }); }).call(window);