Bug 1440249 - Move (almost) all remaining calUtils.js functions to calUtils.jsm and friends. r=MakeMyDay
authorPhilipp Kewisch <mozilla@kewis.ch>
Wed, 21 Feb 2018 21:24:35 +0100
changeset 31378 c75db1860608b515e8e0f6a2a5df4ec165f53525
parent 31377 7b17cda8efa8228690cd1a15d85d843689b0460b
child 31379 10085a3cc8a189498141c21b58018f277e0055fc
push id383
push userclokep@gmail.com
push dateMon, 07 May 2018 21:52:48 +0000
reviewersMakeMyDay
bugs1440249
Bug 1440249 - Move (almost) all remaining calUtils.js functions to calUtils.jsm and friends. r=MakeMyDay MozReview-Commit-ID: BZSH1l9R99P
calendar/base/content/calendar-menus.xml
calendar/base/content/calendar-task-tree.js
calendar/base/modules/calUtils.jsm
calendar/base/modules/calUtilsCompat.jsm
calendar/base/src/calItemBase.js
calendar/base/src/calRecurrenceInfo.js
calendar/base/src/calUtils.js
--- a/calendar/base/content/calendar-menus.xml
+++ b/calendar/base/content/calendar-menus.xml
@@ -47,19 +47,18 @@
             if (gTabmail && gTabmail.currentTabInfo.mode.type == "calendarTask") {
                 // We are in a task tab (editing a single task).
                 propertyValue = gConfig[this.mType];
             } else {
                 // We are in the Tasks tab.
                 let tasks = getSelectedTasks();
                 let tasksSelected = (tasks != null) && (tasks.length > 0);
                 if (tasksSelected) {
-                    let task = tasks[0];
-                    if (cal.isPropertyValueSame(tasks, this.mType)) {
-                        propertyValue = task[this.mType];
+                    if (tasks.every(task => task[this.mType] == tasks[0][this.mType])) {
+                        propertyValue = tasks[0][this.mType];
                     }
                 } else {
                     cal.view.applyAttributeToMenuChildren(this, "disabled", !tasksSelected);
                 }
             }
             if (propertyValue || propertyValue == 0) {
                 let command = document.getElementById("calendar_" + this.mType + "-" + propertyValue + "_command");
                 if (command) {
--- a/calendar/base/content/calendar-task-tree.js
+++ b/calendar/base/content/calendar-task-tree.js
@@ -23,17 +23,17 @@ function addCalendarNames(aEvent) {
     let calendarMenuPopup = aEvent.target;
     while (calendarMenuPopup.hasChildNodes()) {
         calendarMenuPopup.lastChild.remove();
     }
     let tasks = getSelectedTasks(aEvent);
     let tasksSelected = (tasks.length > 0);
     if (tasksSelected) {
         let selIndex = appendCalendarItems(tasks[0], calendarMenuPopup, null, "contextChangeTaskCalendar(event);");
-        if (cal.isPropertyValueSame(tasks, "calendar") && (selIndex > -1)) {
+        if (tasks.every(task => task.calendar == tasks[0].calendar) && selIndex > -1) {
             calendarMenuPopup.childNodes[selIndex].setAttribute("checked", "true");
         }
     }
 }
 
 /**
  * Change the opening context menu for the selected tasks.
  *
@@ -111,17 +111,17 @@ function changeMenuForTask(aEvent) {
         "calendar_general-postpone_command"
     ];
     commands.forEach(goUpdateCommand);
 
     let tasks = getSelectedTasks(aEvent);
     let tasksSelected = (tasks.length > 0);
     if (tasksSelected) {
         let cmd = document.getElementById("calendar_toggle_completed_command");
-        if (cal.isPropertyValueSame(tasks, "isCompleted")) {
+        if (tasks.every(task => task.isCompleted == tasks[0].isCompleted)) {
             setBooleanAttribute(cmd, "checked", tasks[0].isCompleted);
         } else {
             setBooleanAttribute(cmd, "checked", false);
         }
     }
 }
 
 /**
--- a/calendar/base/modules/calUtils.jsm
+++ b/calendar/base/modules/calUtils.jsm
@@ -1,23 +1,29 @@
 /* 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/. */
 
-var gCalThreadingEnabled;
-
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/Console.jsm");
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 
 // Usually the backend loader gets loaded via profile-after-change, but in case
 // a calendar component hooks in earlier, its very likely it will use calUtils.
 // Getting the service here will load if its not already loaded
 Components.classes["@mozilla.org/calendar/backend-loader;1"].getService();
 
+// The calendar console instance
+var gCalendarConsole = new ConsoleAPI({
+    prefix: "Lightning",
+    consoleID: "calendar",
+    maxLogLevel: Preferences.get("calendar.debug.log", false) ? "all" : "warn"
+});
+
 this.EXPORTED_SYMBOLS = ["cal"];
 var cal = {
     // These functions exist to reduce boilerplate code for creating instances
     // as well as getting services and other (cached) objects.
     createEvent: _instance("@mozilla.org/calendar/event;1",
                            Components.interfaces.calIEvent,
                            "icalString"),
     createTodo: _instance("@mozilla.org/calendar/todo;1",
@@ -70,16 +76,90 @@ var cal = {
     getWeekInfoService: _service("@mozilla.org/calendar/weekinfo-service;1",
                                  Components.interfaces.calIWeekInfoService),
     getDateFormatter: _service("@mozilla.org/calendar/datetime-formatter;1",
                                Components.interfaces.calIDateTimeFormatter),
     getDragService: _service("@mozilla.org/widget/dragservice;1",
                              Components.interfaces.nsIDragService),
 
     /**
+     * The calendar console instance
+     */
+    console: gCalendarConsole,
+
+    /**
+     * Logs a calendar message to the console. Needs calendar.debug.log enabled to show messages.
+     * Shortcut to cal.console.log()
+     */
+    LOG: gCalendarConsole.log,
+
+    /**
+     * Logs a calendar warning to the console. Shortcut to cal.console.warn()
+     */
+    WARN: gCalendarConsole.warn,
+
+    /**
+     * Logs a calendar error to the console. Shortcut to cal.console.error()
+     */
+    ERROR: gCalendarConsole.error,
+
+    /**
+     * Uses the prompt service to display an error message. Use this sparingly,
+     * as it interrupts the user.
+     *
+     * @param aMsg The message to be shown
+     * @param aWindow The window to show the message in, or null for any window.
+     */
+    showError: function(aMsg, aWindow=null) {
+        Services.prompt.alert(aWindow, cal.calGetString("calendar", "genericErrorTitle"), aMsg);
+    },
+
+    /**
+     * Returns a string describing the current js-stack with filename and line
+     * numbers.
+     *
+     * @param aDepth (optional) The number of frames to include. Defaults to 5.
+     * @param aSkip  (optional) Number of frames to skip
+     */
+    STACK: function(aDepth=10, aSkip=0) {
+        let stack = "";
+        let frame = Components.stack.caller;
+        for (let i = 1; i <= aDepth + aSkip && frame; i++) {
+            if (i > aSkip) {
+                stack += `${i}: [${frame.filename}:${frame.lineNumber}] ${frame.name}\n`;
+            }
+            frame = frame.caller;
+        }
+        return stack;
+    },
+
+    /**
+     * Logs a message and the current js-stack, if aCondition fails
+     *
+     * @param aCondition  the condition to test for
+     * @param aMessage    the message to report in the case the assert fails
+     * @param aCritical   if true, throw an error to stop current code execution
+     *                    if false, code flow will continue
+     *                    may be a result code
+     */
+    ASSERT: function(aCondition, aMessage, aCritical=false) {
+        if (aCondition) {
+            return;
+        }
+
+        let string = `Assert failed: ${aMessage}\n ${cal.STACK(0, 1)}`;
+        if (aCritical) {
+            let rescode = aCritical === true ? Components.results.NS_ERROR_UNEXPECTED : aCritical;
+            throw new Components.Exception(string, rescode);
+        } else {
+            Components.utils.reportError(string);
+        }
+    },
+
+    /**
      * Loads an array of calendar scripts into the passed scope.
      *
      * @param scriptNames an array of calendar script names
      * @param scope       scope to load into
      * @param baseDir     base dir; defaults to calendar-js/
      */
     loadScripts: function(scriptNames, scope, baseDir) {
         if (!baseDir) {
@@ -181,21 +261,27 @@ var cal = {
                 adapter[method] = function() {};
             }
         }
         adapter.QueryInterface = XPCOMUtils.generateQI([iface]);
 
         return adapter;
     },
 
-    get threadingEnabled() {
-        if (gCalThreadingEnabled === undefined) {
-            gCalThreadingEnabled = !Preferences.get("calendar.threading.disabled", false);
-        }
-        return gCalThreadingEnabled;
+    /**
+     * Make a UUID, without enclosing brackets, e.g. 0d3950fd-22e5-4508-91ba-0489bdac513f
+     *
+     * @return {String}         The generated UUID
+     */
+    getUUID: function() {
+        let uuidGen = Components.classes["@mozilla.org/uuid-generator;1"]
+                                .getService(Components.interfaces.nsIUUIDGenerator);
+        // generate uuids without braces to avoid problems with
+        // CalDAV servers that don't support filenames with {}
+        return uuidGen.generateUUID().toString().replace(/[{}]/g, "");
     },
 
     /**
      * Sort an array of strings according to the current locale.
      * Modifies aStringArray, returning it sorted.
      */
     sortArrayByLocaleCollator: function(aStringArray) {
         let localeCollator = cal.createLocaleCollator();
@@ -271,16 +357,27 @@ var cal = {
         try {
             return aObj.QueryInterface(aInterface);
         } catch (e) {
             return null;
         }
     },
 
     /**
+     * Tries to get rid of wrappers, if this is not possible then return the
+     * passed object.
+     *
+     * @param aObj  The object under consideration
+     * @return      The possibly unwrapped object.
+     */
+    unwrapInstance: function(aObj) {
+        return aObj && aObj.wrappedJSObject ? aObj.wrappedJSObject : aObj;
+    },
+
+    /**
      * Adds an xpcom shutdown observer.
      *
      * @param func function to execute
      */
     addShutdownObserver: function(func) {
         cal.addObserver(func, "xpcom-shutdown", true /* one time */);
     },
 
@@ -290,16 +387,22 @@ var cal = {
      *
      * @param obj    object
      * @param prop   property to be deleted on shutdown
      *               (if null, |object| will be deleted)
      */
     registerForShutdownCleanup: shutdownCleanup
 };
 
+// Preferences
+XPCOMUtils.defineLazyPreferenceGetter(cal, "debugLogEnabled", "calendar.debug.log", false, (pref, prev, value) => {
+    gCalendarConsole.maxLogLevel = value ? "all" : "warn";
+});
+XPCOMUtils.defineLazyPreferenceGetter(cal, "threadingEnabled", "calendar.threading.disabled", false);
+
 // Sub-modules for calUtils
 XPCOMUtils.defineLazyModuleGetter(cal, "acl", "resource://calendar/modules/calACLUtils.jsm", "calacl");
 XPCOMUtils.defineLazyModuleGetter(cal, "category", "resource://calendar/modules/calCategoryUtils.jsm", "calcategory");
 XPCOMUtils.defineLazyModuleGetter(cal, "data", "resource://calendar/modules/calDataUtils.jsm", "caldata");
 XPCOMUtils.defineLazyModuleGetter(cal, "dtz", "resource://calendar/modules/calDateTimeUtils.jsm", "caldtz");
 XPCOMUtils.defineLazyModuleGetter(cal, "email", "resource://calendar/modules/calEmailUtils.jsm", "calemail");
 XPCOMUtils.defineLazyModuleGetter(cal, "item", "resource://calendar/modules/calItemUtils.jsm", "calitem");
 XPCOMUtils.defineLazyModuleGetter(cal, "itip", "resource://calendar/modules/calItipUtils.jsm", "calitip");
--- a/calendar/base/modules/calUtilsCompat.jsm
+++ b/calendar/base/modules/calUtilsCompat.jsm
@@ -28,17 +28,18 @@ var migrations = {
         getPrefCategoriesArray: "fromPrefs",
         categoriesStringToArray: "stringToArray",
         categoriesArrayToString: "arrayToString"
     },
     data: {
         binarySearch: "binarySearch",
         binaryInsertNode: "binaryInsertNode",
         binaryInsert: "binaryInsert",
-        compareObjects: "compareObjects"
+        compareObjects: "compareObjects",
+        // isPropertyValueSame has been removed, it can simply be done with Array every()
     },
     dtz: {
         now: "now",
         ensureDateTime: "ensureDateTime",
         getRecentTimezones: "getRecentTimezones",
         saveRecentTimezone: "saveRecentTimezone",
         getDefaultStartDate: "getDefaultStartDate",
         setDefaultStartEndHour: "setDefaultStartEndHour",
--- a/calendar/base/src/calItemBase.js
+++ b/calendar/base/src/calItemBase.js
@@ -113,45 +113,45 @@ calItemBase.prototype = {
     },
 
     // attribute calIRecurrenceInfo recurrenceInfo;
     get recurrenceInfo() {
         return this.mRecurrenceInfo;
     },
     set recurrenceInfo(value) {
         this.modify();
-        return (this.mRecurrenceInfo = cal.calTryWrappedJSObject(value));
+        return (this.mRecurrenceInfo = cal.unwrapInstance(value));
     },
 
     // attribute calIItemBase parentItem;
     get parentItem() {
         return this.mParentItem || this;
     },
     set parentItem(value) {
         if (this.mImmutable) {
             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
         }
-        return (this.mParentItem = cal.calTryWrappedJSObject(value));
+        return (this.mParentItem = cal.unwrapInstance(value));
     },
 
     /**
      * Initializes the base item to be an item proxy. Used by inheriting
      * objects createProxy() method.
      *
      * XXXdbo Explain proxy a bit better, either here or in
      * calIInternalShallowCopy.
      *
      * @see calIInternalShallowCopy
      * @param aParentItem     The parent item to initialize the proxy on.
      * @param aRecurrenceId   The recurrence id to initialize the proxy for.
      */
     initializeProxy: function(aParentItem, aRecurrenceId) {
         this.mIsProxy = true;
 
-        aParentItem = cal.calTryWrappedJSObject(aParentItem);
+        aParentItem = cal.unwrapInstance(aParentItem);
         this.mParentItem = aParentItem;
         this.mCalendar = aParentItem.mCalendar;
         this.recurrenceId = aRecurrenceId;
 
         // Make sure organizer is unset, as the getter checks for this.
         this.mOrganizer = undefined;
 
         this.mImmutable = aParentItem.mImmutable;
@@ -248,21 +248,21 @@ calItemBase.prototype = {
      *
      * @param m     The item to clone this item into
      * @param aNewParent    (optional) The new parent item to set on m.
      */
     cloneItemBaseInto: function(cloned, aNewParent) {
         cloned.mImmutable = false;
         cloned.mACLEntry = this.mACLEntry;
         cloned.mIsProxy = this.mIsProxy;
-        cloned.mParentItem = cal.calTryWrappedJSObject(aNewParent) || this.mParentItem;
+        cloned.mParentItem = cal.unwrapInstance(aNewParent) || this.mParentItem;
         cloned.mHashId = this.mHashId;
         cloned.mCalendar = this.mCalendar;
         if (this.mRecurrenceInfo) {
-            cloned.mRecurrenceInfo = cal.calTryWrappedJSObject(this.mRecurrenceInfo.clone());
+            cloned.mRecurrenceInfo = cal.unwrapInstance(this.mRecurrenceInfo.clone());
             cloned.mRecurrenceInfo.item = cloned;
         }
 
         let org = this.organizer;
         if (org) {
             org = org.clone();
         }
         cloned.mOrganizer = org;
--- a/calendar/base/src/calRecurrenceInfo.js
+++ b/calendar/base/src/calRecurrenceInfo.js
@@ -119,17 +119,17 @@ calRecurrenceInfo.prototype = {
      * calIRecurrenceInfo
      */
     get item() {
         return this.mBaseItem;
     },
     set item(value) {
         this.ensureMutable();
 
-        value = cal.calTryWrappedJSObject(value);
+        value = cal.unwrapInstance(value);
         this.mBaseItem = value;
         // patch exception's parentItem:
         for (let ex in this.mExceptionMap) {
             let exitem = this.mExceptionMap[ex];
             exitem.parentItem = value;
         }
     },
 
@@ -670,17 +670,17 @@ calRecurrenceInfo.prototype = {
     // This implementation does the first approach (RECURRENCE-ID will
     // never change even if DTSTART for that instance changes), which
     // I think is the right thing to do for CalDAV; I don't know what
     // we'll do for incoming ITIP events though.
     //
     modifyException: function(anItem, aTakeOverOwnership) {
         this.ensureBaseItem();
 
-        anItem = cal.calTryWrappedJSObject(anItem);
+        anItem = cal.unwrapInstance(anItem);
 
         if (anItem.parentItem.calendar != this.mBaseItem.calendar &&
             anItem.parentItem.id != this.mBaseItem.id) {
             cal.ERROR("recurrenceInfo::addException: item parentItem != this.mBaseItem (calendar/id)!");
             throw Components.results.NS_ERROR_INVALID_ARG;
         }
 
         if (anItem.recurrenceId == null) {
--- a/calendar/base/src/calUtils.js
+++ b/calendar/base/src/calUtils.js
@@ -2,31 +2,19 @@
  * 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/. */
 
 /* This file contains commonly used functions in a centralized place so that
  * various components (and other js scopes) don't need to replicate them. Note
  * that loading this file twice in the same scope will throw errors.
  */
 
-/* exported calTryWrappedJSObject, LOG, WARN, ERROR, showError,
- *          applyAttributeToMenuChildren, isPropertyValueSame, calGetString,
- *          getUUID
- */
+/* exported calGetString */
 
-ChromeUtils.import("resource:///modules/mailServices.js");
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/Preferences.jsm");
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
-
-
-/**
- * Other functions
- */
 
 /**
  * Gets the value of a string in a .properties file from the calendar bundle
  *
  * @param aBundleName  the name of the properties file.  It is assumed that the
  *                     file lives in chrome://calendar/locale/
  * @param aStringName  the name of the string within the properties file
  * @param aParams      optional array of parameters to format the string
@@ -44,179 +32,8 @@ function calGetString(aBundleName, aStri
             return props.GetStringFromName(aStringName);
         }
     } catch (ex) {
         let msg = "Failed to read '" + aStringName + "' from " + propName + ".";
         Components.utils.reportError(msg + " Error: " + ex);
         return msg;
     }
 }
-
-/**
- * Make a UUID using the UUIDGenerator service available, we'll use that.
- */
-function getUUID() {
-    let uuidGen = Components.classes["@mozilla.org/uuid-generator;1"]
-                            .getService(Components.interfaces.nsIUUIDGenerator);
-    // generate uuids without braces to avoid problems with
-    // CalDAV servers that don't support filenames with {}
-    return uuidGen.generateUUID().toString().replace(/[{}]/g, "");
-}
-
-/**
- * Tries to get rid of wrappers. This is used to avoid cyclic references, and thus leaks.
- */
-function calTryWrappedJSObject(obj) {
-    if (obj && obj.wrappedJSObject) {
-        obj = obj.wrappedJSObject;
-    }
-    return obj;
-}
-
-
-/**
- * Helper used in the following log functions to actually log the message.
- * Should not be used outside of this file.
- */
-function _log(message, flag) {
-    let frame = Components.stack.caller.caller;
-    let filename = frame.filename ? frame.filename.split(" -> ").pop() : null;
-    let scriptError = Components.classes["@mozilla.org/scripterror;1"]
-                                .createInstance(Components.interfaces.nsIScriptError);
-    scriptError.init(message, filename, null, frame.lineNumber, frame.columnNumber,
-                     flag, "component javascript");
-    Services.console.logMessage(scriptError);
-}
-
-/**
- * Logs a string or an object to both stderr and the js-console only in the case
- * where the calendar.debug.log pref is set to true.
- *
- * @param aArg  either a string to log or an object whose entire set of
- *              properties should be logged.
- */
-function LOG(aArg) {
-    if (!Preferences.get("calendar.debug.log", false)) {
-        return;
-    }
-
-    ASSERT(aArg, "Bad log argument.", false);
-    let string = aArg;
-    // We should just dump() both String objects, and string primitives.
-    if (!(aArg instanceof String) && !(typeof aArg == "string")) {
-        string = "Logging object...\n";
-        for (let prop in aArg) {
-            string += prop + ": " + aArg[prop] + "\n";
-        }
-        string += "End object\n";
-    }
-
-    dump(string + "\n");
-    _log(string, Components.interfaces.nsIScriptError.infoFlag);
-}
-
-/**
- * Dumps a warning to both console and js console.
- *
- * @param aMessage warning message
- */
-function WARN(aMessage) {
-    dump("Warning: " + aMessage + "\n");
-    _log(aMessage, Components.interfaces.nsIScriptError.warningFlag);
-}
-
-/**
- * Dumps an error to both console and js console.
- *
- * @param aMessage error message
- */
-function ERROR(aMessage) {
-    dump("Error: " + aMessage + "\n");
-    _log(aMessage, Components.interfaces.nsIScriptError.errorFlag);
-}
-
-/**
- * Returns a string describing the current js-stack with filename and line
- * numbers.
- *
- * @param aDepth (optional) The number of frames to include. Defaults to 5.
- * @param aSkip  (optional) Number of frames to skip
- */
-function STACK(aDepth, aSkip) {
-    let depth = aDepth || 10;
-    let skip = aSkip || 0;
-    let stack = "";
-    let frame = Components.stack.caller;
-    for (let i = 1; i <= depth + skip && frame; i++) {
-        if (i > skip) {
-            stack += i + ": [" + frame.filename + ":" +
-                     frame.lineNumber + "] " + frame.name + "\n";
-        }
-        frame = frame.caller;
-    }
-    return stack;
-}
-
-/**
- * Logs a message and the current js-stack, if aCondition fails
- *
- * @param aCondition  the condition to test for
- * @param aMessage    the message to report in the case the assert fails
- * @param aCritical   if true, throw an error to stop current code execution
- *                    if false, code flow will continue
- *                    may be a result code
- */
-function ASSERT(aCondition, aMessage, aCritical) {
-    if (aCondition) {
-        return;
-    }
-
-    let string = "Assert failed: " + aMessage + "\n" + STACK(0, 1);
-    if (aCritical) {
-        throw new Components.Exception(string,
-                                       aCritical === true ? Components.results.NS_ERROR_UNEXPECTED : aCritical);
-    } else {
-        Components.utils.reportError(string);
-    }
-}
-
-/**
- * Uses the prompt service to display an error message.
- *
- * @param aMsg The message to be shown
- * @param aWindow The window to show the message in, or null for any window.
- */
-function showError(aMsg, aWindow=null) {
-    Services.prompt.alert(aWindow, cal.calGetString("calendar", "genericErrorTitle"), aMsg);
-}
-
-
-/**
- * TODO: The following UI-related functions need to move somewhere different,
- * i.e calendar-ui-utils.js
- */
-
-
-/**
- * compares the value of a property of an array of objects and returns
- * true or false if it is same or not among all array members
- *
- * @param aObjects An Array of Objects to inspect
- * @param aProperty Name the name of the Property of which the value is compared
- */
-function isPropertyValueSame(aObjects, aPropertyName) {
-    let value = null;
-    for (let i = 0; i < aObjects.length; i++) {
-        if (!value) {
-            value = aObjects[0][aPropertyName];
-        }
-        let compValue = aObjects[i][aPropertyName];
-        if (compValue != value) {
-            return false;
-        }
-    }
-    return true;
-}
-
-/**
- * END TODO: The above UI-related functions need to move somewhere different,
- * i.e calendar-ui-utils.js
- */