Commit 221250d2 authored by Xiaocheng Hu's avatar Xiaocheng Hu Committed by Commit Bot

Migrate Blink CSS minifier to GRIT

This patch migrates the Blink CSS minifier to GRIT and applies it
whenever GRIT creates a CSS resource. This allows us to minify all
CSS resources in a unified and effortless manner.

As a result, this patch achieves 12k Android binary size reduction:

https://ci.chromium.org/p/chromium/builders/try/android-binary-size/368229

Bug: 1015410
Change-Id: Ief8efe57d26f2e26c3b53523d8c973d1d193d20b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1900259Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#713567}
parent 8b59897b
......@@ -553,14 +553,6 @@ grit("resources") {
"grit/blink_resources.h",
"blink_resources.pak",
]
grit_flags = [
"-E",
"blink_core_output_dir=" +
rebase_path(blink_core_output_dir, root_build_dir),
]
deps = [
"//third_party/blink/renderer/core:make_minimized_css",
]
}
grit("image_resources") {
......
......@@ -8,40 +8,38 @@
</outputs>
<release seq="1">
<includes>
<!-- Certain CSS files are processed through minimize_css.py -->
<include name="IDR_UASTYLE_HTML_CSS" file="${cwd}/${blink_core_output_dir}/html.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_QUIRKS_CSS" file="${cwd}/${blink_core_output_dir}/quirks.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_VIEW_SOURCE_CSS" file="${cwd}/${blink_core_output_dir}/view-source.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_THEME_CHROMIUM_ANDROID_CSS" file="${cwd}/${blink_core_output_dir}/android.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_FULLSCREEN_ANDROID_CSS" file="${cwd}/${blink_core_output_dir}/fullscreenAndroid.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_THEME_CHROMIUM_LINUX_CSS" file="${cwd}/${blink_core_output_dir}/linux.css" type="BINDATA" compress="gzip"/>
<!-- GRIT minimizes all CSS and Javascript -->
<include name="IDR_UASTYLE_HTML_CSS" file="../renderer/core/html/resources/html.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_QUIRKS_CSS" file="../renderer/core/html/resources/quirks.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_VIEW_SOURCE_CSS" file="../renderer/core/css/view-source.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_THEME_CHROMIUM_ANDROID_CSS" file="../renderer/core/html/resources/android.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_FULLSCREEN_ANDROID_CSS" file="../renderer/core/css/fullscreenAndroid.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_THEME_CHROMIUM_LINUX_CSS" file="../renderer/core/html/resources/linux.css" type="BINDATA" compress="gzip"/>
<if expr="is_macosx">
<include name="IDR_UASTYLE_THEME_MAC_CSS" file="../renderer/core/html/resources/mac.css" type="BINDATA" compress="gzip"/>
</if>
<include name="IDR_UASTYLE_THEME_INPUT_MULTIPLE_FIELDS_CSS" file="${cwd}/${blink_core_output_dir}/input_multiple_fields.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_THEME_WIN_CSS" file="${cwd}/${blink_core_output_dir}/win.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_THEME_WIN_QUIRKS_CSS" file="${cwd}/${blink_core_output_dir}/win_quirks.css" type="BINDATA" compress="gzip"/>
<!-- TODO(crbug.com/1015410): Use minimized |controls_refresh.css| with relative urls properly revised -->
<include name="IDR_UASTYLE_THEME_INPUT_MULTIPLE_FIELDS_CSS" file="../renderer/core/html/resources/input_multiple_fields.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_THEME_WIN_CSS" file="../renderer/core/html/resources/win.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_THEME_WIN_QUIRKS_CSS" file="../renderer/core/html/resources/win_quirks.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_THEME_CONTROLS_REFRESH_CSS" file="../renderer/core/html/resources/controls_refresh.css" flattenhtml="true" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_THEME_FORCED_COLORS_CSS" file="${cwd}/${blink_core_output_dir}/forced_colors.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_SVG_CSS" file="${cwd}/${blink_core_output_dir}/svg.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_MATHML_CSS" file="${cwd}/${blink_core_output_dir}/mathml.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_FULLSCREEN_CSS" file="${cwd}/${blink_core_output_dir}/fullscreen.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_XHTMLMP_CSS" file="${cwd}/${blink_core_output_dir}/xhtmlmp.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_VIEWPORT_ANDROID_CSS" file="${cwd}/${blink_core_output_dir}/viewportAndroid.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_VIEWPORT_TELEVISION_CSS" file="${cwd}/${blink_core_output_dir}/viewportTelevision.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_THEME_FORCED_COLORS_CSS" file="../renderer/core/html/resources/forced_colors.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_SVG_CSS" file="../renderer/core/css/svg.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_MATHML_CSS" file="../renderer/core/css/mathml.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_FULLSCREEN_CSS" file="../renderer/core/css/fullscreen.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_XHTMLMP_CSS" file="../renderer/core/css/xhtmlmp.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_VIEWPORT_ANDROID_CSS" file="../renderer/core/css/viewportAndroid.css" type="BINDATA" compress="gzip"/>
<include name="IDR_UASTYLE_VIEWPORT_TELEVISION_CSS" file="../renderer/core/css/viewportTelevision.css" type="BINDATA" compress="gzip"/>
<include name="IDR_INSPECT_TOOL_COMMON_JS" file="../renderer/core/inspector/inspect_tool_common.js" type="BINDATA" compress="gzip"/>
<include name="IDR_INSPECT_TOOL_COMMON_CSS" file="${cwd}/${blink_core_output_dir}/inspect_tool_common.css" type="BINDATA" compress="gzip"/>
<include name="IDR_INSPECT_TOOL_COMMON_CSS" file="../renderer/core/inspector/inspect_tool_common.css" type="BINDATA" compress="gzip"/>
<include name="IDR_INSPECT_TOOL_DISTANCES_HTML" file="../renderer/core/inspector/inspect_tool_distances.html" type="BINDATA" compress="gzip"/>
<include name="IDR_INSPECT_TOOL_HIGHLIGHT_HTML" file="../renderer/core/inspector/inspect_tool_highlight.html" type="BINDATA" compress="gzip"/>
<include name="IDR_INSPECT_TOOL_PAUSED_HTML" file="../renderer/core/inspector/inspect_tool_paused.html" type="BINDATA" compress="gzip"/>
<include name="IDR_INSPECT_TOOL_VIEWPORT_SIZE_HTML" file="../renderer/core/inspector/inspect_tool_viewport_size.html" type="BINDATA" compress="gzip"/>
<include name="IDR_INSPECT_TOOL_SCREENSHOT_HTML" file="../renderer/core/inspector/inspect_tool_screenshot.html" type="BINDATA" compress="gzip"/>
<include name="IDR_DOCUMENTXMLTREEVIEWER_CSS" file="${cwd}/${blink_core_output_dir}/DocumentXMLTreeViewer.css" type="BINDATA" compress="gzip"/>
<include name="IDR_DOCUMENTXMLTREEVIEWER_CSS" file="../renderer/core/xml/DocumentXMLTreeViewer.css" type="BINDATA" compress="gzip"/>
<include name="IDR_DOCUMENTXMLTREEVIEWER_JS" file="../renderer/core/xml/DocumentXMLTreeViewer.js" type="BINDATA" compress="gzip"/>
<include name="IDR_VALIDATION_BUBBLE_ICON" file="../renderer/core/html/forms/resources/input_alert.svg" type="BINDATA" compress="gzip"/>
<include name="IDR_VALIDATION_BUBBLE_CSS" file="${cwd}/${blink_core_output_dir}/validation_bubble.css" type="BINDATA" compress="gzip"/>
<include name="IDR_VALIDATION_BUBBLE_CSS" file="../renderer/core/html/forms/resources/validation_bubble.css" type="BINDATA" compress="gzip"/>
<if expr="not is_android">
<include name="IDR_PICKER_COMMON_JS" file="../renderer/core/html/forms/resources/pickerCommon.js" type="BINDATA" compress="gzip"/>
<include name="IDR_PICKER_COMMON_CSS" file="../renderer/core/html/forms/resources/pickerCommon.css" type="BINDATA" compress="gzip"/>
......@@ -64,10 +62,8 @@
<include name="IDR_LIST_PICKER_JS" file="../renderer/core/html/forms/resources/listPicker.js" type="BINDATA" compress="gzip"/>
</if>
<include name="IDR_AUDIO_SPATIALIZATION_COMPOSITE" file="../renderer/platform/audio/resources/Composite.flac" type="BINDATA"/>
<!-- Layered API scripts. -->
<part file="../renderer/core/script/resources/layered_api/resources.grdp" />
</includes>
</release>
</grit>
#!/usr/bin/env python
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import functools
import os.path
import re
import sys
import in_generator
class CSSMinimizer(object):
INITIAL = 0
MAYBE_COMMENT_START = 1
INSIDE_COMMENT = 2
MAYBE_COMMENT_END = 3
INSIDE_SINGLE_QUOTE = 4
INSIDE_SINGLE_QUOTE_ESCAPE = 5
INSIDE_DOUBLE_QUOTE = 6
INSIDE_DOUBLE_QUOTE_ESCAPE = 7
def __init__(self):
self._output = ''
self._codeblock = ''
def flush_codeblock(self):
stripped = re.sub(r"\s+", ' ', self._codeblock)
stripped = re.sub(r";?\s*(?P<op>[{};])\s*", r'\g<op>', stripped)
self._output += stripped
self._codeblock = ''
def parse(self, content):
state = self.INITIAL
for char in content:
if state == self.INITIAL:
if char == '/':
state = self.MAYBE_COMMENT_START
elif char == "'":
self.flush_codeblock()
self._output += char
state = self.INSIDE_SINGLE_QUOTE
elif char == '"':
self.flush_codeblock()
self._output += char
state = self.INSIDE_DOUBLE_QUOTE
else:
self._codeblock += char
elif state == self.MAYBE_COMMENT_START:
if char == '*':
self.flush_codeblock()
state = self.INSIDE_COMMENT
else:
self._codeblock += '/' + char
state = self.INITIAL
elif state == self.INSIDE_COMMENT:
if char == '*':
state = self.MAYBE_COMMENT_END
else:
pass
elif state == self.MAYBE_COMMENT_END:
if char == '/':
state = self.INITIAL
else:
state = self.INSIDE_COMMENT
elif state == self.INSIDE_SINGLE_QUOTE:
if char == '\\':
self._output += char
state = self.INSIDE_SINGLE_QUOTE_ESCAPE
elif char == "'":
self._output += char
state = self.INITIAL
else:
self._output += char
elif state == self.INSIDE_SINGLE_QUOTE_ESCAPE:
self._output += char
state = self.INSIDE_SINGLE_QUOTE
elif state == self.INSIDE_DOUBLE_QUOTE:
if char == '\\':
self._output += char
state = self.INSIDE_DOUBLE_QUOTE_ESCAPE
elif char == '"':
self._output += char
state = self.INITIAL
else:
self._output += char
elif state == self.INSIDE_DOUBLE_QUOTE_ESCAPE:
self._output += char
state = self.INSIDE_DOUBLE_QUOTE
self.flush_codeblock()
self._output = self._output.strip()
return self._output
@classmethod
def minimize_css(cls, content):
minimizer = CSSMinimizer()
return minimizer.parse(content)
class CSSMinimizerWriter(in_generator.GenericWriter):
def __init__(self, in_file_paths):
super(CSSMinimizerWriter, self).__init__(in_file_paths)
self._outputs = {}
for in_file_path in in_file_paths:
out_path = os.path.basename(in_file_path)
self._outputs[out_path] = functools.partial(self.generate_implementation, in_file_path)
def generate_implementation(self, in_file_path):
content = ''
with open(os.path.abspath(in_file_path)) as in_file:
content = in_file.read()
return CSSMinimizer.minimize_css(content)
if __name__ == '__main__':
in_generator.Maker(CSSMinimizerWriter).main(sys.argv)
......@@ -807,61 +807,6 @@ make_qualified_names("make_core_generated_xml_names") {
# One-off scripts --------------------------------------------------------------
action("make_minimized_css") {
script = "../build/scripts/minimize_css.py"
inputs = [
"css/fullscreen.css",
"css/fullscreenAndroid.css",
"css/mathml.css",
"css/svg.css",
"css/view-source.css",
"css/viewportAndroid.css",
"css/viewportTelevision.css",
"css/xhtmlmp.css",
"html/forms/resources/validation_bubble.css",
"html/resources/android.css",
"html/resources/forced_colors.css",
"html/resources/html.css",
"html/resources/input_multiple_fields.css",
"html/resources/linux.css",
"html/resources/quirks.css",
"html/resources/win.css",
"html/resources/win_quirks.css",
"inspector/inspect_tool_common.css",
"xml/DocumentXMLTreeViewer.css",
]
outputs = [
"$blink_core_output_dir/fullscreen.css",
"$blink_core_output_dir/fullscreenAndroid.css",
"$blink_core_output_dir/mathml.css",
"$blink_core_output_dir/svg.css",
"$blink_core_output_dir/view-source.css",
"$blink_core_output_dir/viewportAndroid.css",
"$blink_core_output_dir/viewportTelevision.css",
"$blink_core_output_dir/xhtmlmp.css",
"$blink_core_output_dir/validation_bubble.css",
"$blink_core_output_dir/android.css",
"$blink_core_output_dir/forced_colors.css",
"$blink_core_output_dir/html.css",
"$blink_core_output_dir/input_multiple_fields.css",
"$blink_core_output_dir/linux.css",
"$blink_core_output_dir/quirks.css",
"$blink_core_output_dir/win.css",
"$blink_core_output_dir/win_quirks.css",
"$blink_core_output_dir/inspect_tool_common.css",
"$blink_core_output_dir/DocumentXMLTreeViewer.css",
]
args = [
"--output_dir",
rel_blink_core_gen_dir,
]
args += rebase_path(inputs, root_build_dir)
deps = make_core_generated_deps
}
action("make_core_generated_html_entity_table") {
visibility = [] # Allow re-assignment of list.
visibility = [ ":*" ]
......
......@@ -10,19 +10,27 @@ import subprocess
import sys
__js_minifier = None
__css_minifier = None
def SetJsMinifier(minifier):
global __js_minifier
__js_minifier = minifier.split()
def SetCssMinifier(minifier):
global __css_minifier
__css_minifier = minifier.split()
def Minify(source, filename):
file_type = path.splitext(filename)[1]
if not file_type == '.js' or not __js_minifier:
minifier = None
if file_type == '.js':
minifier = __js_minifier
elif file_type == '.css':
minifier = __css_minifier
if not minifier:
return source
p = subprocess.Popen(
__js_minifier,
minifier,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
......
......@@ -136,6 +136,12 @@ Options:
minified Javascript to standard output. A non-zero exit
status will be taken as indicating failure.
--css-minifier A command to run the CSS minifier. If not set then CSS won't
be minified. The command should read the original CSS from
standard input, and output the minified CSS to standard
output. A non-zero exit status will be taken as indicating
failure.
--brotli The full path to the brotli executable generated by
third_party/brotli/BUILD.gn, required if any entries use
compress="brotli".
......@@ -165,13 +171,14 @@ are exported to translation interchange files (e.g. XMB files), etc.
write_only_new = False
depend_on_stamp = False
js_minifier = None
css_minifier = None
replace_ellipsis = True
(own_opts, args) = getopt.getopt(
args, 'a:p:o:D:E:f:w:t:',
('depdir=', 'depfile=', 'assert-file-list=', 'help',
'output-all-resource-defines', 'no-output-all-resource-defines',
'no-replace-ellipsis', 'depend-on-stamp', 'js-minifier=',
'write-only-new=', 'whitelist-support', 'brotli='))
'css-minifier=', 'write-only-new=', 'whitelist-support', 'brotli='))
for (key, val) in own_opts:
if key == '-a':
assert_output_files.append(val)
......@@ -209,6 +216,8 @@ are exported to translation interchange files (e.g. XMB files), etc.
depend_on_stamp = True
elif key == '--js-minifier':
js_minifier = val
elif key == '--css-minifier':
css_minifier = val
elif key == '--whitelist-support':
whitelist_support = True
elif key == '--brotli':
......@@ -235,6 +244,9 @@ are exported to translation interchange files (e.g. XMB files), etc.
if js_minifier:
minifier.SetJsMinifier(js_minifier)
if css_minifier:
minifier.SetCssMinifier(css_minifier)
self.write_only_new = write_only_new
self.res = grd_reader.Parse(opts.input,
......
......@@ -226,6 +226,7 @@ if (current_toolchain != host_toolchain) {
_strip_resource_files = is_android && is_official_build
_js_minifier = "//tools/grit/minify_with_uglify.py"
_css_minifier = "//tools/grit/minimize_css.py"
grit_resource_id_file = "//tools/gritsettings/resource_ids"
grit_info_script = "//tools/grit/grit_info.py"
......@@ -407,12 +408,16 @@ template("grit") {
}
if (_strip_resource_files) {
js_minifier_command = rebase_path(_js_minifier, root_build_dir)
css_minifier_command = rebase_path(_css_minifier, root_build_dir)
args += [
"--js-minifier",
js_minifier_command,
"--css-minifier",
css_minifier_command,
]
inputs += [
_js_minifier,
_css_minifier,
"//third_party/closure_compiler/compiler/compiler.jar",
]
}
......
#!/usr/bin/env python
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import re
import sys
class CSSMinimizer(object):
INITIAL = 0
MAYBE_COMMENT_START = 1
INSIDE_COMMENT = 2
MAYBE_COMMENT_END = 3
INSIDE_SINGLE_QUOTE = 4
INSIDE_SINGLE_QUOTE_ESCAPE = 5
INSIDE_DOUBLE_QUOTE = 6
INSIDE_DOUBLE_QUOTE_ESCAPE = 7
def __init__(self):
self._output = ''
self._codeblock = ''
def flush_codeblock(self):
stripped = re.sub(r"\s+", ' ', self._codeblock)
stripped = re.sub(r";?\s*(?P<op>[{};])\s*", r'\g<op>', stripped)
self._output += stripped
self._codeblock = ''
def parse(self, content):
state = self.INITIAL
for char in content:
if state == self.INITIAL:
if char == '/':
state = self.MAYBE_COMMENT_START
elif char == "'":
self.flush_codeblock()
self._output += char
state = self.INSIDE_SINGLE_QUOTE
elif char == '"':
self.flush_codeblock()
self._output += char
state = self.INSIDE_DOUBLE_QUOTE
else:
self._codeblock += char
elif state == self.MAYBE_COMMENT_START:
if char == '*':
self.flush_codeblock()
state = self.INSIDE_COMMENT
else:
self._codeblock += '/' + char
state = self.INITIAL
elif state == self.INSIDE_COMMENT:
if char == '*':
state = self.MAYBE_COMMENT_END
else:
pass
elif state == self.MAYBE_COMMENT_END:
if char == '/':
state = self.INITIAL
else:
state = self.INSIDE_COMMENT
elif state == self.INSIDE_SINGLE_QUOTE:
if char == '\\':
self._output += char
state = self.INSIDE_SINGLE_QUOTE_ESCAPE
elif char == "'":
self._output += char
state = self.INITIAL
else:
self._output += char
elif state == self.INSIDE_SINGLE_QUOTE_ESCAPE:
self._output += char
state = self.INSIDE_SINGLE_QUOTE
elif state == self.INSIDE_DOUBLE_QUOTE:
if char == '\\':
self._output += char
state = self.INSIDE_DOUBLE_QUOTE_ESCAPE
elif char == '"':
self._output += char
state = self.INITIAL
else:
self._output += char
elif state == self.INSIDE_DOUBLE_QUOTE_ESCAPE:
self._output += char
state = self.INSIDE_DOUBLE_QUOTE
self.flush_codeblock()
self._output = self._output.strip()
return self._output
@classmethod
def minimize_css(cls, content):
minimizer = CSSMinimizer()
return minimizer.parse(content)
def main():
result = ''
try:
result = CSSMinimizer.minimize_css(sys.stdin.read())
finally:
print(result)
if __name__ == '__main__':
main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment