@@ -6,6 +6,7 @@ use crate::macros::enum_property;
66use crate :: printer:: Printer ;
77use crate :: rules:: custom_media:: CustomMediaRule ;
88use crate :: rules:: Location ;
9+ use crate :: targets:: Browsers ;
910use crate :: traits:: { Parse , ToCss } ;
1011use crate :: values:: ident:: Ident ;
1112use crate :: values:: number:: CSSNumber ;
@@ -290,17 +291,8 @@ impl<'i> MediaQuery<'i> {
290291 if let Some ( cond) = & b. condition {
291292 self . condition = if let Some ( condition) = & self . condition {
292293 if condition != cond {
293- macro_rules! parenthesize {
294- ( $condition: ident) => {
295- if matches!( $condition, MediaCondition :: Operation ( _, Operator :: Or ) ) {
296- MediaCondition :: InParens ( Box :: new( $condition. clone( ) ) )
297- } else {
298- $condition. clone( )
299- }
300- } ;
301- }
302294 Some ( MediaCondition :: Operation (
303- vec ! [ parenthesize! ( condition) , parenthesize! ( cond) ] ,
295+ vec ! [ condition. clone ( ) , cond. clone ( ) ] ,
304296 Operator :: And ,
305297 ) )
306298 } else {
@@ -346,11 +338,14 @@ impl<'i> ToCss for MediaQuery<'i> {
346338 None => return Ok ( ( ) ) ,
347339 } ;
348340
349- if self . media_type != MediaType :: All || self . qualifier . is_some ( ) {
341+ let needs_parens = if self . media_type != MediaType :: All || self . qualifier . is_some ( ) {
350342 dest. write_str ( " and " ) ?;
351- }
343+ matches ! ( condition, MediaCondition :: Operation ( _, op) if * op != Operator :: And )
344+ } else {
345+ false
346+ } ;
352347
353- condition. to_css ( dest)
348+ condition. to_css_with_parens_if_needed ( dest, needs_parens )
354349 }
355350}
356351
@@ -381,9 +376,6 @@ pub enum MediaCondition<'i> {
381376 /// A set of joint operations.
382377 #[ skip_type]
383378 Operation ( Vec < MediaCondition < ' i > > , Operator ) ,
384- /// A condition wrapped in parenthesis.
385- #[ skip_type]
386- InParens ( Box < MediaCondition < ' i > > ) ,
387379}
388380
389381impl < ' i > MediaCondition < ' i > {
@@ -439,13 +431,40 @@ impl<'i> MediaCondition<'i> {
439431 fn parse_paren_block < ' t > ( input : & mut Parser < ' i , ' t > ) -> Result < Self , ParseError < ' i , ParserError < ' i > > > {
440432 input. parse_nested_block ( |input| {
441433 if let Ok ( inner) = input. try_parse ( |i| Self :: parse ( i, true ) ) {
442- return Ok ( MediaCondition :: InParens ( Box :: new ( inner) ) ) ;
434+ return Ok ( inner) ;
443435 }
444436
445437 let feature = MediaFeature :: parse ( input) ?;
446438 Ok ( MediaCondition :: Feature ( feature) )
447439 } )
448440 }
441+
442+ fn needs_parens ( & self , parent_operator : Option < Operator > , targets : & Option < Browsers > ) -> bool {
443+ match self {
444+ MediaCondition :: Not ( _) => true ,
445+ MediaCondition :: Operation ( _, op) => Some ( * op) != parent_operator,
446+ MediaCondition :: Feature ( f) => {
447+ parent_operator != Some ( Operator :: And )
448+ && targets. is_some ( )
449+ && matches ! ( f, MediaFeature :: Interval { .. } )
450+ && !Feature :: MediaIntervalSyntax . is_compatible ( targets. unwrap ( ) )
451+ }
452+ }
453+ }
454+
455+ fn to_css_with_parens_if_needed < W > ( & self , dest : & mut Printer < W > , needs_parens : bool ) -> Result < ( ) , PrinterError >
456+ where
457+ W : std:: fmt:: Write ,
458+ {
459+ if needs_parens {
460+ dest. write_char ( '(' ) ?;
461+ }
462+ self . to_css ( dest) ?;
463+ if needs_parens {
464+ dest. write_char ( ')' ) ?;
465+ }
466+ Ok ( ( ) )
467+ }
449468}
450469
451470impl < ' i > ToCss for MediaCondition < ' i > {
@@ -457,21 +476,17 @@ impl<'i> ToCss for MediaCondition<'i> {
457476 MediaCondition :: Feature ( ref f) => f. to_css ( dest) ,
458477 MediaCondition :: Not ( ref c) => {
459478 dest. write_str ( "not " ) ?;
460- c. to_css ( dest)
461- }
462- MediaCondition :: InParens ( ref c) => {
463- dest. write_char ( '(' ) ?;
464- c. to_css ( dest) ?;
465- dest. write_char ( ')' )
479+ c. to_css_with_parens_if_needed ( dest, c. needs_parens ( None , & dest. targets ) )
466480 }
467481 MediaCondition :: Operation ( ref list, op) => {
468482 let mut iter = list. iter ( ) ;
469- iter. next ( ) . unwrap ( ) . to_css ( dest) ?;
483+ let first = iter. next ( ) . unwrap ( ) ;
484+ first. to_css_with_parens_if_needed ( dest, first. needs_parens ( Some ( op) , & dest. targets ) ) ?;
470485 for item in iter {
471486 dest. write_char ( ' ' ) ?;
472487 op. to_css ( dest) ?;
473488 dest. write_char ( ' ' ) ?;
474- item. to_css ( dest) ?;
489+ item. to_css_with_parens_if_needed ( dest, item . needs_parens ( Some ( op ) , & dest . targets ) ) ?;
475490 }
476491 Ok ( ( ) )
477492 }
@@ -879,21 +894,9 @@ fn process_condition<'i>(
879894 MediaCondition :: Not ( cond) => {
880895 * condition = ( * * cond) . clone ( ) ;
881896 }
882- MediaCondition :: InParens ( parens) => {
883- if let MediaCondition :: Not ( cond) = & * * parens {
884- * condition = ( * * cond) . clone ( ) ;
885- }
886- }
887897 _ => { }
888898 }
889899 }
890- MediaCondition :: InParens ( cond) => {
891- let res = process_condition ( loc, custom_media, media_type, qualifier, & mut * cond, seen) ;
892- if let MediaCondition :: InParens ( cond) = & * * cond {
893- * condition = ( * * cond) . clone ( ) ;
894- }
895- return res;
896- }
897900 MediaCondition :: Operation ( conditions, _) => {
898901 let mut res = Ok ( true ) ;
899902 conditions. retain_mut ( |condition| {
@@ -963,8 +966,8 @@ fn process_condition<'i>(
963966 }
964967 // Parentheses are required around the condition unless there is a single media feature.
965968 match condition {
966- MediaCondition :: Feature ( ..) | MediaCondition :: InParens ( .. ) => Some ( condition) ,
967- _ => Some ( MediaCondition :: InParens ( Box :: new ( condition) ) ) ,
969+ MediaCondition :: Feature ( ..) => Some ( condition) ,
970+ _ => Some ( condition) ,
968971 }
969972 } else {
970973 None
@@ -985,7 +988,7 @@ fn process_condition<'i>(
985988 if conditions. len ( ) == 1 {
986989 * condition = conditions. pop ( ) . unwrap ( ) ;
987990 } else {
988- * condition = MediaCondition :: InParens ( Box :: new ( MediaCondition :: Operation ( conditions, Operator :: Or ) ) ) ;
991+ * condition = MediaCondition :: Operation ( conditions, Operator :: Or ) ;
989992 }
990993 }
991994 _ => { }
@@ -997,7 +1000,7 @@ fn process_condition<'i>(
9971000#[ cfg( test) ]
9981001mod tests {
9991002 use super :: * ;
1000- use crate :: stylesheet:: PrinterOptions ;
1003+ use crate :: { stylesheet:: PrinterOptions , targets :: Browsers } ;
10011004
10021005 fn parse ( s : & str ) -> MediaQuery {
10031006 let mut input = ParserInput :: new ( & s) ;
@@ -1044,4 +1047,20 @@ mod tests {
10441047 assert_eq ! ( and( "only screen" , "all" ) , "only screen" ) ;
10451048 assert_eq ! ( and( "print" , "print" ) , "print" ) ;
10461049 }
1050+
1051+ #[ test]
1052+ fn test_negated_interval_parens ( ) {
1053+ let media_query = parse ( "screen and not (200px <= width < 500px)" ) ;
1054+ let printer_options = PrinterOptions {
1055+ targets : Some ( Browsers {
1056+ chrome : Some ( 95 << 16 ) ,
1057+ ..Default :: default ( )
1058+ } ) ,
1059+ ..Default :: default ( )
1060+ } ;
1061+ assert_eq ! (
1062+ media_query. to_css_string( printer_options) . unwrap( ) ,
1063+ "screen and not ((min-width: 200px) and (max-width: 499.999px))"
1064+ ) ;
1065+ }
10471066}
0 commit comments