Source code

Revision control

Other Tools

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 https://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 crate::dom::TElement;
9
use crate::properties::computed_value_flags::ComputedValueFlags;
10
use crate::properties::longhands::display::computed_value::T as Display;
11
use crate::properties::longhands::float::computed_value::T as Float;
12
use crate::properties::longhands::overflow_x::computed_value::T as Overflow;
13
use crate::properties::longhands::position::computed_value::T as Position;
14
use crate::properties::{self, ComputedValues, StyleBuilder};
15
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
16
use crate::values::specified::box_::DisplayInside;
17
use app_units::Au;
18
19
/// A struct that implements all the adjustment methods.
20
///
21
/// NOTE(emilio): If new adjustments are introduced that depend on reset
22
/// properties of the parent, you may need tweaking the
23
/// `ChildCascadeRequirement` code in `matching.rs`.
24
///
25
/// NOTE(emilio): Also, if new adjustments are introduced that break the
26
/// following invariant:
27
///
28
/// Given same tag name, namespace, rules and parent style, two elements would
29
/// end up with exactly the same style.
30
///
31
/// Then you need to adjust the lookup_by_rules conditions in the sharing cache.
32
pub struct StyleAdjuster<'a, 'b: 'a> {
33
style: &'a mut StyleBuilder<'b>,
34
}
35
36
#[cfg(feature = "gecko")]
37
fn is_topmost_svg_svg_element<E>(e: E) -> bool
38
where
39
E: TElement,
40
{
41
debug_assert!(e.is_svg_element());
42
if e.local_name() != &*atom!("svg") {
43
return false;
44
}
45
46
let parent = match e.traversal_parent() {
47
Some(n) => n,
48
None => return true,
49
};
50
51
if !parent.is_svg_element() {
52
return true;
53
}
54
55
parent.local_name() == &*atom!("foreignObject")
56
}
57
59
#[cfg(feature = "gecko")]
60
fn is_effective_display_none_for_display_contents<E>(element: E) -> bool
61
where
62
E: TElement,
63
{
64
use crate::Atom;
65
66
// FIXME(emilio): This should be an actual static.
67
lazy_static! {
68
static ref SPECIAL_HTML_ELEMENTS: [Atom; 16] = [
69
atom!("br"),
70
atom!("wbr"),
71
atom!("meter"),
72
atom!("progress"),
73
atom!("canvas"),
74
atom!("embed"),
75
atom!("object"),
76
atom!("audio"),
77
atom!("iframe"),
78
atom!("img"),
79
atom!("video"),
80
atom!("frame"),
81
atom!("frameset"),
82
atom!("input"),
83
atom!("textarea"),
84
atom!("select"),
85
];
86
}
87
89
//
90
// There's a note about "Unknown elements", but there's not a good way to
91
// know what that means, or to get that information from here, and no other
92
// UA implements this either.
93
lazy_static! {
94
static ref SPECIAL_SVG_ELEMENTS: [Atom; 6] = [
95
atom!("svg"),
96
atom!("a"),
97
atom!("g"),
98
atom!("use"),
99
atom!("tspan"),
100
atom!("textPath"),
101
];
102
}
103
105
if element.is_html_element() {
106
let local_name = element.local_name();
107
return SPECIAL_HTML_ELEMENTS
108
.iter()
109
.any(|name| &**name == local_name);
110
}
111
113
if element.is_svg_element() {
114
if is_topmost_svg_svg_element(element) {
115
return true;
116
}
117
let local_name = element.local_name();
118
return !SPECIAL_SVG_ELEMENTS
119
.iter()
120
.any(|name| &**name == local_name);
121
}
122
124
//
125
// We always treat XUL as display: none. We don't use display:
126
// contents in XUL anyway, so should be fine to be consistent with
127
// MathML unless there's a use case for it.
128
if element.is_mathml_element() || element.is_xul_element() {
129
return true;
130
}
131
132
false
133
}
134
135
impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
136
/// Trivially constructs a new StyleAdjuster.
137
#[inline]
138
pub fn new(style: &'a mut StyleBuilder<'b>) -> Self {
139
StyleAdjuster { style }
140
}
141
143
///
144
/// Any position value other than 'absolute' and 'fixed' are
145
/// computed to 'absolute' if the element is in a top layer.
146
///
147
fn adjust_for_top_layer(&mut self) {
148
if !self.style.is_absolutely_positioned() && self.style.in_top_layer() {
149
self.style.mutate_box().set_position(Position::Absolute);
150
}
151
}
152
153
/// CSS 2.1 section 9.7:
154
///
155
/// If 'position' has the value 'absolute' or 'fixed', [...] the computed
156
/// value of 'float' is 'none'.
157
///
158
fn adjust_for_position(&mut self) {
159
if self.style.is_absolutely_positioned() && self.style.is_floating() {
160
self.style.mutate_box().set_float(Float::None);
161
}
162
}
163
164
/// Whether we should skip any item-based display property blockification on
165
/// this element.
166
fn skip_item_display_fixup<E>(&self, element: Option<E>) -> bool
167
where
168
E: TElement,
169
{
170
if let Some(pseudo) = self.style.pseudo {
171
return pseudo.skip_item_display_fixup();
172
}
173
174
element.map_or(false, |e| e.skip_item_display_fixup())
175
}
176
177
/// Apply the blockification rules based on the table in CSS 2.2 section 9.7.
179
/// A ::marker pseudo-element with 'list-style-position:outside' needs to
180
/// have its 'display' blockified, unless the ::marker is for an inline
181
/// list-item (for which 'list-style-position:outside' behaves as 'inside').
183
fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
184
where
185
E: TElement,
186
{
187
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
188
use crate::computed_values::list_style_position::T as ListStylePosition;
189
190
let mut blockify = false;
191
macro_rules! blockify_if {
192
($if_what:expr) => {
193
if !blockify {
194
blockify = $if_what;
195
}
196
};
197
}
198
199
let is_root = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_root());
200
blockify_if!(is_root);
201
if !self.skip_item_display_fixup(element) {
202
let parent_display = layout_parent_style.get_box().clone_display();
203
blockify_if!(parent_display.is_item_container());
204
}
205
206
let is_item_or_root = blockify;
207
208
blockify_if!(self.style.is_floating());
209
blockify_if!(self.style.is_absolutely_positioned());
210
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
211
blockify_if!(
212
self.style.pseudo.map_or(false, |p| p.is_marker()) &&
213
self.style.get_parent_list().clone_list_style_position() ==
214
ListStylePosition::Outside &&
215
layout_parent_style.get_box().clone_display().inside() != DisplayInside::Inline
216
);
217
218
if !blockify {
219
return;
220
}
221
222
let display = self.style.get_box().clone_display();
223
let blockified_display = display.equivalent_block_display(is_root);
224
if display != blockified_display {
225
self.style
226
.mutate_box()
227
.set_adjusted_display(blockified_display, is_item_or_root);
228
}
229
}
230
231
/// Compute a few common flags for both text and element's style.
232
pub fn set_bits(&mut self) {
233
let display = self.style.get_box().clone_display();
234
235
if !display.is_contents() &&
236
!self
237
.style
238
.get_text()
239
.clone_text_decoration_line()
240
.is_empty()
241
{
242
self.style
243
.add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
244
}
245
246
if self.style.is_pseudo_element() {
247
self.style
248
.add_flags(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE);
249
}
250
251
#[cfg(feature = "servo-layout-2013")]
252
{
253
if self.style.get_parent_column().is_multicol() {
254
self.style.add_flags(ComputedValueFlags::CAN_BE_FRAGMENTED);
255
}
256
}
257
}
258
259
/// Adjust the style for text style.
260
///
261
/// The adjustments here are a subset of the adjustments generally, because
262
/// text only inherits properties.
263
///
264
/// Note that this, for Gecko, comes through Servo_ComputedValues_Inherit.
265
#[cfg(feature = "gecko")]
266
pub fn adjust_for_text(&mut self) {
267
self.adjust_for_text_combine_upright();
268
self.adjust_for_text_in_ruby();
269
self.set_bits();
270
}
271
272
/// Change writing mode of the text frame for text-combine-upright.
273
///
274
/// It is safe to look at our own style because we are looking at inherited
275
/// properties, and text is just plain inheritance.
276
///
277
/// TODO(emilio): we should (Gecko too) revise these adjustments in presence
278
/// of display: contents.
279
///
280
/// FIXME(emilio): How does this play with logical properties? Doesn't
281
/// mutating writing-mode change the potential physical sides chosen?
282
#[cfg(feature = "gecko")]
283
fn adjust_for_text_combine_upright(&mut self) {
284
use crate::computed_values::text_combine_upright::T as TextCombineUpright;
285
use crate::computed_values::writing_mode::T as WritingMode;
286
use crate::logical_geometry;
287
288
let writing_mode = self.style.get_inherited_box().clone_writing_mode();
289
let text_combine_upright = self.style.get_inherited_text().clone_text_combine_upright();
290
291
if writing_mode != WritingMode::HorizontalTb &&
292
text_combine_upright == TextCombineUpright::All
293
{
294
self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED);
295
self.style
296
.mutate_inherited_box()
297
.set_writing_mode(WritingMode::HorizontalTb);
298
self.style.writing_mode =
299
logical_geometry::WritingMode::new(self.style.get_inherited_box());
300
}
301
}
302
303
/// Unconditionally propagates the line break suppression flag to text, and
304
/// additionally it applies it if it is in any ruby box.
305
///
306
/// This is necessary because its parent may not itself have the flag set
307
/// (e.g. ruby or ruby containers), thus we may not inherit the flag from
308
/// them.
309
#[cfg(feature = "gecko")]
310
fn adjust_for_text_in_ruby(&mut self) {
311
let parent_display = self.style.get_parent_box().clone_display();
312
if parent_display.is_ruby_type() ||
313
self.style
314
.get_parent_flags()
315
.contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
316
{
317
self.style
318
.add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
319
}
320
}
321
323
///
324
/// If a box has a different writing-mode value than its containing
325
/// block:
326
///
327
/// - If the box has a specified display of inline, its display
328
/// computes to inline-block. [CSS21]
329
///
330
/// This matches the adjustment that Gecko does, not exactly following
331
/// the spec. See also:
332
///
335
fn adjust_for_writing_mode(&mut self, layout_parent_style: &ComputedValues) {
336
let our_writing_mode = self.style.get_inherited_box().clone_writing_mode();
337
let parent_writing_mode = layout_parent_style.get_inherited_box().clone_writing_mode();
338
339
if our_writing_mode != parent_writing_mode &&
340
self.style.get_box().clone_display() == Display::Inline
341
{
342
// TODO(emilio): Figure out if we can just set the adjusted display
343
// on Gecko too and unify this code path.
344
if cfg!(feature = "servo") {
345
self.style
346
.mutate_box()
347
.set_adjusted_display(Display::InlineBlock, false);
348
} else {
349
self.style.mutate_box().set_display(Display::InlineBlock);
350
}
351
}
352
}
353
354
/// When mathvariant is not "none", font-weight and font-style are
355
/// both forced to "normal".
356
#[cfg(feature = "gecko")]
357
fn adjust_for_mathvariant(&mut self) {
358
use crate::properties::longhands::_moz_math_variant::computed_value::T as MozMathVariant;
359
use crate::properties::longhands::font_weight::computed_value::T as FontWeight;
360
use crate::values::generics::font::FontStyle;
361
if self.style.get_font().clone__moz_math_variant() != MozMathVariant::None {
362
let font_style = self.style.mutate_font();
363
font_style.set_font_weight(FontWeight::normal());
364
font_style.set_font_style(FontStyle::Normal);
365
}
366
}
367
368
/// This implements an out-of-date spec. The new spec moves the handling of
369
/// this to layout, which Gecko implements but Servo doesn't.
370
///
372
#[cfg(feature = "servo")]
373
fn adjust_for_alignment(&mut self, layout_parent_style: &ComputedValues) {
374
use crate::computed_values::align_items::T as AlignItems;
375
use crate::computed_values::align_self::T as AlignSelf;
376
377
if self.style.get_position().clone_align_self() == AlignSelf::Auto &&
378
!self.style.is_absolutely_positioned()
379
{
380
let self_align = match layout_parent_style.get_position().clone_align_items() {
381
AlignItems::Stretch => AlignSelf::Stretch,
382
AlignItems::Baseline => AlignSelf::Baseline,
383
AlignItems::FlexStart => AlignSelf::FlexStart,
384
AlignItems::FlexEnd => AlignSelf::FlexEnd,
385
AlignItems::Center => AlignSelf::Center,
386
};
387
self.style.mutate_position().set_align_self(self_align);
388
}
389
}
390
391
/// The initial value of border-*-width may be changed at computed value
392
/// time.
393
///
394
/// This is moved to properties.rs for convenience.
395
fn adjust_for_border_width(&mut self) {
396
properties::adjust_border_width(self.style);
397
}
398
399
/// The initial value of outline-width may be changed at computed value time.
400
fn adjust_for_outline(&mut self) {
401
if self
402
.style
403
.get_outline()
404
.clone_outline_style()
405
.none_or_hidden() &&
406
self.style.get_outline().outline_has_nonzero_width()
407
{
408
self.style.mutate_outline().set_outline_width(Au(0).into());
409
}
410
}
411
412
/// CSS3 overflow-x and overflow-y require some fixup as well in some
413
/// cases.
414
///
415
/// overflow: clip and overflow: visible are meaningful only when used in
416
/// both dimensions.
417
fn adjust_for_overflow(&mut self) {
418
let original_overflow_x = self.style.get_box().clone_overflow_x();
419
let original_overflow_y = self.style.get_box().clone_overflow_y();
420
421
let mut overflow_x = original_overflow_x;
422
let mut overflow_y = original_overflow_y;
423
424
if overflow_x == overflow_y {
425
return;
426
}
427
428
// If 'visible' is specified but doesn't match the other dimension,
429
// it turns into 'auto'.
430
if overflow_x == Overflow::Visible {
431
overflow_x = Overflow::Auto;
432
}
433
434
if overflow_y == Overflow::Visible {
435
overflow_y = Overflow::Auto;
436
}
437
438
#[cfg(feature = "gecko")]
439
{
440
// overflow: clip is deprecated, so convert to hidden if it's
441
// specified in only one dimension.
442
if overflow_x == Overflow::MozHiddenUnscrollable {
443
overflow_x = Overflow::Hidden;
444
}
445
if overflow_y == Overflow::MozHiddenUnscrollable {
446
overflow_y = Overflow::Hidden;
447
}
448
}
449
450
if overflow_x != original_overflow_x || overflow_y != original_overflow_y {
451
let box_style = self.style.mutate_box();
452
box_style.set_overflow_x(overflow_x);
453
box_style.set_overflow_y(overflow_y);
454
}
455
}
456
457
/// Handles the relevant sections in:
458
///
460
///
461
/// And forbidding display: contents in pseudo-elements, at least for now.
462
#[cfg(feature = "gecko")]
463
fn adjust_for_prohibited_display_contents<E>(&mut self, element: Option<E>)
464
where
465
E: TElement,
466
{
467
if self.style.get_box().clone_display() != Display::Contents {
468
return;
469
}
470
471
// FIXME(emilio): ::before and ::after should support display: contents,
472
// see bug 1418138.
473
if self.style.pseudo.is_some() {
474
self.style.mutate_box().set_display(Display::Inline);
475
return;
476
}
477
478
let element = match element {
479
Some(e) => e,
480
None => return,
481
};
482
483
if is_effective_display_none_for_display_contents(element) {
484
self.style.mutate_box().set_display(Display::None);
485
}
486
}
487
488
/// If a <fieldset> has grid/flex display type, we need to inherit
489
/// this type into its ::-moz-fieldset-content anonymous box.
490
///
491
/// NOTE(emilio): We don't need to handle the display change for this case
492
/// in matching.rs because anonymous box restyling works separately to the
493
/// normal cascading process.
494
#[cfg(feature = "gecko")]
495
fn adjust_for_fieldset_content(&mut self, layout_parent_style: &ComputedValues) {
496
match self.style.pseudo {
497
Some(ref p) if p.is_fieldset_content() => {},
498
_ => return,
499
}
500
501
debug_assert_eq!(self.style.get_box().clone_display(), Display::Block);
502
// TODO We actually want style from parent rather than layout
503
// parent, so that this fixup doesn't happen incorrectly when
504
// when <fieldset> has "display: contents".
505
let parent_display = layout_parent_style.get_box().clone_display();
506
let new_display = match parent_display {
507
Display::Flex | Display::InlineFlex => Some(Display::Flex),
508
Display::Grid | Display::InlineGrid => Some(Display::Grid),
509
_ => None,
510
};
511
if let Some(new_display) = new_display {
512
self.style.mutate_box().set_display(new_display);
513
}
514
}
515
516
/// -moz-center, -moz-left and -moz-right are used for HTML's alignment.
517
///
518
/// This is covering the <div align="right"><table>...</table></div> case.
519
///
520
/// In this case, we don't want to inherit the text alignment into the
521
/// table.
522
#[cfg(feature = "gecko")]
523
fn adjust_for_table_text_align(&mut self) {
524
use crate::properties::longhands::text_align::computed_value::T as TextAlign;
525
if self.style.get_box().clone_display() != Display::Table {
526
return;
527
}
528
529
match self.style.get_inherited_text().clone_text_align() {
530
TextAlign::MozLeft | TextAlign::MozCenter | TextAlign::MozRight => {},
531
_ => return,
532
}
533
534
self.style
535
.mutate_inherited_text()
536
.set_text_align(TextAlign::Start)
537
}
538
539
/// Computes the used text decoration for Servo.
540
///
541
/// FIXME(emilio): This is a layout tree concept, should move away from
542
/// style, since otherwise we're going to have the same subtle bugs WebKit
543
/// and Blink have with this very same thing.
544
#[cfg(feature = "servo")]
545
fn adjust_for_text_decorations_in_effect(&mut self) {
546
use crate::values::computed::text::TextDecorationsInEffect;
547
548
let decorations_in_effect = TextDecorationsInEffect::from_style(&self.style);
549
if self.style.get_inherited_text().text_decorations_in_effect != decorations_in_effect {
550
self.style
551
.mutate_inherited_text()
552
.text_decorations_in_effect = decorations_in_effect;
553
}
554
}
555
556
#[cfg(feature = "gecko")]
557
fn should_suppress_linebreak(&self, layout_parent_style: &ComputedValues) -> bool {
558
// Line break suppression should only be propagated to in-flow children.
559
if self.style.is_floating() || self.style.is_absolutely_positioned() {
560
return false;
561
}
562
let parent_display = layout_parent_style.get_box().clone_display();
563
if layout_parent_style
564
.flags
565
.contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
566
{
567
// Line break suppression is propagated to any children of
568
// line participants.
569
if parent_display.is_line_participant() {
570
return true;
571
}
572
}
573
match self.style.get_box().clone_display() {
574
// Ruby base and text are always non-breakable.
575
Display::RubyBase | Display::RubyText => true,
576
// Ruby base container and text container are breakable.
577
// Note that, when certain HTML tags, e.g. form controls, have ruby
578
// level container display type, they could also escape from the
579
// line break suppression flag while they shouldn't. However, it is
580
// generally fine since they themselves are non-breakable.
581
Display::RubyBaseContainer | Display::RubyTextContainer => false,
582
// Anything else is non-breakable if and only if its layout parent
583
// has a ruby display type, because any of the ruby boxes can be
584
// anonymous.
585
_ => parent_display.is_ruby_type(),
586
}
587
}
588
589
/// Do ruby-related style adjustments, which include:
590
/// * propagate the line break suppression flag,
591
/// * inlinify block descendants,
592
/// * suppress border and padding for ruby level containers,
593
/// * correct unicode-bidi.
594
#[cfg(feature = "gecko")]
595
fn adjust_for_ruby<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
596
where
597
E: TElement,
598
{
599
use crate::properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi;
600
601
let self_display = self.style.get_box().clone_display();
602
// Check whether line break should be suppressed for this element.
603
if self.should_suppress_linebreak(layout_parent_style) {
604
self.style
605
.add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
606
// Inlinify the display type if allowed.
607
if !self.skip_item_display_fixup(element) {
608
let inline_display = self_display.inlinify();
609
if self_display != inline_display {
610
self.style
611
.mutate_box()
612
.set_adjusted_display(inline_display, false);
613
}
614
}
615
}
616
// Suppress border and padding for ruby level containers.
617
// This is actually not part of the spec. It is currently unspecified
618
// how border and padding should be handled for ruby level container,
619
// and suppressing them here make it easier for layout to handle.
620
if self_display.is_ruby_level_container() {
621
self.style.reset_border_struct();
622
self.style.reset_padding_struct();
623
}
624
625
// Force bidi isolation on all internal ruby boxes and ruby container
627
if self_display.is_ruby_type() {
628
let new_value = match self.style.get_text().clone_unicode_bidi() {
629
UnicodeBidi::Normal | UnicodeBidi::Embed => Some(UnicodeBidi::Isolate),
630
UnicodeBidi::BidiOverride => Some(UnicodeBidi::IsolateOverride),
631
_ => None,
632
};
633
if let Some(new_value) = new_value {
634
self.style.mutate_text().set_unicode_bidi(new_value);
635
}
636
}
637
}
638
639
/// Computes the RELEVANT_LINK_VISITED flag based on the parent style and on
640
/// whether we're a relevant link.
641
///
642
/// NOTE(emilio): We don't do this for text styles, which is... dubious, but
643
/// Gecko doesn't seem to do it either. It's extremely easy to do if needed
644
/// though.
645
///
646
/// FIXME(emilio): This isn't technically a style adjustment thingie, could
647
/// it move somewhere else?
648
fn adjust_for_visited<E>(&mut self, element: Option<E>)
649
where
650
E: TElement,
651
{
652
if !self.style.has_visited_style() {
653
return;
654
}
655
656
let is_link_element = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_link());
657
658
if !is_link_element {
659
return;
660
}
661
662
if element.unwrap().is_visited_link() {
663
self.style
664
.add_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
665
} else {
666
// Need to remove to handle unvisited link inside visited.
667
self.style
668
.remove_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
669
}
670
}
671
672
/// Resolves "justify-items: legacy" based on the inherited style if needed
673
/// to comply with:
674
///
676
#[cfg(feature = "gecko")]
677
fn adjust_for_justify_items(&mut self) {
678
use crate::values::specified::align;
679
let justify_items = self.style.get_position().clone_justify_items();
680
if justify_items.specified.0 != align::AlignFlags::LEGACY {
681
return;
682
}
683
684
let parent_justify_items = self.style.get_parent_position().clone_justify_items();
685
686
if !parent_justify_items
687
.computed
688
.0
689
.contains(align::AlignFlags::LEGACY)
690
{
691
return;
692
}
693
694
if parent_justify_items.computed == justify_items.computed {
695
return;
696
}
697
698
self.style
699
.mutate_position()
700
.set_computed_justify_items(parent_justify_items.computed);
701
}
702
703
/// If '-webkit-appearance' is 'menulist' on a <select> element then
704
/// the computed value of 'line-height' is 'normal'.
705
///
707
#[cfg(feature = "gecko")]
708
fn adjust_for_appearance<E>(&mut self, element: Option<E>)
709
where
710
E: TElement,
711
{
712
use crate::properties::longhands::_moz_appearance::computed_value::T as Appearance;
713
use crate::properties::longhands::line_height::computed_value::T as LineHeight;
714
715
if self.style.get_box().clone__moz_appearance() == Appearance::Menulist {
716
if self.style.get_inherited_text().clone_line_height() == LineHeight::normal() {
717
return;
718
}
719
if self.style.pseudo.is_some() {
720
return;
721
}
722
let is_html_select_element = element.map_or(false, |e| {
723
e.is_html_element() && e.local_name() == &*local_name!("select")
724
});
725
if !is_html_select_element {
726
return;
727
}
728
self.style
729
.mutate_inherited_text()
730
.set_line_height(LineHeight::normal());
731
}
732
}
733
734
/// Adjusts the style to account for various fixups that don't fit naturally
735
/// into the cascade.
736
///
737
/// When comparing to Gecko, this is similar to the work done by
738
/// `ComputedStyle::ApplyStyleFixups`, plus some parts of
739
/// `nsStyleSet::GetContext`.
740
pub fn adjust<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
741
where
742
E: TElement,
743
{
744
if cfg!(debug_assertions) {
745
if element.map_or(false, |e| e.is_pseudo_element()) {
746
// It'd be nice to assert `self.style.pseudo == Some(&pseudo)`,
747
// but we do resolve ::-moz-list pseudos on ::before / ::after
748
// content, sigh.
749
debug_assert!(self.style.pseudo.is_some(), "Someone really messed up");
750
}
751
}
752
// FIXME(emilio): The apply_declarations callsite in Servo's
753
// animation, and the font stuff for Gecko
754
// (Stylist::compute_for_declarations) should pass an element to
755
// cascade(), then we can make this assertion hold everywhere.
756
// debug_assert!(
757
// element.is_some() || self.style.pseudo.is_some(),
758
// "Should always have an element around for non-pseudo styles"
759
// );
760
761
self.adjust_for_visited(element);
762
#[cfg(feature = "gecko")]
763
{
764
self.adjust_for_prohibited_display_contents(element);
765
self.adjust_for_fieldset_content(layout_parent_style);
766
}
767
self.adjust_for_top_layer();
768
self.blockify_if_necessary(layout_parent_style, element);
769
self.adjust_for_position();
770
self.adjust_for_overflow();
771
#[cfg(feature = "gecko")]
772
{
773
self.adjust_for_table_text_align();
774
self.adjust_for_mathvariant();
775
self.adjust_for_justify_items();
776
}
777
#[cfg(feature = "servo")]
778
{
779
self.adjust_for_alignment(layout_parent_style);
780
}
781
self.adjust_for_border_width();
782
self.adjust_for_outline();
783
self.adjust_for_writing_mode(layout_parent_style);
784
#[cfg(feature = "gecko")]
785
{
786
self.adjust_for_ruby(layout_parent_style, element);
787
}
788
#[cfg(feature = "servo")]
789
{
790
self.adjust_for_text_decorations_in_effect();
791
}
792
#[cfg(feature = "gecko")]
793
{
794
self.adjust_for_appearance(element);
795
}
796
self.set_bits();
797
}
798
}