diff --git a/devtools/server/actors/animation-type-longhand.js b/devtools/server/actors/animation-type-longhand.js index 043d1243443b..5966179331b8 100644 --- a/devtools/server/actors/animation-type-longhand.js +++ b/devtools/server/actors/animation-type-longhand.js @@ -87,6 +87,7 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [ "image-orientation", "image-rendering", "ime-mode", + "-moz-inert", "initial-letter", "isolation", "justify-content", diff --git a/dom/events/EventStates.h b/dom/events/EventStates.h index 59de4c556959..3aa7a055ae7c 100644 --- a/dom/events/EventStates.h +++ b/dom/events/EventStates.h @@ -294,6 +294,8 @@ class EventStates { #define NS_EVENT_STATE_FOCUS_VISIBLE NS_DEFINE_EVENT_STATE_MACRO(52) // Modal element #define NS_EVENT_STATE_MODAL_DIALOG NS_DEFINE_EVENT_STATE_MACRO(53) +// Inert subtrees +#define NS_EVENT_STATE_MOZINERT NS_DEFINE_EVENT_STATE_MACRO(54) /** * NOTE: do not go over 63 without updating EventStates::InternalType! @@ -329,7 +331,8 @@ class EventStates { NS_EVENT_STATE_DRAGOVER | NS_EVENT_STATE_FOCUS | NS_EVENT_STATE_FOCUSRING | \ NS_EVENT_STATE_FOCUS_WITHIN | NS_EVENT_STATE_FULLSCREEN | \ NS_EVENT_STATE_HOVER | NS_EVENT_STATE_URLTARGET | \ - NS_EVENT_STATE_FOCUS_VISIBLE | NS_EVENT_STATE_MODAL_DIALOG) + NS_EVENT_STATE_FOCUS_VISIBLE | NS_EVENT_STATE_MODAL_DIALOG | \ + NS_EVENT_STATE_MOZINERT) #define INTRINSIC_STATES (~EXTERNALLY_MANAGED_STATES) diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp index 31445731f321..a4380b57f79f 100644 --- a/layout/generic/nsIFrame.cpp +++ b/layout/generic/nsIFrame.cpp @@ -9761,7 +9761,8 @@ bool nsIFrame::IsFocusable(int32_t* aTabIndex, bool aWithMouse) { if (mContent && mContent->IsElement() && IsVisibleConsideringAncestors() && Style()->GetPseudoType() != PseudoStyleType::anonymousFlexItem && - Style()->GetPseudoType() != PseudoStyleType::anonymousGridItem) { + Style()->GetPseudoType() != PseudoStyleType::anonymousGridItem && + StyleUI()->mInert != StyleInert::Inert) { const nsStyleUI* ui = StyleUI(); if (ui->mUserFocus != StyleUserFocus::Ignore && ui->mUserFocus != StyleUserFocus::None) { diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml index 369d62ae1a30..46202d1b81cd 100644 --- a/layout/style/ServoBindings.toml +++ b/layout/style/ServoBindings.toml @@ -97,6 +97,7 @@ rusty-enums = [ "mozilla::StyleFloat", "mozilla::StyleImageOrientation", "mozilla::StyleImageRendering", + "mozilla::StyleInert", "mozilla::StyleUserModify", "mozilla::StyleUserInput", "mozilla::StyleBoxDirection", diff --git a/layout/style/nsStyleConsts.h b/layout/style/nsStyleConsts.h index 278fff536f57..270d0727851c 100644 --- a/layout/style/nsStyleConsts.h +++ b/layout/style/nsStyleConsts.h @@ -286,6 +286,12 @@ enum class StyleUserModify : uint8_t { WriteOnly, }; +// -moz-inert +enum class StyleInert : uint8_t { + None, + Inert, +}; + // -moz-window-dragging enum class StyleWindowDragging : uint8_t { Default, diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 175277165a69..a07732e65c8f 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -3107,7 +3107,8 @@ LogicalSide nsStyleText::TextEmphasisSide(WritingMode aWM) const { // nsStyleUI::nsStyleUI(const Document& aDocument) - : mUserInput(StyleUserInput::Auto), + : mInert(StyleInert::None), + mUserInput(StyleUserInput::Auto), mUserModify(StyleUserModify::ReadOnly), mUserFocus(StyleUserFocus::None), mPointerEvents(StylePointerEvents::Auto), @@ -3118,7 +3119,8 @@ nsStyleUI::nsStyleUI(const Document& aDocument) } nsStyleUI::nsStyleUI(const nsStyleUI& aSource) - : mUserInput(aSource.mUserInput), + : mInert(aSource.mInert), + mUserInput(aSource.mUserInput), mUserModify(aSource.mUserModify), mUserFocus(aSource.mUserFocus), mPointerEvents(aSource.mPointerEvents), @@ -3179,7 +3181,7 @@ nsChangeHint nsStyleUI::CalcDifference(const nsStyleUI& aNewData) const { } } - if (mUserFocus != aNewData.mUserFocus) { + if (mUserFocus != aNewData.mUserFocus || mInert != aNewData.mInert) { hint |= nsChangeHint_NeutralChange; } diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 69f1cac2c65e..3998ca66df11 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1730,6 +1730,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUI { nsChangeHint CalcDifference(const nsStyleUI& aNewData) const; + mozilla::StyleInert mInert; mozilla::StyleUserInput mUserInput; mozilla::StyleUserModify mUserModify; // (modify-content) mozilla::StyleUserFocus mUserFocus; // (auto-select) diff --git a/layout/style/res/ua.css b/layout/style/res/ua.css index 1a20b3d8b562..a63ca0b964e5 100644 --- a/layout/style/res/ua.css +++ b/layout/style/res/ua.css @@ -153,6 +153,11 @@ outline: 1px dotted; } +/* Inert subtrees */ +*|*:-moz-inert { + -moz-inert: inert; +} + /* Miscellaneous */ *|*::-moz-cell-content { diff --git a/layout/style/test/ListCSSProperties.cpp b/layout/style/test/ListCSSProperties.cpp index e5ee5e737244..a9d45eba041b 100644 --- a/layout/style/test/ListCSSProperties.cpp +++ b/layout/style/test/ListCSSProperties.cpp @@ -94,6 +94,7 @@ const char* gInaccessibleProperties[] = { "-moz-context-properties", "-moz-control-character-visibility", "-moz-default-appearance", + "-moz-inert", "-moz-list-reversed", // parsed by UA sheets only "-moz-script-level", // parsed by UA sheets only "-moz-script-size-multiplier", diff --git a/layout/style/test/test_non_content_accessible_properties.html b/layout/style/test/test_non_content_accessible_properties.html index 4e1e2a6dce0a..7a6e0f5d4ff5 100644 --- a/layout/style/test/test_non_content_accessible_properties.html +++ b/layout/style/test/test_non_content_accessible_properties.html @@ -23,6 +23,7 @@ const NON_CONTENT_ACCESSIBLE_PROPERTIES = [ "-moz-min-font-size-ratio", "-moz-script-size-multiplier", "-moz-default-appearance", + "-moz-inert", // TODO(emilio): Whenever we stop using `-moz-binding` in a gazillion tests // we should add it here. ]; diff --git a/layout/style/test/test_selectors.html b/layout/style/test/test_selectors.html index 72486caeaee4..380756b63d38 100644 --- a/layout/style/test/test_selectors.html +++ b/layout/style/test/test_selectors.html @@ -1137,6 +1137,7 @@ function runTests() { test_unbalanced_unparseable(":-moz-handler-crashed"); // We're not in a UA sheet, so this should be invalid. + test_balanced_unparseable(":-moz-inert"); test_balanced_unparseable(":-moz-native-anonymous"); test_balanced_unparseable(":-moz-table-border-nonzero"); diff --git a/servo/components/style/element_state.rs b/servo/components/style/element_state.rs index 027ebc839b4f..da44186b44ba 100644 --- a/servo/components/style/element_state.rs +++ b/servo/components/style/element_state.rs @@ -145,6 +145,9 @@ bitflags! { /// /// https://html.spec.whatwg.org/multipage/#centered-alignment const IN_MODAL_DIALOG_STATE = 1 << 53; + + /// https://html.spec.whatwg.org/multipage/interaction.html#inert-subtrees + const IN_MOZINERT_STATE = 1 << 54; } } diff --git a/servo/components/style/gecko/non_ts_pseudo_class_list.rs b/servo/components/style/gecko/non_ts_pseudo_class_list.rs index 034ce0694ad7..bdd994c70955 100644 --- a/servo/components/style/gecko/non_ts_pseudo_class_list.rs +++ b/servo/components/style/gecko/non_ts_pseudo_class_list.rs @@ -48,6 +48,7 @@ macro_rules! apply_non_ts_list { ("-moz-drag-over", MozDragOver, IN_DRAGOVER_STATE, _), ("target", Target, IN_TARGET_STATE, _), ("indeterminate", Indeterminate, IN_INDETERMINATE_STATE, _), + ("-moz-inert", MozInert, IN_MOZINERT_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-devtools-highlighted", MozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("fullscreen", Fullscreen, IN_FULLSCREEN_STATE, _), diff --git a/servo/components/style/gecko/wrapper.rs b/servo/components/style/gecko/wrapper.rs index 0b2547edc46e..218642d304f9 100644 --- a/servo/components/style/gecko/wrapper.rs +++ b/servo/components/style/gecko/wrapper.rs @@ -2016,6 +2016,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { NonTSPseudoClass::Checked | NonTSPseudoClass::Fullscreen | NonTSPseudoClass::Indeterminate | + NonTSPseudoClass::MozInert | NonTSPseudoClass::PlaceholderShown | NonTSPseudoClass::Target | NonTSPseudoClass::Valid | diff --git a/servo/components/style/properties/longhands/inherited_ui.mako.rs b/servo/components/style/properties/longhands/inherited_ui.mako.rs index bbe342096471..cb48acaa1fe9 100644 --- a/servo/components/style/properties/longhands/inherited_ui.mako.rs +++ b/servo/components/style/properties/longhands/inherited_ui.mako.rs @@ -29,6 +29,17 @@ ${helpers.single_keyword( gecko_enum_prefix="StylePointerEvents", )} +${helpers.single_keyword( + "-moz-inert", + "none inert", + engines="gecko", + gecko_ffi_name="mInert", + gecko_enum_prefix="StyleInert", + animation_value_type="discrete", + enabled_in="ua", + spec="Nonstandard (https://html.spec.whatwg.org/multipage/interaction.html#inert-subtrees)", +)} + ${helpers.single_keyword( "-moz-user-input", "auto none", diff --git a/servo/components/style/servo/selector_parser.rs b/servo/components/style/servo/selector_parser.rs index a3a3ca7c2b86..3652eef0058a 100644 --- a/servo/components/style/servo/selector_parser.rs +++ b/servo/components/style/servo/selector_parser.rs @@ -347,6 +347,7 @@ impl ToCss for NonTSPseudoClass { Fullscreen => ":fullscreen", Hover => ":hover", Indeterminate => ":indeterminate", + MozInert => ":-moz-inert", Link => ":link", PlaceholderShown => ":placeholder-shown", ReadWrite => ":read-write", @@ -436,6 +437,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { "fullscreen" => Fullscreen, "hover" => Hover, "indeterminate" => Indeterminate, + "-moz-inert" => MozInert, "link" => Link, "placeholder-shown" => PlaceholderShown, "read-write" => ReadWrite, diff --git a/servo/components/style/style_adjuster.rs b/servo/components/style/style_adjuster.rs index 8ab4aea3d216..fb5fae96bd0d 100644 --- a/servo/components/style/style_adjuster.rs +++ b/servo/components/style/style_adjuster.rs @@ -153,6 +153,56 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { } } + /// https://html.spec.whatwg.org/multipage/interaction.html#inert-subtrees + /// + /// If -moz-inert is applied then add: + /// -moz-user-focus: none; + /// -moz-user-input: none; + /// -moz-user-modify: read-only; + /// user-select: none; + /// pointer-events: none; + /// cursor: default; + fn adjust_for_inert(&mut self) { + use properties::longhands::_moz_inert::computed_value::T as Inert; + use properties::longhands::_moz_user_focus::computed_value::T as UserFocus; + use properties::longhands::_moz_user_input::computed_value::T as UserInput; + use properties::longhands::_moz_user_modify::computed_value::T as UserModify; + use properties::longhands::pointer_events::computed_value::T as PointerEvents; + use properties::longhands::cursor::computed_value::T as Cursor; + use crate::values::specified::ui::CursorKind; + use crate::values::specified::ui::UserSelect; + + let needs_update = { + let ui = self.style.get_inherited_ui(); + if ui.clone__moz_inert() == Inert::None { + return; + } + + ui.clone__moz_user_focus() != UserFocus::None || + ui.clone__moz_user_input() != UserInput::None || + ui.clone__moz_user_modify() != UserModify::ReadOnly || + ui.clone_pointer_events() != PointerEvents::None || + ui.clone_cursor().keyword != CursorKind::Default || + ui.clone_cursor().images != Default::default() + }; + + if needs_update { + let ui = self.style.mutate_inherited_ui(); + ui.set__moz_user_focus(UserFocus::None); + ui.set__moz_user_input(UserInput::None); + ui.set__moz_user_modify(UserModify::ReadOnly); + ui.set_pointer_events(PointerEvents::None); + ui.set_cursor(Cursor { + images: Default::default(), + keyword: CursorKind::Default, + }); + } + + if self.style.get_ui().clone_user_select() != UserSelect::None { + self.style.mutate_ui().set_user_select(UserSelect::None); + } + } + /// Whether we should skip any item-based display property blockification on /// this element. fn skip_item_display_fixup(&self, element: Option) -> bool @@ -855,6 +905,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { #[cfg(feature = "gecko")] { self.adjust_for_appearance(element); + self.adjust_for_inert(); } self.set_bits(); }