1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 //! A struct to encapsulate all the style fixups and flags propagations 6 //! a computed style needs in order for it to adhere to the CSS spec. 7 8 use app_units::Au; 9 use dom::TElement; 10 use properties::{self, CascadeFlags, ComputedValues, StyleBuilder}; 11 use properties::computed_value_flags::ComputedValueFlags; 12 use properties::longhands::display::computed_value::T as Display; 13 use properties::longhands::float::computed_value::T as Float; 14 use properties::longhands::overflow_x::computed_value::T as Overflow; 15 use properties::longhands::position::computed_value::T as Position; 16 17 /// A struct that implements all the adjustment methods. 18 /// 19 /// NOTE(emilio): If new adjustments are introduced that depend on reset 20 /// properties of the parent, you may need tweaking the 21 /// `ChildCascadeRequirement` code in `matching.rs`. 22 pub struct StyleAdjuster<'a, 'b: 'a> { 23 style: &'a mut StyleBuilder<'b>, 24 } 25 26 impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { 27 /// Trivially constructs a new StyleAdjuster. 28 #[inline] new(style: &'a mut StyleBuilder<'b>) -> Self29 pub fn new(style: &'a mut StyleBuilder<'b>) -> Self { 30 StyleAdjuster { style } 31 } 32 33 /// <https://fullscreen.spec.whatwg.org/#new-stacking-layer> 34 /// 35 /// Any position value other than 'absolute' and 'fixed' are 36 /// computed to 'absolute' if the element is in a top layer. 37 /// adjust_for_top_layer(&mut self)38 fn adjust_for_top_layer(&mut self) { 39 if !self.style.out_of_flow_positioned() && self.style.in_top_layer() { 40 self.style.mutate_box().set_position(Position::Absolute); 41 } 42 } 43 44 /// CSS 2.1 section 9.7: 45 /// 46 /// If 'position' has the value 'absolute' or 'fixed', [...] the computed 47 /// value of 'float' is 'none'. 48 /// adjust_for_position(&mut self)49 fn adjust_for_position(&mut self) { 50 if self.style.out_of_flow_positioned() && self.style.floated() { 51 self.style.mutate_box().set_float(Float::None); 52 } 53 } 54 55 /// Whether we should skip any item-based display property blockification on 56 /// this element. skip_item_display_fixup<E>(&self, element: Option<E>) -> bool where E: TElement,57 fn skip_item_display_fixup<E>(&self, element: Option<E>) -> bool 58 where 59 E: TElement, 60 { 61 if let Some(pseudo) = self.style.pseudo { 62 return pseudo.skip_item_display_fixup(); 63 } 64 65 element.map_or(false, |e| e.skip_item_display_fixup()) 66 } 67 68 69 /// Apply the blockification rules based on the table in CSS 2.2 section 9.7. 70 /// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo> blockify_if_necessary<E>( &mut self, layout_parent_style: &ComputedValues, element: Option<E>, ) where E: TElement,71 fn blockify_if_necessary<E>( 72 &mut self, 73 layout_parent_style: &ComputedValues, 74 element: Option<E>, 75 ) 76 where 77 E: TElement, 78 { 79 let mut blockify = false; 80 macro_rules! blockify_if { 81 ($if_what:expr) => { 82 if !blockify { 83 blockify = $if_what; 84 } 85 } 86 } 87 88 let is_root = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_root()); 89 blockify_if!(is_root); 90 if !self.skip_item_display_fixup(element) { 91 blockify_if!(layout_parent_style.get_box().clone_display().is_item_container()); 92 } 93 94 let is_item_or_root = blockify; 95 96 blockify_if!(self.style.floated()); 97 blockify_if!(self.style.out_of_flow_positioned()); 98 99 if !blockify { 100 return; 101 } 102 103 let display = self.style.get_box().clone_display(); 104 let blockified_display = display.equivalent_block_display(is_root); 105 if display != blockified_display { 106 self.style.mutate_box().set_adjusted_display( 107 blockified_display, 108 is_item_or_root, 109 ); 110 } 111 } 112 113 /// Compute a few common flags for both text and element's style. set_bits(&mut self)114 pub fn set_bits(&mut self) { 115 let display = self.style.get_box().clone_display(); 116 117 if !display.is_contents() && !self.style.get_text().clone_text_decoration_line().is_empty() { 118 self.style.flags.insert(ComputedValueFlags::HAS_TEXT_DECORATION_LINES); 119 } 120 121 if display == Display::None { 122 self.style.flags.insert(ComputedValueFlags::IS_IN_DISPLAY_NONE_SUBTREE); 123 } 124 125 if self.style.is_pseudo_element() { 126 self.style.flags.insert(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE); 127 } 128 129 #[cfg(feature = "servo")] 130 { 131 if self.style.get_parent_column().is_multicol() { 132 self.style.flags.insert(ComputedValueFlags::CAN_BE_FRAGMENTED); 133 } 134 } 135 } 136 137 /// Adjust the style for text style. 138 /// 139 /// The adjustments here are a subset of the adjustments generally, because 140 /// text only inherits properties. 141 /// 142 /// Note that this, for Gecko, comes through Servo_ComputedValues_Inherit. 143 #[cfg(feature = "gecko")] adjust_for_text(&mut self)144 pub fn adjust_for_text(&mut self) { 145 self.adjust_for_text_combine_upright(); 146 self.adjust_for_text_in_ruby(); 147 self.set_bits(); 148 } 149 150 /// Change writing mode of the text frame for text-combine-upright. 151 /// 152 /// It is safe to look at our own style because we are looking at inherited 153 /// properties, and text is just plain inheritance. 154 /// 155 /// TODO(emilio): we should (Gecko too) revise these adjustments in presence 156 /// of display: contents. 157 /// 158 /// FIXME(emilio): How does this play with logical properties? Doesn't 159 /// mutating writing-mode change the potential physical sides chosen? 160 #[cfg(feature = "gecko")] adjust_for_text_combine_upright(&mut self)161 fn adjust_for_text_combine_upright(&mut self) { 162 use computed_values::text_combine_upright::T as TextCombineUpright; 163 use computed_values::writing_mode::T as WritingMode; 164 165 let writing_mode = 166 self.style.get_inheritedbox().clone_writing_mode(); 167 let text_combine_upright = 168 self.style.get_inheritedtext().clone_text_combine_upright(); 169 170 if writing_mode != WritingMode::HorizontalTb && 171 text_combine_upright == TextCombineUpright::All { 172 self.style.flags.insert(ComputedValueFlags::IS_TEXT_COMBINED); 173 self.style.mutate_inheritedbox().set_writing_mode(WritingMode::HorizontalTb); 174 } 175 } 176 177 /// Unconditionally propagates the line break suppression flag to text, and 178 /// additionally it applies it if it is in any ruby box. 179 /// 180 /// This is necessary because its parent may not itself have the flag set 181 /// (e.g. ruby or ruby containers), thus we may not inherit the flag from 182 /// them. 183 #[cfg(feature = "gecko")] adjust_for_text_in_ruby(&mut self)184 fn adjust_for_text_in_ruby(&mut self) { 185 let parent_display = self.style.get_parent_box().clone_display(); 186 if parent_display.is_ruby_type() || 187 self.style.get_parent_flags().contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK) 188 { 189 self.style.flags.insert(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK); 190 } 191 } 192 193 /// <https://drafts.csswg.org/css-writing-modes-3/#block-flow:> 194 /// 195 /// If a box has a different writing-mode value than its containing 196 /// block: 197 /// 198 /// - If the box has a specified display of inline, its display 199 /// computes to inline-block. [CSS21] 200 /// 201 /// This matches the adjustment that Gecko does, not exactly following 202 /// the spec. See also: 203 /// 204 /// <https://lists.w3.org/Archives/Public/www-style/2017Mar/0045.html> 205 /// <https://github.com/servo/servo/issues/15754> adjust_for_writing_mode( &mut self, layout_parent_style: &ComputedValues, )206 fn adjust_for_writing_mode( 207 &mut self, 208 layout_parent_style: &ComputedValues, 209 ) { 210 let our_writing_mode = 211 self.style.get_inheritedbox().clone_writing_mode(); 212 let parent_writing_mode = 213 layout_parent_style.get_inheritedbox().clone_writing_mode(); 214 215 if our_writing_mode != parent_writing_mode && 216 self.style.get_box().clone_display() == Display::Inline { 217 // TODO(emilio): Figure out if we can just set the adjusted display 218 // on Gecko too and unify this code path. 219 if cfg!(feature = "servo") { 220 self.style.mutate_box().set_adjusted_display( 221 Display::InlineBlock, 222 false, 223 ); 224 } else { 225 self.style.mutate_box().set_display(Display::InlineBlock); 226 } 227 } 228 } 229 230 #[cfg(feature = "gecko")] adjust_for_contain(&mut self)231 fn adjust_for_contain(&mut self) { 232 use properties::longhands::contain::SpecifiedValue; 233 234 // An element with contain: paint needs to be a formatting context, and 235 // also implies overflow: clip. 236 // 237 // TODO(emilio): This mimics Gecko, but spec links are missing! 238 let contain = self.style.get_box().clone_contain(); 239 if !contain.contains(SpecifiedValue::PAINT) { 240 return; 241 } 242 243 if self.style.get_box().clone_display() == Display::Inline { 244 self.style.mutate_box().set_adjusted_display(Display::InlineBlock, 245 false); 246 } 247 248 249 // When 'contain: paint', update overflow from 'visible' to 'clip'. 250 if self.style.get_box().clone_contain().contains(SpecifiedValue::PAINT) { 251 if self.style.get_box().clone_overflow_x() == Overflow::Visible { 252 let box_style = self.style.mutate_box(); 253 box_style.set_overflow_x(Overflow::MozHiddenUnscrollable); 254 box_style.set_overflow_y(Overflow::MozHiddenUnscrollable); 255 } 256 } 257 } 258 259 /// When mathvariant is not "none", font-weight and font-style are 260 /// both forced to "normal". 261 #[cfg(feature = "gecko")] adjust_for_mathvariant(&mut self)262 fn adjust_for_mathvariant(&mut self) { 263 use properties::longhands::_moz_math_variant::computed_value::T as MozMathVariant; 264 use properties::longhands::font_style::computed_value::T as FontStyle; 265 use properties::longhands::font_weight::computed_value::T as FontWeight; 266 if self.style.get_font().clone__moz_math_variant() != MozMathVariant::None { 267 let font_style = self.style.mutate_font(); 268 // Sadly we don't have a nice name for the computed value 269 // of "font-weight: normal". 270 font_style.set_font_weight(FontWeight::normal()); 271 font_style.set_font_style(FontStyle::Normal); 272 } 273 } 274 275 /// This implements an out-of-date spec. The new spec moves the handling of 276 /// this to layout, which Gecko implements but Servo doesn't. 277 /// 278 /// See https://github.com/servo/servo/issues/15229 279 #[cfg(feature = "servo")] adjust_for_alignment(&mut self, layout_parent_style: &ComputedValues)280 fn adjust_for_alignment(&mut self, layout_parent_style: &ComputedValues) { 281 use computed_values::align_items::T as AlignItems; 282 use computed_values::align_self::T as AlignSelf; 283 284 if self.style.get_position().clone_align_self() == AlignSelf::Auto && 285 !self.style.out_of_flow_positioned() { 286 let self_align = 287 match layout_parent_style.get_position().clone_align_items() { 288 AlignItems::Stretch => AlignSelf::Stretch, 289 AlignItems::Baseline => AlignSelf::Baseline, 290 AlignItems::FlexStart => AlignSelf::FlexStart, 291 AlignItems::FlexEnd => AlignSelf::FlexEnd, 292 AlignItems::Center => AlignSelf::Center, 293 }; 294 self.style.mutate_position().set_align_self(self_align); 295 } 296 } 297 298 /// The initial value of border-*-width may be changed at computed value 299 /// time. 300 /// 301 /// This is moved to properties.rs for convenience. adjust_for_border_width(&mut self)302 fn adjust_for_border_width(&mut self) { 303 properties::adjust_border_width(self.style); 304 } 305 306 /// The initial value of outline-width may be changed at computed value time. adjust_for_outline(&mut self)307 fn adjust_for_outline(&mut self) { 308 if self.style.get_outline().clone_outline_style().none_or_hidden() && 309 self.style.get_outline().outline_has_nonzero_width() { 310 self.style.mutate_outline().set_outline_width(Au(0).into()); 311 } 312 } 313 314 /// CSS3 overflow-x and overflow-y require some fixup as well in some 315 /// cases. 316 /// 317 /// overflow: clip and overflow: visible are meaningful only when used in 318 /// both dimensions. adjust_for_overflow(&mut self)319 fn adjust_for_overflow(&mut self) { 320 let original_overflow_x = self.style.get_box().clone_overflow_x(); 321 let original_overflow_y = self.style.get_box().clone_overflow_y(); 322 323 let mut overflow_x = original_overflow_x; 324 let mut overflow_y = original_overflow_y; 325 326 if overflow_x == overflow_y { 327 return; 328 } 329 330 // If 'visible' is specified but doesn't match the other dimension, 331 // it turns into 'auto'. 332 if overflow_x == Overflow::Visible { 333 overflow_x = Overflow::Auto; 334 } 335 336 if overflow_y == Overflow::Visible { 337 overflow_y = Overflow::Auto; 338 } 339 340 #[cfg(feature = "gecko")] 341 { 342 // overflow: clip is deprecated, so convert to hidden if it's 343 // specified in only one dimension. 344 if overflow_x == Overflow::MozHiddenUnscrollable { 345 overflow_x = Overflow::Hidden; 346 } 347 if overflow_y == Overflow::MozHiddenUnscrollable { 348 overflow_y = Overflow::Hidden; 349 } 350 } 351 352 if overflow_x != original_overflow_x || 353 overflow_y != original_overflow_y { 354 let box_style = self.style.mutate_box(); 355 box_style.set_overflow_x(overflow_x); 356 box_style.set_overflow_y(overflow_y); 357 } 358 } 359 360 /// Native anonymous content converts display:contents into display:inline. 361 #[cfg(feature = "gecko")] adjust_for_prohibited_display_contents(&mut self)362 fn adjust_for_prohibited_display_contents(&mut self) { 363 // TODO: We should probably convert display:contents into display:none 364 // in some cases too: https://drafts.csswg.org/css-display/#unbox 365 // 366 // FIXME(emilio): ::before and ::after should support display: contents, 367 // see bug 1418138. 368 if self.style.pseudo.is_none() || 369 self.style.get_box().clone_display() != Display::Contents { 370 return; 371 } 372 373 self.style.mutate_box().set_display(Display::Inline); 374 } 375 376 /// If a <fieldset> has grid/flex display type, we need to inherit 377 /// this type into its ::-moz-fieldset-content anonymous box. 378 /// 379 /// NOTE(emilio): We don't need to handle the display change for this case 380 /// in matching.rs because anonymous box restyling works separately to the 381 /// normal cascading process. 382 #[cfg(feature = "gecko")] adjust_for_fieldset_content( &mut self, layout_parent_style: &ComputedValues, )383 fn adjust_for_fieldset_content( 384 &mut self, 385 layout_parent_style: &ComputedValues, 386 ) { 387 match self.style.pseudo { 388 Some(ref p) if p.is_fieldset_content() => {}, 389 _ => return, 390 } 391 392 debug_assert_eq!(self.style.get_box().clone_display(), Display::Block); 393 // TODO We actually want style from parent rather than layout 394 // parent, so that this fixup doesn't happen incorrectly when 395 // when <fieldset> has "display: contents". 396 let parent_display = layout_parent_style.get_box().clone_display(); 397 let new_display = match parent_display { 398 Display::Flex | 399 Display::InlineFlex => Some(Display::Flex), 400 Display::Grid | 401 Display::InlineGrid => Some(Display::Grid), 402 _ => None, 403 }; 404 if let Some(new_display) = new_display { 405 self.style.mutate_box().set_display(new_display); 406 } 407 } 408 409 /// -moz-center, -moz-left and -moz-right are used for HTML's alignment. 410 /// 411 /// This is covering the <div align="right"><table>...</table></div> case. 412 /// 413 /// In this case, we don't want to inherit the text alignment into the 414 /// table. 415 #[cfg(feature = "gecko")] adjust_for_table_text_align(&mut self)416 fn adjust_for_table_text_align(&mut self) { 417 use properties::longhands::text_align::computed_value::T as TextAlign; 418 if self.style.get_box().clone_display() != Display::Table { 419 return; 420 } 421 422 match self.style.get_inheritedtext().clone_text_align() { 423 TextAlign::MozLeft | 424 TextAlign::MozCenter | 425 TextAlign::MozRight => {}, 426 _ => return, 427 } 428 429 self.style.mutate_inheritedtext().set_text_align(TextAlign::Start) 430 } 431 432 /// Computes the used text decoration for Servo. 433 /// 434 /// FIXME(emilio): This is a layout tree concept, should move away from 435 /// style, since otherwise we're going to have the same subtle bugs WebKit 436 /// and Blink have with this very same thing. 437 #[cfg(feature = "servo")] adjust_for_text_decorations_in_effect(&mut self)438 fn adjust_for_text_decorations_in_effect(&mut self) { 439 use values::computed::text::TextDecorationsInEffect; 440 441 let decorations_in_effect = TextDecorationsInEffect::from_style(&self.style); 442 if self.style.get_inheritedtext().text_decorations_in_effect != decorations_in_effect { 443 self.style.mutate_inheritedtext().text_decorations_in_effect = decorations_in_effect; 444 } 445 } 446 447 #[cfg(feature = "gecko")] should_suppress_linebreak( &self, layout_parent_style: &ComputedValues, ) -> bool448 fn should_suppress_linebreak( 449 &self, 450 layout_parent_style: &ComputedValues, 451 ) -> bool { 452 // Line break suppression should only be propagated to in-flow children. 453 if self.style.floated() || self.style.out_of_flow_positioned() { 454 return false; 455 } 456 let parent_display = layout_parent_style.get_box().clone_display(); 457 if layout_parent_style.flags.contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK) { 458 // Line break suppression is propagated to any children of 459 // line participants. 460 if parent_display.is_line_participant() { 461 return true; 462 } 463 } 464 match self.style.get_box().clone_display() { 465 // Ruby base and text are always non-breakable. 466 Display::RubyBase | 467 Display::RubyText => true, 468 // Ruby base container and text container are breakable. 469 // Note that, when certain HTML tags, e.g. form controls, have ruby 470 // level container display type, they could also escape from the 471 // line break suppression flag while they shouldn't. However, it is 472 // generally fine since they themselves are non-breakable. 473 Display::RubyBaseContainer | 474 Display::RubyTextContainer => false, 475 // Anything else is non-breakable if and only if its layout parent 476 // has a ruby display type, because any of the ruby boxes can be 477 // anonymous. 478 _ => parent_display.is_ruby_type(), 479 } 480 } 481 482 /// Do ruby-related style adjustments, which include: 483 /// * propagate the line break suppression flag, 484 /// * inlinify block descendants, 485 /// * suppress border and padding for ruby level containers, 486 /// * correct unicode-bidi. 487 #[cfg(feature = "gecko")] adjust_for_ruby<E>( &mut self, layout_parent_style: &ComputedValues, element: Option<E>, ) where E: TElement,488 fn adjust_for_ruby<E>( 489 &mut self, 490 layout_parent_style: &ComputedValues, 491 element: Option<E>, 492 ) 493 where 494 E: TElement, 495 { 496 use properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi; 497 498 let self_display = self.style.get_box().clone_display(); 499 // Check whether line break should be suppressed for this element. 500 if self.should_suppress_linebreak(layout_parent_style) { 501 self.style.flags.insert(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK); 502 // Inlinify the display type if allowed. 503 if !self.skip_item_display_fixup(element) { 504 let inline_display = self_display.inlinify(); 505 if self_display != inline_display { 506 self.style.mutate_box().set_adjusted_display(inline_display, false); 507 } 508 } 509 } 510 // Suppress border and padding for ruby level containers. 511 // This is actually not part of the spec. It is currently unspecified 512 // how border and padding should be handled for ruby level container, 513 // and suppressing them here make it easier for layout to handle. 514 if self_display.is_ruby_level_container() { 515 self.style.reset_border_struct(); 516 self.style.reset_padding_struct(); 517 } 518 519 // Force bidi isolation on all internal ruby boxes and ruby container 520 // per spec https://drafts.csswg.org/css-ruby-1/#bidi 521 if self_display.is_ruby_type() { 522 let new_value = match self.style.get_text().clone_unicode_bidi() { 523 UnicodeBidi::Normal | 524 UnicodeBidi::Embed => Some(UnicodeBidi::Isolate), 525 UnicodeBidi::BidiOverride => Some(UnicodeBidi::IsolateOverride), 526 _ => None, 527 }; 528 if let Some(new_value) = new_value { 529 self.style.mutate_text().set_unicode_bidi(new_value); 530 } 531 } 532 } 533 534 /// Computes the RELEVANT_LINK_VISITED flag based on the parent style and on 535 /// whether we're a relevant link. 536 /// 537 /// NOTE(emilio): We don't do this for text styles, which is... dubious, but 538 /// Gecko doesn't seem to do it either. It's extremely easy to do if needed 539 /// though. 540 /// 541 /// FIXME(emilio): This isn't technically a style adjustment thingie, could 542 /// it move somewhere else? adjust_for_visited<E>(&mut self, element: Option<E>) where E: TElement,543 fn adjust_for_visited<E>(&mut self, element: Option<E>) 544 where 545 E: TElement, 546 { 547 if !self.style.has_visited_style() { 548 return; 549 } 550 551 let is_link_element = 552 self.style.pseudo.is_none() && 553 element.map_or(false, |e| e.is_link()); 554 555 if !is_link_element { 556 return; 557 } 558 559 if element.unwrap().is_visited_link() { 560 self.style.flags.insert(ComputedValueFlags::IS_RELEVANT_LINK_VISITED); 561 } else { 562 // Need to remove to handle unvisited link inside visited. 563 self.style.flags.remove(ComputedValueFlags::IS_RELEVANT_LINK_VISITED); 564 } 565 } 566 567 /// Resolves "justify-items: auto" based on the inherited style if needed to 568 /// comply with: 569 /// 570 /// <https://drafts.csswg.org/css-align/#valdef-justify-items-legacy> 571 /// 572 /// (Note that "auto" is being renamed to "legacy") 573 #[cfg(feature = "gecko")] adjust_for_justify_items(&mut self)574 fn adjust_for_justify_items(&mut self) { 575 use values::specified::align; 576 let justify_items = self.style.get_position().clone_justify_items(); 577 if justify_items.specified.0 != align::AlignFlags::AUTO { 578 return; 579 } 580 581 let parent_justify_items = 582 self.style.get_parent_position().clone_justify_items(); 583 584 if !parent_justify_items.computed.0.contains(align::AlignFlags::LEGACY) { 585 return; 586 } 587 588 if parent_justify_items.computed == justify_items.computed { 589 return; 590 } 591 592 self.style 593 .mutate_position() 594 .set_computed_justify_items(parent_justify_items.computed); 595 } 596 597 /// Adjusts the style to account for various fixups that don't fit naturally 598 /// into the cascade. 599 /// 600 /// When comparing to Gecko, this is similar to the work done by 601 /// `nsStyleContext::ApplyStyleFixups`, plus some parts of 602 /// `nsStyleSet::GetContext`. adjust<E>( &mut self, layout_parent_style: &ComputedValues, element: Option<E>, flags: CascadeFlags, ) where E: TElement,603 pub fn adjust<E>( 604 &mut self, 605 layout_parent_style: &ComputedValues, 606 element: Option<E>, 607 flags: CascadeFlags, 608 ) 609 where 610 E: TElement, 611 { 612 if cfg!(debug_assertions) { 613 if element.and_then(|e| e.implemented_pseudo_element()).is_some() { 614 // It'd be nice to assert `self.style.pseudo == Some(&pseudo)`, 615 // but we do resolve ::-moz-list pseudos on ::before / ::after 616 // content, sigh. 617 debug_assert!( 618 self.style.pseudo.is_some(), 619 "Someone really messed up" 620 ); 621 } 622 } 623 // FIXME(emilio): The apply_declarations callsite in Servo's 624 // animation, and the font stuff for Gecko 625 // (Stylist::compute_for_declarations) should pass an element to 626 // cascade(), then we can make this assertion hold everywhere. 627 // debug_assert!( 628 // element.is_some() || self.style.pseudo.is_some(), 629 // "Should always have an element around for non-pseudo styles" 630 // ); 631 632 // Don't adjust visited styles, visited-dependent properties aren't 633 // affected by these adjustments and it'd be just wasted work anyway. 634 // 635 // It also doesn't make much sense to adjust them, since we don't 636 // cascade most properties anyway, and they wouldn't be looked up. 637 if flags.contains(CascadeFlags::VISITED_DEPENDENT_ONLY) { 638 return; 639 } 640 641 self.adjust_for_visited(element); 642 #[cfg(feature = "gecko")] 643 { 644 self.adjust_for_prohibited_display_contents(); 645 self.adjust_for_fieldset_content(layout_parent_style); 646 } 647 self.adjust_for_top_layer(); 648 self.blockify_if_necessary(layout_parent_style, element); 649 self.adjust_for_position(); 650 self.adjust_for_overflow(); 651 #[cfg(feature = "gecko")] 652 { 653 self.adjust_for_table_text_align(); 654 self.adjust_for_contain(); 655 self.adjust_for_mathvariant(); 656 self.adjust_for_justify_items(); 657 } 658 #[cfg(feature = "servo")] 659 { 660 self.adjust_for_alignment(layout_parent_style); 661 } 662 self.adjust_for_border_width(); 663 self.adjust_for_outline(); 664 self.adjust_for_writing_mode(layout_parent_style); 665 #[cfg(feature = "gecko")] 666 { 667 self.adjust_for_ruby(layout_parent_style, element); 668 } 669 #[cfg(feature = "servo")] 670 { 671 self.adjust_for_text_decorations_in_effect(); 672 } 673 self.set_bits(); 674 } 675 } 676