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/css-tree/lib/parser/create.js b/node_modules/css-tree/lib/parser/create.js
new file mode 100644
index 0000000..a01f625
--- /dev/null
+++ b/node_modules/css-tree/lib/parser/create.js
@@ -0,0 +1,304 @@
+var OffsetToLocation = require('../common/OffsetToLocation');
+var SyntaxError = require('../common/SyntaxError');
+var TokenStream = require('../common/TokenStream');
+var List = require('../common/List');
+var tokenize = require('../tokenizer');
+var constants = require('../tokenizer/const');
+var { findWhiteSpaceStart, cmpStr } = require('../tokenizer/utils');
+var sequence = require('./sequence');
+var noop = function() {};
+
+var TYPE = constants.TYPE;
+var NAME = constants.NAME;
+var WHITESPACE = TYPE.WhiteSpace;
+var COMMENT = TYPE.Comment;
+var IDENT = TYPE.Ident;
+var FUNCTION = TYPE.Function;
+var URL = TYPE.Url;
+var HASH = TYPE.Hash;
+var PERCENTAGE = TYPE.Percentage;
+var NUMBER = TYPE.Number;
+var NUMBERSIGN = 0x0023; // U+0023 NUMBER SIGN (#)
+var NULL = 0;
+
+function createParseContext(name) {
+    return function() {
+        return this[name]();
+    };
+}
+
+function processConfig(config) {
+    var parserConfig = {
+        context: {},
+        scope: {},
+        atrule: {},
+        pseudo: {}
+    };
+
+    if (config.parseContext) {
+        for (var name in config.parseContext) {
+            switch (typeof config.parseContext[name]) {
+                case 'function':
+                    parserConfig.context[name] = config.parseContext[name];
+                    break;
+
+                case 'string':
+                    parserConfig.context[name] = createParseContext(config.parseContext[name]);
+                    break;
+            }
+        }
+    }
+
+    if (config.scope) {
+        for (var name in config.scope) {
+            parserConfig.scope[name] = config.scope[name];
+        }
+    }
+
+    if (config.atrule) {
+        for (var name in config.atrule) {
+            var atrule = config.atrule[name];
+
+            if (atrule.parse) {
+                parserConfig.atrule[name] = atrule.parse;
+            }
+        }
+    }
+
+    if (config.pseudo) {
+        for (var name in config.pseudo) {
+            var pseudo = config.pseudo[name];
+
+            if (pseudo.parse) {
+                parserConfig.pseudo[name] = pseudo.parse;
+            }
+        }
+    }
+
+    if (config.node) {
+        for (var name in config.node) {
+            parserConfig[name] = config.node[name].parse;
+        }
+    }
+
+    return parserConfig;
+}
+
+module.exports = function createParser(config) {
+    var parser = {
+        scanner: new TokenStream(),
+        locationMap: new OffsetToLocation(),
+
+        filename: '<unknown>',
+        needPositions: false,
+        onParseError: noop,
+        onParseErrorThrow: false,
+        parseAtrulePrelude: true,
+        parseRulePrelude: true,
+        parseValue: true,
+        parseCustomProperty: false,
+
+        readSequence: sequence,
+
+        createList: function() {
+            return new List();
+        },
+        createSingleNodeList: function(node) {
+            return new List().appendData(node);
+        },
+        getFirstListNode: function(list) {
+            return list && list.first();
+        },
+        getLastListNode: function(list) {
+            return list.last();
+        },
+
+        parseWithFallback: function(consumer, fallback) {
+            var startToken = this.scanner.tokenIndex;
+
+            try {
+                return consumer.call(this);
+            } catch (e) {
+                if (this.onParseErrorThrow) {
+                    throw e;
+                }
+
+                var fallbackNode = fallback.call(this, startToken);
+
+                this.onParseErrorThrow = true;
+                this.onParseError(e, fallbackNode);
+                this.onParseErrorThrow = false;
+
+                return fallbackNode;
+            }
+        },
+
+        lookupNonWSType: function(offset) {
+            do {
+                var type = this.scanner.lookupType(offset++);
+                if (type !== WHITESPACE) {
+                    return type;
+                }
+            } while (type !== NULL);
+
+            return NULL;
+        },
+
+        eat: function(tokenType) {
+            if (this.scanner.tokenType !== tokenType) {
+                var offset = this.scanner.tokenStart;
+                var message = NAME[tokenType] + ' is expected';
+
+                // tweak message and offset
+                switch (tokenType) {
+                    case IDENT:
+                        // when identifier is expected but there is a function or url
+                        if (this.scanner.tokenType === FUNCTION || this.scanner.tokenType === URL) {
+                            offset = this.scanner.tokenEnd - 1;
+                            message = 'Identifier is expected but function found';
+                        } else {
+                            message = 'Identifier is expected';
+                        }
+                        break;
+
+                    case HASH:
+                        if (this.scanner.isDelim(NUMBERSIGN)) {
+                            this.scanner.next();
+                            offset++;
+                            message = 'Name is expected';
+                        }
+                        break;
+
+                    case PERCENTAGE:
+                        if (this.scanner.tokenType === NUMBER) {
+                            offset = this.scanner.tokenEnd;
+                            message = 'Percent sign is expected';
+                        }
+                        break;
+
+                    default:
+                        // when test type is part of another token show error for current position + 1
+                        // e.g. eat(HYPHENMINUS) will fail on "-foo", but pointing on "-" is odd
+                        if (this.scanner.source.charCodeAt(this.scanner.tokenStart) === tokenType) {
+                            offset = offset + 1;
+                        }
+                }
+
+                this.error(message, offset);
+            }
+
+            this.scanner.next();
+        },
+
+        consume: function(tokenType) {
+            var value = this.scanner.getTokenValue();
+
+            this.eat(tokenType);
+
+            return value;
+        },
+        consumeFunctionName: function() {
+            var name = this.scanner.source.substring(this.scanner.tokenStart, this.scanner.tokenEnd - 1);
+
+            this.eat(FUNCTION);
+
+            return name;
+        },
+
+        getLocation: function(start, end) {
+            if (this.needPositions) {
+                return this.locationMap.getLocationRange(
+                    start,
+                    end,
+                    this.filename
+                );
+            }
+
+            return null;
+        },
+        getLocationFromList: function(list) {
+            if (this.needPositions) {
+                var head = this.getFirstListNode(list);
+                var tail = this.getLastListNode(list);
+                return this.locationMap.getLocationRange(
+                    head !== null ? head.loc.start.offset - this.locationMap.startOffset : this.scanner.tokenStart,
+                    tail !== null ? tail.loc.end.offset - this.locationMap.startOffset : this.scanner.tokenStart,
+                    this.filename
+                );
+            }
+
+            return null;
+        },
+
+        error: function(message, offset) {
+            var location = typeof offset !== 'undefined' && offset < this.scanner.source.length
+                ? this.locationMap.getLocation(offset)
+                : this.scanner.eof
+                    ? this.locationMap.getLocation(findWhiteSpaceStart(this.scanner.source, this.scanner.source.length - 1))
+                    : this.locationMap.getLocation(this.scanner.tokenStart);
+
+            throw new SyntaxError(
+                message || 'Unexpected input',
+                this.scanner.source,
+                location.offset,
+                location.line,
+                location.column
+            );
+        }
+    };
+
+    config = processConfig(config || {});
+    for (var key in config) {
+        parser[key] = config[key];
+    }
+
+    return function(source, options) {
+        options = options || {};
+
+        var context = options.context || 'default';
+        var onComment = options.onComment;
+        var ast;
+
+        tokenize(source, parser.scanner);
+        parser.locationMap.setSource(
+            source,
+            options.offset,
+            options.line,
+            options.column
+        );
+
+        parser.filename = options.filename || '<unknown>';
+        parser.needPositions = Boolean(options.positions);
+        parser.onParseError = typeof options.onParseError === 'function' ? options.onParseError : noop;
+        parser.onParseErrorThrow = false;
+        parser.parseAtrulePrelude = 'parseAtrulePrelude' in options ? Boolean(options.parseAtrulePrelude) : true;
+        parser.parseRulePrelude = 'parseRulePrelude' in options ? Boolean(options.parseRulePrelude) : true;
+        parser.parseValue = 'parseValue' in options ? Boolean(options.parseValue) : true;
+        parser.parseCustomProperty = 'parseCustomProperty' in options ? Boolean(options.parseCustomProperty) : false;
+
+        if (!parser.context.hasOwnProperty(context)) {
+            throw new Error('Unknown context `' + context + '`');
+        }
+
+        if (typeof onComment === 'function') {
+            parser.scanner.forEachToken((type, start, end) => {
+                if (type === COMMENT) {
+                    const loc = parser.getLocation(start, end);
+                    const value = cmpStr(source, end - 2, end, '*/')
+                        ? source.slice(start + 2, end - 2)
+                        : source.slice(start + 2, end);
+
+                    onComment(value, loc);
+                }
+            });
+        }
+
+        ast = parser.context[context].call(parser, options);
+
+        if (!parser.scanner.eof) {
+            parser.error();
+        }
+
+        return ast;
+    };
+};