Bug 1524548 - (Part 1) Add support to filter by ruleId and sourceId when building the changes tree. r=pbro
authorRazvan Caliman <rcaliman@mozilla.com>
Wed, 06 Feb 2019 08:12:23 +0000
changeset 457630 08fd81ac849bd369901e1d00989499aa0d27dc1c
parent 457629 f526887aa3ae0087bbb9553627453eb9ecdec871
child 457631 8c2113b14a96f565d94004526418923d38e4afb0
push id35516
push userrmaries@mozilla.com
push dateFri, 08 Feb 2019 04:23:26 +0000
treeherdermozilla-central@d599d1a73a3a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbro
bugs1524548
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1524548 - (Part 1) Add support to filter by ruleId and sourceId when building the changes tree. r=pbro The diff for this change looks needlessly scary. The only material change is the introduction of the `filter` parameter to the `getChangesTree()` selector to restrict the output to a subtree that includes only the martching rules and sources provided in as arrays of ids in the `filters` argument. The sources and rules are filtered, if any corresponding ids are provided, then the same logic to build the tree is used. The meaningful blocks of code introduced are: ``` .filter(([sourceId, source]) => { // Use only matching sources if an array to filter by was provided. if (sourceIdsFilter.length) { return sourceIdsFilter.includes(sourceId); } return true; }) ``` introduced right after `Object.entries(state)` and ``` .filter(([ruleId, rule]) => { // Use only matching rules if an array to filter by was provided. if (rulesIdsFilter.length) { return rulesIdsFilter.includes(ruleId); } return true; }) ``` introduced right after `Object.entries(rules)`. Adding a newline confused the diff algorithm to consider that much bigger changes have occurred. Introducing these filtering options lays the ground work for building subtrees of changes per-rule and per-stylesheet to be copied to the clipboard or saved as a file. Differential Revision: https://phabricator.services.mozilla.com/D18703
devtools/client/inspector/changes/selectors/changes.js
--- a/devtools/client/inspector/changes/selectors/changes.js
+++ b/devtools/client/inspector/changes/selectors/changes.js
@@ -10,19 +10,28 @@
  * a single level array, regardless of nesting.
  * This method returns a nested tree structure of the changed CSS rules so the React
  * consumer components can traverse it easier when rendering the nested CSS rules view.
  * Keeping this interface updated allows the Redux state structure to change without
  * affecting the consumer components.
  *
  * @param {Object} state
  *        Redux slice for tracked changes.
+ * @param {Object} filter
+ *        Object with optional filters to use. Has the following properties:
+ *        - sourceIds: {Array}
+ *          Use only subtrees of sources matching source ids from this array.
+ *        - ruleIds: {Array}
+ *          Use only rules matching rule ids from this array. If the array includes ids
+ *          of ancestor rules (@media, @supports), their nested rules will be included.
  * @return {Object}
  */
-function getChangesTree(state) {
+function getChangesTree(state, filter = {}) {
+  // Use or assign defaults of sourceId and ruleId arrays by which to filter the tree.
+  const { sourceIds: sourceIdsFilter = [], ruleIds: rulesIdsFilter = [] } = filter;
   /**
    * Recursively replace a rule's array of child rule ids with the referenced child rules.
    * Mark visited rules so as not to handle them (and their children) again.
    *
    * Returns the rule object with expanded children or null if previously visited.
    *
    * @param  {String} ruleId
    * @param  {Object} rule
@@ -39,41 +48,59 @@ function getChangesTree(state) {
 
     return {
       ...rule,
       children: rule.children.map(childRuleId =>
           expandRuleChildren(childRuleId, rules[childRuleId], rules, visitedRules)),
     };
   }
 
-  return Object.entries(state).reduce((sourcesObj, [sourceId, source]) => {
-    const { rules } = source;
-    // Log of visited rules in this source. Helps avoid duplication when traversing the
-    // descendant rule tree. This Set is unique per source. It will be passed down to
-    // be populated with ids of rules once visited. This ensures that only visited rules
-    // unique to this source will be skipped and prevents skipping identical rules from
-    // other sources (ex: rules with the same selector and the same index).
-    const visitedRules = new Set();
+  return Object.entries(state)
+    .filter(([sourceId, source]) => {
+      // Use only matching sources if an array to filter by was provided.
+      if (sourceIdsFilter.length) {
+        return sourceIdsFilter.includes(sourceId);
+      }
+
+      return true;
+    })
+    .reduce((sourcesObj, [sourceId, source]) => {
+      const { rules } = source;
+      // Log of visited rules in this source. Helps avoid duplication when traversing the
+      // descendant rule tree. This Set is unique per source. It will be passed down to
+      // be populated with ids of rules once visited. This ensures that only visited rules
+      // unique to this source will be skipped and prevents skipping identical rules from
+      // other sources (ex: rules with the same selector and the same index).
+      const visitedRules = new Set();
 
-    // Build a new collection of sources keyed by source id.
-    sourcesObj[sourceId] = {
-      ...source,
-      // Build a new collection of rules keyed by rule id.
-      rules: Object.entries(rules).reduce((rulesObj, [ruleId, rule]) => {
-        // Expand the rule's array of child rule ids with the referenced child rules.
-        // Skip exposing null values which mean the rule was previously visited as part
-        // of an ancestor descendant tree.
-        const expandedRule = expandRuleChildren(ruleId, rule, rules, visitedRules);
-        if (expandedRule !== null) {
-          rulesObj[ruleId] = expandedRule;
-        }
+      // Build a new collection of sources keyed by source id.
+      sourcesObj[sourceId] = {
+        ...source,
+        // Build a new collection of rules keyed by rule id.
+        rules: Object.entries(rules)
+          .filter(([ruleId, rule]) => {
+            // Use only matching rules if an array to filter by was provided.
+            if (rulesIdsFilter.length) {
+              return rulesIdsFilter.includes(ruleId);
+            }
 
-        return rulesObj;
-      }, {}),
-    };
+            return true;
+          })
+          .reduce((rulesObj, [ruleId, rule]) => {
+            // Expand the rule's array of child rule ids with the referenced child rules.
+            // Skip exposing null values which mean the rule was previously visited
+            // as part of an ancestor descendant tree.
+            const expandedRule = expandRuleChildren(ruleId, rule, rules, visitedRules);
+            if (expandedRule !== null) {
+              rulesObj[ruleId] = expandedRule;
+            }
 
-    return sourcesObj;
-  }, {});
+            return rulesObj;
+          }, {}),
+      };
+
+      return sourcesObj;
+    }, {});
 }
 
 module.exports = {
   getChangesTree,
 };