summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoichi Murase <myoga.murase@gmail.com>2019-08-07 07:59:16 +0900
committerChristian Duerr <contact@christianduerr.com>2019-08-06 22:59:16 +0000
commit33cfc52909bda1e2d5ceeae5397f254509d00539 (patch)
tree5c216e26de4c095058844cfab1ad18e7140157bd
parentb20d28578229a570b72f420fa50a64040bd2105b (diff)
downloadalacritty-33cfc52909bda1e2d5ceeae5397f254509d00539.tar.gz
alacritty-33cfc52909bda1e2d5ceeae5397f254509d00539.zip
Ignore unsupported CSI sequences
Instead of ignoring unexpected intermediates in CSI escape sequences, the intermediates are now explicitly checked and the escape sequence is rejected when an unexpected intermediate is found. Fixes #2171.
-rw-r--r--CHANGELOG.md1
-rw-r--r--alacritty_terminal/src/ansi.rs298
-rw-r--r--alacritty_terminal/tests/ref.rs1
-rw-r--r--alacritty_terminal/tests/ref/selective_erasure/alacritty.recording2
-rw-r--r--alacritty_terminal/tests/ref/selective_erasure/config.json1
-rw-r--r--alacritty_terminal/tests/ref/selective_erasure/grid.json1
-rw-r--r--alacritty_terminal/tests/ref/selective_erasure/size.json1
7 files changed, 167 insertions, 138 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 06862ac0..14871794 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- On Linux, respect fontconfig's `embeddedbitmap` configuration option
- Selecting trailing tab with semantic expansion
- URL parser incorrectly handling Markdown URLs and angled brackets
+- Intermediate bytes of CSI sequences not checked
## 0.3.3
diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs
index b4cf4476..1914e952 100644
--- a/alacritty_terminal/src/ansi.rs
+++ b/alacritty_terminal/src/ansi.rs
@@ -890,11 +890,7 @@ where
}
#[inline]
- fn csi_dispatch(&mut self, args: &[i64], intermediates: &[u8], _ignore: bool, action: char) {
- let private = intermediates.get(0).map(|b| *b == b'?').unwrap_or(false);
- let handler = &mut self.handler;
- let writer = &mut self.writer;
-
+ fn csi_dispatch(&mut self, args: &[i64], intermediates: &[u8], has_ignored_intermediates: bool, action: char) {
macro_rules! unhandled {
() => {{
debug!(
@@ -913,12 +909,19 @@ where
};
}
- match action {
- '@' => handler.insert_blank(Column(arg_or_default!(idx: 0, default: 1) as usize)),
- 'A' => {
+ if has_ignored_intermediates || intermediates.len() > 1 {
+ unhandled!();
+ }
+
+ let handler = &mut self.handler;
+ let writer = &mut self.writer;
+
+ match (action, intermediates.get(0)) {
+ ('@', None) => handler.insert_blank(Column(arg_or_default!(idx: 0, default: 1) as usize)),
+ ('A', None) => {
handler.move_up(Line(arg_or_default!(idx: 0, default: 1) as usize));
},
- 'b' => {
+ ('b', None) => {
if let Some(c) = self._state.preceding_char {
for _ in 0..arg_or_default!(idx: 0, default: 1) {
handler.input(c);
@@ -927,13 +930,13 @@ where
debug!("tried to repeat with no preceding char");
}
},
- 'B' | 'e' => handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'c' => handler.identify_terminal(writer),
- 'C' | 'a' => handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize)),
- 'D' => handler.move_backward(Column(arg_or_default!(idx: 0, default: 1) as usize)),
- 'E' => handler.move_down_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'F' => handler.move_up_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'g' => {
+ ('B', None) | ('e', None) => handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize)),
+ ('c', None) => handler.identify_terminal(writer),
+ ('C', None) | ('a', None) => handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize)),
+ ('D', None) => handler.move_backward(Column(arg_or_default!(idx: 0, default: 1) as usize)),
+ ('E', None) => handler.move_down_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)),
+ ('F', None) => handler.move_up_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)),
+ ('g', None) => {
let mode = match arg_or_default!(idx: 0, default: 0) {
0 => TabulationClearMode::Current,
3 => TabulationClearMode::All,
@@ -942,14 +945,14 @@ where
handler.clear_tabs(mode);
},
- 'G' | '`' => handler.goto_col(Column(arg_or_default!(idx: 0, default: 1) as usize - 1)),
- 'H' | 'f' => {
+ ('G', None) | ('`', None) => handler.goto_col(Column(arg_or_default!(idx: 0, default: 1) as usize - 1)),
+ ('H', None) | ('f', None) => {
let y = arg_or_default!(idx: 0, default: 1) as usize;
let x = arg_or_default!(idx: 1, default: 1) as usize;
handler.goto(Line(y - 1), Column(x - 1));
},
- 'I' => handler.move_forward_tabs(arg_or_default!(idx: 0, default: 1)),
- 'J' => {
+ ('I', None) => handler.move_forward_tabs(arg_or_default!(idx: 0, default: 1)),
+ ('J', None) => {
let mode = match arg_or_default!(idx: 0, default: 0) {
0 => ClearMode::Below,
1 => ClearMode::Above,
@@ -960,7 +963,7 @@ where
handler.clear_screen(mode);
},
- 'K' => {
+ ('K', None) => {
let mode = match arg_or_default!(idx: 0, default: 0) {
0 => LineClearMode::Right,
1 => LineClearMode::Left,
@@ -970,129 +973,68 @@ where
handler.clear_line(mode);
},
- 'S' => handler.scroll_up(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'T' => handler.scroll_down(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'L' => handler.insert_blank_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'l' => {
+ ('S', None) => handler.scroll_up(Line(arg_or_default!(idx: 0, default: 1) as usize)),
+ ('T', None) => handler.scroll_down(Line(arg_or_default!(idx: 0, default: 1) as usize)),
+ ('L', None) => handler.insert_blank_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)),
+ ('l', intermediate) => {
+ let is_private_mode = match intermediate {
+ Some(b'?') => true,
+ None => false,
+ _ => unhandled!(),
+ };
for arg in args {
- let mode = Mode::from_primitive(private, *arg);
+ let mode = Mode::from_primitive(is_private_mode, *arg);
match mode {
Some(mode) => handler.unset_mode(mode),
None => unhandled!(),
}
}
},
- 'M' => handler.delete_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'X' => handler.erase_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)),
- 'P' => handler.delete_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)),
- 'Z' => handler.move_backward_tabs(arg_or_default!(idx: 0, default: 1)),
- 'd' => handler.goto_line(Line(arg_or_default!(idx: 0, default: 1) as usize - 1)),
- 'h' => {
+ ('M', None) => handler.delete_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)),
+ ('X', None) => handler.erase_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)),
+ ('P', None) => handler.delete_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)),
+ ('Z', None) => handler.move_backward_tabs(arg_or_default!(idx: 0, default: 1)),
+ ('d', None) => handler.goto_line(Line(arg_or_default!(idx: 0, default: 1) as usize - 1)),
+ ('h', intermediate) => {
+ let is_private_mode = match intermediate {
+ Some(b'?') => true,
+ None => false,
+ _ => unhandled!(),
+ };
for arg in args {
- let mode = Mode::from_primitive(private, *arg);
+ let mode = Mode::from_primitive(is_private_mode, *arg);
match mode {
Some(mode) => handler.set_mode(mode),
None => unhandled!(),
}
}
},
- 'm' => {
- // Sometimes a C-style for loop is just what you need
- let mut i = 0; // C-for initializer
+ ('m', None) => {
if args.is_empty() {
handler.terminal_attribute(Attr::Reset);
- return;
- }
- loop {
- if i >= args.len() {
- // C-for condition
- break;
+ } else {
+ for attr in attrs_from_sgr_parameters(args) {
+ match attr {
+ Some(attr) => handler.terminal_attribute(attr),
+ None => unhandled!(),
+ }
}
-
- let attr = match args[i] {
- 0 => Attr::Reset,
- 1 => Attr::Bold,
- 2 => Attr::Dim,
- 3 => Attr::Italic,
- 4 => Attr::Underscore,
- 5 => Attr::BlinkSlow,
- 6 => Attr::BlinkFast,
- 7 => Attr::Reverse,
- 8 => Attr::Hidden,
- 9 => Attr::Strike,
- 21 => Attr::CancelBold,
- 22 => Attr::CancelBoldDim,
- 23 => Attr::CancelItalic,
- 24 => Attr::CancelUnderline,
- 25 => Attr::CancelBlink,
- 27 => Attr::CancelReverse,
- 28 => Attr::CancelHidden,
- 29 => Attr::CancelStrike,
- 30 => Attr::Foreground(Color::Named(NamedColor::Black)),
- 31 => Attr::Foreground(Color::Named(NamedColor::Red)),
- 32 => Attr::Foreground(Color::Named(NamedColor::Green)),
- 33 => Attr::Foreground(Color::Named(NamedColor::Yellow)),
- 34 => Attr::Foreground(Color::Named(NamedColor::Blue)),
- 35 => Attr::Foreground(Color::Named(NamedColor::Magenta)),
- 36 => Attr::Foreground(Color::Named(NamedColor::Cyan)),
- 37 => Attr::Foreground(Color::Named(NamedColor::White)),
- 38 => {
- let mut start = 0;
- if let Some(color) = parse_color(&args[i..], &mut start) {
- i += start;
- Attr::Foreground(color)
- } else {
- break;
- }
- },
- 39 => Attr::Foreground(Color::Named(NamedColor::Foreground)),
- 40 => Attr::Background(Color::Named(NamedColor::Black)),
- 41 => Attr::Background(Color::Named(NamedColor::Red)),
- 42 => Attr::Background(Color::Named(NamedColor::Green)),
- 43 => Attr::Background(Color::Named(NamedColor::Yellow)),
- 44 => Attr::Background(Color::Named(NamedColor::Blue)),
- 45 => Attr::Background(Color::Named(NamedColor::Magenta)),
- 46 => Attr::Background(Color::Named(NamedColor::Cyan)),
- 47 => Attr::Background(Color::Named(NamedColor::White)),
- 48 => {
- let mut start = 0;
- if let Some(color) = parse_color(&args[i..], &mut start) {
- i += start;
- Attr::Background(color)
- } else {
- break;
- }
- },
- 49 => Attr::Background(Color::Named(NamedColor::Background)),
- 90 => Attr::Foreground(Color::Named(NamedColor::BrightBlack)),
- 91 => Attr::Foreground(Color::Named(NamedColor::BrightRed)),
- 92 => Attr::Foreground(Color::Named(NamedColor::BrightGreen)),
- 93 => Attr::Foreground(Color::Named(NamedColor::BrightYellow)),
- 94 => Attr::Foreground(Color::Named(NamedColor::BrightBlue)),
- 95 => Attr::Foreground(Color::Named(NamedColor::BrightMagenta)),
- 96 => Attr::Foreground(Color::Named(NamedColor::BrightCyan)),
- 97 => Attr::Foreground(Color::Named(NamedColor::BrightWhite)),
- 100 => Attr::Background(Color::Named(NamedColor::BrightBlack)),
- 101 => Attr::Background(Color::Named(NamedColor::BrightRed)),
- 102 => Attr::Background(Color::Named(NamedColor::BrightGreen)),
- 103 => Attr::Background(Color::Named(NamedColor::BrightYellow)),
- 104 => Attr::Background(Color::Named(NamedColor::BrightBlue)),
- 105 => Attr::Background(Color::Named(NamedColor::BrightMagenta)),
- 106 => Attr::Background(Color::Named(NamedColor::BrightCyan)),
- 107 => Attr::Background(Color::Named(NamedColor::BrightWhite)),
- _ => unhandled!(),
- };
-
- handler.terminal_attribute(attr);
-
- i += 1; // C-for expr
}
},
- 'n' => handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize),
- 'r' => {
- if private {
- unhandled!();
- }
+ ('n', None) => handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize),
+ ('q', Some(b' ')) => {
+ // DECSCUSR (CSI Ps SP q) -- Set Cursor Style
+ let style = match arg_or_default!(idx: 0, default: 0) {
+ 0 => None,
+ 1 | 2 => Some(CursorStyle::Block),
+ 3 | 4 => Some(CursorStyle::Underline),
+ 5 | 6 => Some(CursorStyle::Beam),
+ _ => unhandled!(),
+ };
+
+ handler.set_cursor_style(style);
+ },
+ ('r', None) => {
let arg0 = arg_or_default!(idx: 0, default: 1) as usize;
let top = Line(arg0 - 1);
// Bottom should be included in the range, but range end is not
@@ -1104,19 +1046,8 @@ where
handler.set_scrolling_region(top..bottom);
},
- 's' => handler.save_cursor_position(),
- 'u' => handler.restore_cursor_position(),
- 'q' => {
- let style = match arg_or_default!(idx: 0, default: 0) {
- 0 => None,
- 1 | 2 => Some(CursorStyle::Block),
- 3 | 4 => Some(CursorStyle::Underline),
- 5 | 6 => Some(CursorStyle::Beam),
- _ => unhandled!(),
- };
-
- handler.set_cursor_style(style);
- },
+ ('s', None) => handler.save_cursor_position(),
+ ('u', None) => handler.restore_cursor_position(),
_ => unhandled!(),
}
}
@@ -1174,6 +1105,97 @@ where
}
}
+fn attrs_from_sgr_parameters(parameters: &[i64]) -> Vec<Option<Attr>> {
+ // Sometimes a C-style for loop is just what you need
+ let mut i = 0; // C-for initializer
+ let mut attrs = Vec::with_capacity(parameters.len());
+ loop {
+ if i >= parameters.len() {
+ // C-for condition
+ break;
+ }
+
+ let attr = match parameters[i] {
+ 0 => Some(Attr::Reset),
+ 1 => Some(Attr::Bold),
+ 2 => Some(Attr::Dim),
+ 3 => Some(Attr::Italic),
+ 4 => Some(Attr::Underscore),
+ 5 => Some(Attr::BlinkSlow),
+ 6 => Some(Attr::BlinkFast),
+ 7 => Some(Attr::Reverse),
+ 8 => Some(Attr::Hidden),
+ 9 => Some(Attr::Strike),
+ 21 => Some(Attr::CancelBold),
+ 22 => Some(Attr::CancelBoldDim),
+ 23 => Some(Attr::CancelItalic),
+ 24 => Some(Attr::CancelUnderline),
+ 25 => Some(Attr::CancelBlink),
+ 27 => Some(Attr::CancelReverse),
+ 28 => Some(Attr::CancelHidden),
+ 29 => Some(Attr::CancelStrike),
+ 30 => Some(Attr::Foreground(Color::Named(NamedColor::Black))),
+ 31 => Some(Attr::Foreground(Color::Named(NamedColor::Red))),
+ 32 => Some(Attr::Foreground(Color::Named(NamedColor::Green))),
+ 33 => Some(Attr::Foreground(Color::Named(NamedColor::Yellow))),
+ 34 => Some(Attr::Foreground(Color::Named(NamedColor::Blue))),
+ 35 => Some(Attr::Foreground(Color::Named(NamedColor::Magenta))),
+ 36 => Some(Attr::Foreground(Color::Named(NamedColor::Cyan))),
+ 37 => Some(Attr::Foreground(Color::Named(NamedColor::White))),
+ 38 => {
+ let mut start = 0;
+ if let Some(color) = parse_color(&parameters[i..], &mut start) {
+ i += start;
+ Some(Attr::Foreground(color))
+ } else {
+ None
+ }
+ },
+ 39 => Some(Attr::Foreground(Color::Named(NamedColor::Foreground))),
+ 40 => Some(Attr::Background(Color::Named(NamedColor::Black))),
+ 41 => Some(Attr::Background(Color::Named(NamedColor::Red))),
+ 42 => Some(Attr::Background(Color::Named(NamedColor::Green))),
+ 43 => Some(Attr::Background(Color::Named(NamedColor::Yellow))),
+ 44 => Some(Attr::Background(Color::Named(NamedColor::Blue))),
+ 45 => Some(Attr::Background(Color::Named(NamedColor::Magenta))),
+ 46 => Some(Attr::Background(Color::Named(NamedColor::Cyan))),
+ 47 => Some(Attr::Background(Color::Named(NamedColor::White))),
+ 48 => {
+ let mut start = 0;
+ if let Some(color) = parse_color(&parameters[i..], &mut start) {
+ i += start;
+ Some(Attr::Background(color))
+ } else {
+ None
+ }
+ },
+ 49 => Some(Attr::Background(Color::Named(NamedColor::Background))),
+ 90 => Some(Attr::Foreground(Color::Named(NamedColor::BrightBlack))),
+ 91 => Some(Attr::Foreground(Color::Named(NamedColor::BrightRed))),
+ 92 => Some(Attr::Foreground(Color::Named(NamedColor::BrightGreen))),
+ 93 => Some(Attr::Foreground(Color::Named(NamedColor::BrightYellow))),
+ 94 => Some(Attr::Foreground(Color::Named(NamedColor::BrightBlue))),
+ 95 => Some(Attr::Foreground(Color::Named(NamedColor::BrightMagenta))),
+ 96 => Some(Attr::Foreground(Color::Named(NamedColor::BrightCyan))),
+ 97 => Some(Attr::Foreground(Color::Named(NamedColor::BrightWhite))),
+ 100 => Some(Attr::Background(Color::Named(NamedColor::BrightBlack))),
+ 101 => Some(Attr::Background(Color::Named(NamedColor::BrightRed))),
+ 102 => Some(Attr::Background(Color::Named(NamedColor::BrightGreen))),
+ 103 => Some(Attr::Background(Color::Named(NamedColor::BrightYellow))),
+ 104 => Some(Attr::Background(Color::Named(NamedColor::BrightBlue))),
+ 105 => Some(Attr::Background(Color::Named(NamedColor::BrightMagenta))),
+ 106 => Some(Attr::Background(Color::Named(NamedColor::BrightCyan))),
+ 107 => Some(Attr::Background(Color::Named(NamedColor::BrightWhite))),
+ _ => None,
+ };
+
+ attrs.push(attr);
+
+ i += 1; // C-for expr
+ }
+ attrs
+}
+
/// Parse a color specifier from list of attributes
fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
if attrs.len() < 2 {
diff --git a/alacritty_terminal/tests/ref.rs b/alacritty_terminal/tests/ref.rs
index fb87adb1..b6efb6a8 100644
--- a/alacritty_terminal/tests/ref.rs
+++ b/alacritty_terminal/tests/ref.rs
@@ -53,6 +53,7 @@ ref_tests! {
grid_reset
row_reset
zerowidth
+ selective_erasure
}
fn read_u8<P>(path: P) -> Vec<u8>
diff --git a/alacritty_terminal/tests/ref/selective_erasure/alacritty.recording b/alacritty_terminal/tests/ref/selective_erasure/alacritty.recording
new file mode 100644
index 00000000..aae1560f
--- /dev/null
+++ b/alacritty_terminal/tests/ref/selective_erasure/alacritty.recording
@@ -0,0 +1,2 @@
+A[1"qB[2"qC[?2J
+A[1"qB[2"qC[?2K
diff --git a/alacritty_terminal/tests/ref/selective_erasure/config.json b/alacritty_terminal/tests/ref/selective_erasure/config.json
new file mode 100644
index 00000000..6fc49fd7
--- /dev/null
+++ b/alacritty_terminal/tests/ref/selective_erasure/config.json
@@ -0,0 +1 @@
+{"history_size":0} \ No newline at end of file
diff --git a/alacritty_terminal/tests/ref/selective_erasure/grid.json b/alacritty_terminal/tests/ref/selective_erasure/grid.json
new file mode 100644
index 00000000..08632f7d
--- /dev/null
+++ b/alacritty_terminal/tests/ref/selective_erasure/grid.json
@@ -0,0 +1 @@
+{"raw":{"inner":[{"inner":[{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]}],"occ":0},{"inner":[{"c":"A","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":"B","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":"C","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]}],"occ":3},{"inner":[{"c":"A","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":"B","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":"C","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]}],"occ":3}],"zero":0,"visible_lines":2,"len":3},"cols":10,"lines":3,"display_offset":0,"scroll_limit":0,"max_scroll_limit":0,"url_highlight":null} \ No newline at end of file
diff --git a/alacritty_terminal/tests/ref/selective_erasure/size.json b/alacritty_terminal/tests/ref/selective_erasure/size.json
new file mode 100644
index 00000000..58891d8a
--- /dev/null
+++ b/alacritty_terminal/tests/ref/selective_erasure/size.json
@@ -0,0 +1 @@
+{"width":70.0,"height":63.0,"cell_width":7.0,"cell_height":21.0,"padding_x":0.0,"padding_y":0.0,"dpr":1.0} \ No newline at end of file