aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mitmproxy/contentviews/css.py67
-rw-r--r--test/mitmproxy/contentviews/test_css.py61
-rw-r--r--test/mitmproxy/contentviews/test_css_data/animation-keyframe-formatted.css11
-rw-r--r--test/mitmproxy/contentviews/test_css_data/animation-keyframe.css3
-rw-r--r--test/mitmproxy/contentviews/test_css_data/blank-lines-and-spaces-formatted.css35
-rw-r--r--test/mitmproxy/contentviews/test_css_data/blank-lines-and-spaces.css30
-rw-r--r--test/mitmproxy/contentviews/test_css_data/block-comment-formatted.css22
-rw-r--r--test/mitmproxy/contentviews/test_css_data/block-comment.css18
-rw-r--r--test/mitmproxy/contentviews/test_css_data/empty-rule-formatted.css2
-rw-r--r--test/mitmproxy/contentviews/test_css_data/empty-rule.css1
-rw-r--r--test/mitmproxy/contentviews/test_css_data/import-directive-formatted.css8
-rw-r--r--test/mitmproxy/contentviews/test_css_data/import-directive.css2
-rw-r--r--test/mitmproxy/contentviews/test_css_data/indentation-formatted.css3
-rw-r--r--test/mitmproxy/contentviews/test_css_data/indentation.css3
-rw-r--r--test/mitmproxy/contentviews/test_css_data/media-directive-formatted.css17
-rw-r--r--test/mitmproxy/contentviews/test_css_data/media-directive.css7
-rw-r--r--test/mitmproxy/contentviews/test_css_data/quoted-string-formatted.css7
-rw-r--r--test/mitmproxy/contentviews/test_css_data/quoted-string.css2
-rw-r--r--test/mitmproxy/contentviews/test_css_data/selectors-formatted.css19
-rw-r--r--test/mitmproxy/contentviews/test_css_data/selectors.css5
-rw-r--r--test/mitmproxy/contentviews/test_css_data/simple-formatted.css16
-rw-r--r--test/mitmproxy/contentviews/test_css_data/simple.css5
22 files changed, 309 insertions, 35 deletions
diff --git a/mitmproxy/contentviews/css.py b/mitmproxy/contentviews/css.py
index 353a3257..8fa09ed3 100644
--- a/mitmproxy/contentviews/css.py
+++ b/mitmproxy/contentviews/css.py
@@ -1,8 +1,51 @@
-import logging
+import re
+import time
-import cssutils
+from mitmproxy.contentviews import base
+from mitmproxy.utils import strutils
-from . import base
+"""
+A custom CSS prettifier. Compared to other prettifiers, its main features are:
+
+- Implemented in pure Python.
+- Modifies whitespace only.
+- Works with any input.
+- Considerably faster than e.g. cssutils.
+"""
+
+CSS_SPECIAL_AREAS = (
+ ("'", strutils.NO_ESCAPE + "'"),
+ ('"', strutils.NO_ESCAPE + '"'),
+ (r"/\*", r"\*/"),
+ ("//", "$")
+)
+CSS_SPECIAL_CHARS = "{};:"
+
+
+def beautify(data: str, indent: str = " "):
+ """Beautify a string containing CSS code"""
+ data = strutils.escape_special_areas(
+ data.strip(),
+ CSS_SPECIAL_AREAS,
+ CSS_SPECIAL_CHARS,
+ )
+
+ # Add newlines
+ data = re.sub(r"\s*;\s*", ";\n", data)
+ data = re.sub(r"\s*{\s*", " {\n", data)
+ data = re.sub(r"\s*}\s*", "\n}\n\n", data)
+
+ # Fix incorrect ":" placement
+ data = re.sub(r"\s*:\s*(?=[^{]+})", ": ", data)
+ # Fix no space after ","
+ data = re.sub(r"\s*,\s*", ", ", data)
+
+ # indent
+ data = re.sub("\n[ \t]+", "\n", data)
+ data = re.sub("\n(?![}\n])(?=[^{]*})", "\n" + indent, data)
+
+ data = strutils.unescape_special_areas(data)
+ return data.rstrip("\n") + "\n"
class ViewCSS(base.View):
@@ -13,13 +56,15 @@ class ViewCSS(base.View):
]
def __call__(self, data, **metadata):
- cssutils.log.setLevel(logging.CRITICAL)
- cssutils.ser.prefs.keepComments = True
- cssutils.ser.prefs.omitLastSemicolon = False
- cssutils.ser.prefs.indentClosingBrace = False
- cssutils.ser.prefs.validOnly = False
+ data = data.decode("utf8", "surrogateescape")
+ beautified = beautify(data)
+ return "CSS", base.format_text(beautified)
- sheet = cssutils.parseString(data)
- beautified = sheet.cssText
- return "CSS", base.format_text(beautified)
+if __name__ == "__main__": # pragma: no cover
+ with open("../tools/web/static/vendor.css") as f:
+ data = f.read()
+
+ t = time.time()
+ x = beautify(data)
+ print("Beautifying vendor.css took {:.2}s".format(time.time() - t))
diff --git a/test/mitmproxy/contentviews/test_css.py b/test/mitmproxy/contentviews/test_css.py
index ecb9259b..814f6e83 100644
--- a/test/mitmproxy/contentviews/test_css.py
+++ b/test/mitmproxy/contentviews/test_css.py
@@ -1,29 +1,42 @@
+import pytest
+
from mitmproxy.contentviews import css
from mitmproxy.test import tutils
from . import full_eval
-try:
- import cssutils
-except:
- cssutils = None
-
-
-def test_view_css():
+data = tutils.test_data.push("mitmproxy/contentviews/test_css_data/")
+
+
+@pytest.mark.parametrize("filename", [
+ "animation-keyframe.css",
+ "blank-lines-and-spaces.css",
+ "block-comment.css",
+ "empty-rule.css",
+ "import-directive.css",
+ "indentation.css",
+ "media-directive.css",
+ "quoted-string.css",
+ "selectors.css",
+ "simple.css",
+])
+def test_beautify(filename):
+ path = data.path(filename)
+ with open(path) as f:
+ input = f.read()
+ with open("-formatted.".join(path.rsplit(".", 1))) as f:
+ expected = f.read()
+ formatted = css.beautify(input)
+ assert formatted == expected
+
+
+def test_simple():
v = full_eval(css.ViewCSS())
-
- with open(tutils.test_data.path('mitmproxy/data/1.css'), 'r') as fp:
- fixture_1 = fp.read()
-
- result = v('a')
-
- if cssutils:
- assert len(list(result[1])) == 0
- else:
- assert len(list(result[1])) == 1
-
- result = v(fixture_1)
-
- if cssutils:
- assert len(list(result[1])) > 1
- else:
- assert len(list(result[1])) == 1
+ assert v(b"#foo{color:red}") == ('CSS', [
+ [('text', '#foo {')],
+ [('text', ' color: red')],
+ [('text', '}')]
+ ])
+ assert v(b"") == ('CSS', [[('text', '')]])
+ assert v(b"console.log('not really css')") == (
+ 'CSS', [[('text', "console.log('not really css')")]]
+ )
diff --git a/test/mitmproxy/contentviews/test_css_data/animation-keyframe-formatted.css b/test/mitmproxy/contentviews/test_css_data/animation-keyframe-formatted.css
new file mode 100644
index 00000000..3f91d508
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/animation-keyframe-formatted.css
@@ -0,0 +1,11 @@
+@-webkit-keyframes anim {
+0% {
+ -webkit-transform: translate3d(0px, 0px, 0px);
+}
+
+100% {
+ -webkit-transform: translate3d(150px, 0px, 0px)
+}
+
+
+}
diff --git a/test/mitmproxy/contentviews/test_css_data/animation-keyframe.css b/test/mitmproxy/contentviews/test_css_data/animation-keyframe.css
new file mode 100644
index 00000000..ce63da5c
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/animation-keyframe.css
@@ -0,0 +1,3 @@
+@-webkit-keyframes anim {
+0% { -webkit-transform: translate3d(0px, 0px, 0px); }
+100% { -webkit-transform: translate3d(150px, 0px, 0px) }}
diff --git a/test/mitmproxy/contentviews/test_css_data/blank-lines-and-spaces-formatted.css b/test/mitmproxy/contentviews/test_css_data/blank-lines-and-spaces-formatted.css
new file mode 100644
index 00000000..de6bd045
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/blank-lines-and-spaces-formatted.css
@@ -0,0 +1,35 @@
+/* only one blank line between */
+menu {
+ color: red
+}
+
+navi {
+ color: black
+}
+
+/* automatically insert a blank line */
+button {
+ border: 1px
+}
+
+sidebar {
+ color: #ffe
+}
+
+/* always whitespace before { */
+hidden {
+ opacity: 0%
+}
+
+/* no blank lines inside ruleset */
+imprint {
+ color: blue;
+ opacity: 0.5;
+ font-size: small
+}
+
+/* before colon: no space, after colon: one space only */
+footer {
+ font-family: Arial;
+ float: right;
+}
diff --git a/test/mitmproxy/contentviews/test_css_data/blank-lines-and-spaces.css b/test/mitmproxy/contentviews/test_css_data/blank-lines-and-spaces.css
new file mode 100644
index 00000000..c6892105
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/blank-lines-and-spaces.css
@@ -0,0 +1,30 @@
+/* only one blank line between */
+menu { color: red }
+
+
+
+
+navi { color: black }
+
+/* automatically insert a blank line */
+button { border: 1px } sidebar { color: #ffe }
+
+/* always whitespace before { */
+hidden{opacity:0%}
+
+/* no blank lines inside ruleset */
+imprint {
+ color: blue;
+
+
+ opacity: 0.5;
+
+ font-size: small
+}
+
+/* before colon: no space, after colon: one space only */
+footer {
+ font-family: Arial;
+
+ float :right;
+ }
diff --git a/test/mitmproxy/contentviews/test_css_data/block-comment-formatted.css b/test/mitmproxy/contentviews/test_css_data/block-comment-formatted.css
new file mode 100644
index 00000000..83e0f4e6
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/block-comment-formatted.css
@@ -0,0 +1,22 @@
+/* line comment */
+navigation {
+ color: blue
+}
+
+menu {
+ /* line comment inside */
+ border: 2px
+}
+
+/* block
+comment */
+sidebar {
+ color: red
+}
+
+invisible {
+ /* block
+ * comment
+ * inside */
+ color: #eee
+}
diff --git a/test/mitmproxy/contentviews/test_css_data/block-comment.css b/test/mitmproxy/contentviews/test_css_data/block-comment.css
new file mode 100644
index 00000000..3ba26540
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/block-comment.css
@@ -0,0 +1,18 @@
+/* line comment */
+navigation { color: blue }
+
+menu {
+ /* line comment inside */
+ border: 2px
+}
+
+/* block
+ comment */
+sidebar { color: red }
+
+invisible {
+ /* block
+ * comment
+ * inside */
+ color: #eee
+}
diff --git a/test/mitmproxy/contentviews/test_css_data/empty-rule-formatted.css b/test/mitmproxy/contentviews/test_css_data/empty-rule-formatted.css
new file mode 100644
index 00000000..7c0a78f4
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/empty-rule-formatted.css
@@ -0,0 +1,2 @@
+menu {
+}
diff --git a/test/mitmproxy/contentviews/test_css_data/empty-rule.css b/test/mitmproxy/contentviews/test_css_data/empty-rule.css
new file mode 100644
index 00000000..7d6ecfcd
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/empty-rule.css
@@ -0,0 +1 @@
+menu{}
diff --git a/test/mitmproxy/contentviews/test_css_data/import-directive-formatted.css b/test/mitmproxy/contentviews/test_css_data/import-directive-formatted.css
new file mode 100644
index 00000000..08a0ad57
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/import-directive-formatted.css
@@ -0,0 +1,8 @@
+menu {
+ background-color: red
+}
+
+@import url('foobar.css') screen;
+nav {
+ margin: 0
+}
diff --git a/test/mitmproxy/contentviews/test_css_data/import-directive.css b/test/mitmproxy/contentviews/test_css_data/import-directive.css
new file mode 100644
index 00000000..61979f0a
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/import-directive.css
@@ -0,0 +1,2 @@
+menu{background-color:red} @import url('foobar.css') screen;
+nav{margin:0}
diff --git a/test/mitmproxy/contentviews/test_css_data/indentation-formatted.css b/test/mitmproxy/contentviews/test_css_data/indentation-formatted.css
new file mode 100644
index 00000000..18ea527d
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/indentation-formatted.css
@@ -0,0 +1,3 @@
+navigation {
+ color: blue
+}
diff --git a/test/mitmproxy/contentviews/test_css_data/indentation.css b/test/mitmproxy/contentviews/test_css_data/indentation.css
new file mode 100644
index 00000000..77e00f83
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/indentation.css
@@ -0,0 +1,3 @@
+ navigation {
+ color: blue
+ }
diff --git a/test/mitmproxy/contentviews/test_css_data/media-directive-formatted.css b/test/mitmproxy/contentviews/test_css_data/media-directive-formatted.css
new file mode 100644
index 00000000..84d95421
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/media-directive-formatted.css
@@ -0,0 +1,17 @@
+@import "subs.css";
+@import "print-main.css" print;
+@media print {
+body {
+ font-size: 10pt
+}
+
+nav {
+ color: blue;
+}
+
+
+}
+
+h1 {
+ color: red;
+}
diff --git a/test/mitmproxy/contentviews/test_css_data/media-directive.css b/test/mitmproxy/contentviews/test_css_data/media-directive.css
new file mode 100644
index 00000000..ddf67c58
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/media-directive.css
@@ -0,0 +1,7 @@
+@import "subs.css";
+@import "print-main.css" print;
+@media print {
+ body { font-size: 10pt }
+ nav { color: blue; }
+}
+h1 {color: red; }
diff --git a/test/mitmproxy/contentviews/test_css_data/quoted-string-formatted.css b/test/mitmproxy/contentviews/test_css_data/quoted-string-formatted.css
new file mode 100644
index 00000000..ab4c3412
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/quoted-string-formatted.css
@@ -0,0 +1,7 @@
+nav:after {
+ content: '}'
+}
+
+nav:before {
+ content: "}"
+}
diff --git a/test/mitmproxy/contentviews/test_css_data/quoted-string.css b/test/mitmproxy/contentviews/test_css_data/quoted-string.css
new file mode 100644
index 00000000..f5f3279e
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/quoted-string.css
@@ -0,0 +1,2 @@
+nav:after{content:'}'}
+nav:before{content:"}"}
diff --git a/test/mitmproxy/contentviews/test_css_data/selectors-formatted.css b/test/mitmproxy/contentviews/test_css_data/selectors-formatted.css
new file mode 100644
index 00000000..166251cb
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/selectors-formatted.css
@@ -0,0 +1,19 @@
+* {
+ border: 0px solid blue;
+}
+
+div[class="{}"] {
+ color: red;
+}
+
+a[id=\"foo"] {
+ padding: 0;
+}
+
+[id=\"foo"] {
+ margin: 0;
+}
+
+#menu, #nav, #footer {
+ color: royalblue;
+}
diff --git a/test/mitmproxy/contentviews/test_css_data/selectors.css b/test/mitmproxy/contentviews/test_css_data/selectors.css
new file mode 100644
index 00000000..dc36f9e5
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/selectors.css
@@ -0,0 +1,5 @@
+* { border: 0px solid blue; }
+div[class="{}"] { color: red; }
+a[id=\"foo"] { padding: 0; }
+[id=\"foo"] { margin: 0; }
+#menu, #nav, #footer { color: royalblue; }
diff --git a/test/mitmproxy/contentviews/test_css_data/simple-formatted.css b/test/mitmproxy/contentviews/test_css_data/simple-formatted.css
new file mode 100644
index 00000000..9435236b
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/simple-formatted.css
@@ -0,0 +1,16 @@
+menu {
+ color: blue;
+}
+
+box {
+ border-radius: 4px;
+ background-color: red
+}
+
+a {
+ color: green
+}
+
+b {
+ color: red
+}
diff --git a/test/mitmproxy/contentviews/test_css_data/simple.css b/test/mitmproxy/contentviews/test_css_data/simple.css
new file mode 100644
index 00000000..33b29a03
--- /dev/null
+++ b/test/mitmproxy/contentviews/test_css_data/simple.css
@@ -0,0 +1,5 @@
+menu { color: blue; }
+
+box { border-radius: 4px; background-color: red }
+a { color: green }
+b { color: red }