Add packages to optimize svgs dynamically

These packages will be used to dynamically optimize SVG images
during the build.

R=jacktfranklin@chromium.org

Bug: 1216402
Change-Id: I04e95aa7d79c9d67beaf8a7861182c52b16b7d0f
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2939992
Reviewed-by: Jack Franklin <jacktfranklin@chromium.org>
Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org>
diff --git a/node_modules/csso/lib/restructure/utils.js b/node_modules/csso/lib/restructure/utils.js
new file mode 100644
index 0000000..bdc3471
--- /dev/null
+++ b/node_modules/csso/lib/restructure/utils.js
@@ -0,0 +1,151 @@
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+function isEqualSelectors(a, b) {
+    var cursor1 = a.head;
+    var cursor2 = b.head;
+
+    while (cursor1 !== null && cursor2 !== null && cursor1.data.id === cursor2.data.id) {
+        cursor1 = cursor1.next;
+        cursor2 = cursor2.next;
+    }
+
+    return cursor1 === null && cursor2 === null;
+}
+
+function isEqualDeclarations(a, b) {
+    var cursor1 = a.head;
+    var cursor2 = b.head;
+
+    while (cursor1 !== null && cursor2 !== null && cursor1.data.id === cursor2.data.id) {
+        cursor1 = cursor1.next;
+        cursor2 = cursor2.next;
+    }
+
+    return cursor1 === null && cursor2 === null;
+}
+
+function compareDeclarations(declarations1, declarations2) {
+    var result = {
+        eq: [],
+        ne1: [],
+        ne2: [],
+        ne2overrided: []
+    };
+
+    var fingerprints = Object.create(null);
+    var declarations2hash = Object.create(null);
+
+    for (var cursor = declarations2.head; cursor; cursor = cursor.next)  {
+        declarations2hash[cursor.data.id] = true;
+    }
+
+    for (var cursor = declarations1.head; cursor; cursor = cursor.next)  {
+        var data = cursor.data;
+
+        if (data.fingerprint) {
+            fingerprints[data.fingerprint] = data.important;
+        }
+
+        if (declarations2hash[data.id]) {
+            declarations2hash[data.id] = false;
+            result.eq.push(data);
+        } else {
+            result.ne1.push(data);
+        }
+    }
+
+    for (var cursor = declarations2.head; cursor; cursor = cursor.next)  {
+        var data = cursor.data;
+
+        if (declarations2hash[data.id]) {
+            // when declarations1 has an overriding declaration, this is not a difference
+            // unless no !important is used on prev and !important is used on the following
+            if (!hasOwnProperty.call(fingerprints, data.fingerprint) ||
+                (!fingerprints[data.fingerprint] && data.important)) {
+                result.ne2.push(data);
+            }
+
+            result.ne2overrided.push(data);
+        }
+    }
+
+    return result;
+}
+
+function addSelectors(dest, source) {
+    source.each(function(sourceData) {
+        var newStr = sourceData.id;
+        var cursor = dest.head;
+
+        while (cursor) {
+            var nextStr = cursor.data.id;
+
+            if (nextStr === newStr) {
+                return;
+            }
+
+            if (nextStr > newStr) {
+                break;
+            }
+
+            cursor = cursor.next;
+        }
+
+        dest.insert(dest.createItem(sourceData), cursor);
+    });
+
+    return dest;
+}
+
+// check if simpleselectors has no equal specificity and element selector
+function hasSimilarSelectors(selectors1, selectors2) {
+    var cursor1 = selectors1.head;
+
+    while (cursor1 !== null) {
+        var cursor2 = selectors2.head;
+
+        while (cursor2 !== null) {
+            if (cursor1.data.compareMarker === cursor2.data.compareMarker) {
+                return true;
+            }
+
+            cursor2 = cursor2.next;
+        }
+
+        cursor1 = cursor1.next;
+    }
+
+    return false;
+}
+
+// test node can't to be skipped
+function unsafeToSkipNode(node) {
+    switch (node.type) {
+        case 'Rule':
+            // unsafe skip ruleset with selector similarities
+            return hasSimilarSelectors(node.prelude.children, this);
+
+        case 'Atrule':
+            // can skip at-rules with blocks
+            if (node.block) {
+                // unsafe skip at-rule if block contains something unsafe to skip
+                return node.block.children.some(unsafeToSkipNode, this);
+            }
+            break;
+
+        case 'Declaration':
+            return false;
+    }
+
+    // unsafe by default
+    return true;
+}
+
+module.exports = {
+    isEqualSelectors: isEqualSelectors,
+    isEqualDeclarations: isEqualDeclarations,
+    compareDeclarations: compareDeclarations,
+    addSelectors: addSelectors,
+    hasSimilarSelectors: hasSimilarSelectors,
+    unsafeToSkipNode: unsafeToSkipNode
+};