--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -9585,36 +9585,48 @@ nsDocument::ForgetImagePreload(nsIURI* a
mPreloadingImages.Remove(aURI, getter_AddRefs(req));
if (req) {
// Make sure to cancel the request so imagelib knows it's gone.
req->CancelAndForgetObserver(NS_BINDING_ABORTED);
}
}
}
-EventStates
-nsDocument::GetDocumentState()
+void
+nsDocument::UpdatePossiblyStaleDocumentState()
{
if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) {
if (IsDocumentRightToLeft()) {
mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
}
mGotDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
}
if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
nsIPresShell* shell = GetShell();
if (shell && shell->GetPresContext() &&
shell->GetPresContext()->IsTopLevelWindowInactive()) {
mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
}
mGotDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
}
+}
+
+EventStates
+nsDocument::GetPossiblyStaleDocumentState() const
+{
return mDocumentState;
}
+EventStates
+nsDocument::GetDocumentState()
+{
+ UpdatePossiblyStaleDocumentState();
+ return GetPossiblyStaleDocumentState();
+}
+
namespace {
/**
* Stub for LoadSheet(), since all we want is to get the sheet into
* the CSSLoader's style cache
*/
class StubCSSLoaderObserver final : public nsICSSLoaderObserver {
~StubCSSLoaderObserver() {}
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -974,16 +974,18 @@ public:
const nsAString& aIntegrity) override;
virtual nsresult LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
RefPtr<mozilla::StyleSheet>* aSheet) override;
virtual nsISupports* GetCurrentContentSink() override;
virtual mozilla::EventStates GetDocumentState() override;
+ virtual mozilla::EventStates GetPossiblyStaleDocumentState() const override;
+ virtual void UpdatePossiblyStaleDocumentState() override;
// Only BlockOnload should call this!
void AsyncBlockOnload();
virtual void SetScrollToRef(nsIURI *aDocumentURI) override;
virtual void ScrollToRef() override;
virtual void ResetScrolledToRefAlready() override;
virtual void SetChangeScrollPosWhenScrollingToRef(bool aValue) override;
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2373,16 +2373,18 @@ public:
virtual int GetDocumentLWTheme() { return Doc_Theme_None; }
/**
* Returns the document state.
* Document state bits have the form NS_DOCUMENT_STATE_* and are declared in
* nsIDocument.h.
*/
virtual mozilla::EventStates GetDocumentState() = 0;
+ virtual mozilla::EventStates GetPossiblyStaleDocumentState() const = 0;
+ virtual void UpdatePossiblyStaleDocumentState() = 0;
virtual nsISupports* GetCurrentContentSink() = 0;
virtual void SetScrollToRef(nsIURI *aDocumentURI) = 0;
virtual void ScrollToRef() = 0;
virtual void ResetScrolledToRefAlready() = 0;
virtual void SetChangeScrollPosWhenScrollingToRef(bool aValue) = 0;
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -491,16 +491,31 @@ nscolor Gecko_GetLookAndFeelSystemColor(
{
bool useStandinsForNativeColors = aPresContext && !aPresContext->IsChrome();
nscolor result;
LookAndFeel::ColorID colorId = static_cast<LookAndFeel::ColorID>(aId);
LookAndFeel::GetColor(colorId, useStandinsForNativeColors, &result);
return result;
}
+bool
+Gecko_MatchStringArgPseudo(RawGeckoElementBorrowed aElement,
+ CSSPseudoClassType aType,
+ const char16_t* aIdent,
+ RawGeckoDocumentBorrowed aDocument,
+ bool* aSetSlowSelectorFlag)
+{
+ EventStates dummyMask; // mask is never read because we pass aDependence=nullptr
+ auto matches = nsCSSRuleProcessor::StringPseudoMatches(aElement, aType, aIdent,
+ aDocument, true,
+ dummyMask, nullptr, aSetSlowSelectorFlag);
+ MOZ_ASSERT(matches, "Servo should never try to match against a non-string-arg pseudo");
+ return *matches;
+}
+
template <typename Implementor>
static nsIAtom*
AtomAttrValue(Implementor* aElement, nsIAtom* aName)
{
const nsAttrValue* attr = aElement->GetParsedAttr(aName);
return attr ? attr->GetAtomValue() : nullptr;
}
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -371,16 +371,22 @@ void Gecko_nsStyleFont_CopyLangFrom(nsSt
const nsMediaFeature* Gecko_GetMediaFeatures();
// We use an int32_t here instead of a LookAndFeel::ColorID
// because forward-declaring a nested enum/struct is impossible
nscolor Gecko_GetLookAndFeelSystemColor(int32_t color_id,
RawGeckoPresContextBorrowed pres_context);
+bool Gecko_MatchStringArgPseudo(RawGeckoElementBorrowed element,
+ mozilla::CSSPseudoClassType type,
+ const char16_t* ident,
+ RawGeckoDocumentBorrowed document,
+ bool* set_slow_selector);
+
// Style-struct management.
#define STYLE_STRUCT(name, checkdata_cb) \
void Gecko_Construct_Default_nsStyle##name( \
nsStyle##name* ptr, \
RawGeckoPresContextBorrowed pres_context); \
void Gecko_CopyConstruct_nsStyle##name(nsStyle##name* ptr, \
const nsStyle##name* other); \
void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr);
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -1634,29 +1634,30 @@ StateSelectorMatches(Element* aElement,
return false;
}
}
return true;
}
/* static */
mozilla::Maybe<bool>
-nsCSSRuleProcessor::StringPseudoMatches(mozilla::dom::Element* aElement,
+nsCSSRuleProcessor::StringPseudoMatches(const mozilla::dom::Element* aElement,
CSSPseudoClassType aPseudo,
- char16_t* aString,
- nsIDocument* aDocument,
+ const char16_t* aString,
+ const nsIDocument* aDocument,
bool aForStyling,
EventStates aStateMask,
+ bool* aSetSlowSelectorFlag,
bool* const aDependence)
{
switch (aPseudo) {
case CSSPseudoClassType::mozLocaleDir:
{
bool docIsRTL =
- aDocument->GetDocumentState().
+ aDocument->GetPossiblyStaleDocumentState().
HasState(NS_DOCUMENT_STATE_RTL_LOCALE);
nsDependentString dirString(aString);
if (dirString.EqualsLiteral("rtl")) {
if (!docIsRTL) {
return Some(false);
}
@@ -1687,17 +1688,17 @@ nsCSSRuleProcessor::StringPseudoMatches(
int32_t index = -1;
if (aForStyling)
// FIXME: This isn't sufficient to handle:
// :-moz-empty-except-children-with-localname() + E
// :-moz-empty-except-children-with-localname() ~ E
// because we don't know to restyle the grandparent of the
// inserted/removed element (as in bug 534804 for :empty).
- aElement->SetFlags(NODE_HAS_SLOW_SELECTOR);
+ *aSetSlowSelectorFlag = true;
do {
child = aElement->GetChildAt(++index);
} while (child &&
(!IsSignificantChild(child, true, false) ||
(child->GetNameSpaceID() == aElement->GetNameSpaceID() &&
child->NodeInfo()->NameAtom()->Equals(nsDependentString(aString)))));
if (child != nullptr) {
return Some(false);
@@ -2136,23 +2137,29 @@ static bool SelectorMatches(Element* aEl
return false;
}
}
break;
default:
{
if (nsCSSPseudoClasses::HasStringArg(pseudoClass->mType)) {
+ bool setSlowSelectorFlag = false;
+ aTreeMatchContext.mDocument->UpdatePossiblyStaleDocumentState();
auto result = nsCSSRuleProcessor::StringPseudoMatches(aElement,
pseudoClass->mType,
pseudoClass->u.mString,
aTreeMatchContext.mDocument,
aTreeMatchContext.mForStyling,
aNodeMatchContext.mStateMask,
+ &setSlowSelectorFlag,
aDependence);
+ if (setSlowSelectorFlag) {
+ aElement->SetFlags(NODE_HAS_SLOW_SELECTOR);
+ }
// Did we find a pseudo successfully?
if (result) {
if (!*result) {
return false;
}
// else continue, we have a match!
} else {
--- a/layout/style/nsCSSRuleProcessor.h
+++ b/layout/style/nsCSSRuleProcessor.h
@@ -135,22 +135,23 @@ public:
TreeMatchContext& aTreeMatchContext);
/**
* Checks if a function-like ident-containing pseudo (:pseudo(ident))
* matches a given element.
*
* Returns Some(true) if it parses and matches, Some(false) if it
* parses but does not match, and Nothing() if it fails to parse.
*/
- static mozilla::Maybe<bool> StringPseudoMatches(mozilla::dom::Element* aElement,
+ static mozilla::Maybe<bool> StringPseudoMatches(const mozilla::dom::Element* aElement,
mozilla::CSSPseudoClassType aPseudo,
- char16_t* aString,
- nsIDocument* aDocument,
+ const char16_t* aString,
+ const nsIDocument* aDocument,
bool aForStyling,
mozilla::EventStates aStateMask,
+ bool* aSetSlowSelectorFlag,
bool* const aDependence = nullptr);
// static mozilla::Maybe<bool> StringPseudoMatches(mozilla::dom::Element* aElement,
// mozilla::CSSPseudoClassType aPseudo,
// char16_t* aString,
// TreeMatchContext& aTreeMatchContext);
// nsIStyleRuleProcessor
--- a/servo/components/style/gecko/non_ts_pseudo_class_list.rs
+++ b/servo/components/style/gecko/non_ts_pseudo_class_list.rs
@@ -47,12 +47,16 @@ macro_rules! apply_non_ts_list {
("read-write", ReadWrite, _, IN_READ_WRITE_STATE, _),
("read-only", ReadOnly, _, IN_READ_WRITE_STATE, _),
("-moz-browser-frame", MozBrowserFrame, mozBrowserFrame, _, PSEUDO_CLASS_INTERNAL),
("-moz-table-border-nonzero", MozTableBorderNonzero, mozTableBorderNonzero, _, PSEUDO_CLASS_INTERNAL),
],
string: [
("-moz-system-metric", MozSystemMetric, mozSystemMetric, _, PSEUDO_CLASS_INTERNAL),
+ ("-moz-locale-dir", MozLocaleDir, mozLocaleDir, _, PSEUDO_CLASS_INTERNAL),
+ ("-moz-empty-except-children-with-localname", MozEmptyExceptChildrenWithLocalname, mozEmptyExceptChildrenWithLocalname, _, PSEUDO_CLASS_INTERNAL),
+ ("dir", Dir, dir, _, _),
+ ("lang", Lang, lang, _, _),
]
}
}
}
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -145,32 +145,32 @@ macro_rules! pseudo_class_name {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum NonTSPseudoClass {
$(
#[doc = $css]
$name,
)*
$(
#[doc = $s_css]
- $s_name(Box<str>),
+ $s_name(Box<[u16]>),
)*
}
}
}
apply_non_ts_list!(pseudo_class_name);
impl ToCss for NonTSPseudoClass {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
macro_rules! pseudo_class_serialize {
(bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
match *self {
$(NonTSPseudoClass::$name => concat!(":", $css),)*
$(NonTSPseudoClass::$s_name(ref s) => {
- return dest.write_str(&format!(":{}({})", $s_css, s))
+ return dest.write_str(&format!(":{}({})", $s_css, String::from_utf16(&s).unwrap()))
}, )*
}
}
}
let ser = apply_non_ts_list!(pseudo_class_serialize);
dest.write_str(ser)
}
}
@@ -285,18 +285,21 @@ impl<'a> ::selectors::Parser for Selecto
name: Cow<str>,
parser: &mut Parser)
-> Result<NonTSPseudoClass, ()> {
macro_rules! pseudo_class_string_parse {
(bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
match_ignore_ascii_case! { &name,
$($s_css => {
- let name = String::from(parser.expect_ident_or_string()?).into_boxed_str();
- NonTSPseudoClass::$s_name(name)
+ let name = parser.expect_ident_or_string()?;
+ // convert to null terminated utf16 string
+ // since that's what Gecko deals with
+ let utf16: Vec<u16> = name.encode_utf16().chain(Some(0u16)).collect();
+ NonTSPseudoClass::$s_name(utf16.into_boxed_slice())
}, )*
_ => return Err(())
}
}
}
let pseudo_class = apply_non_ts_list!(pseudo_class_string_parse);
if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
Ok(pseudo_class)
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -29,16 +29,17 @@ use gecko_bindings::bindings::{Gecko_IsL
use gecko_bindings::bindings::{Gecko_IsUnvisitedLink, Gecko_IsVisitedLink, Gecko_Namespace};
use gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
use gecko_bindings::bindings::Gecko_ClassOrClassList;
use gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;
use gecko_bindings::bindings::Gecko_GetAnimationRule;
use gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock;
use gecko_bindings::bindings::Gecko_GetStyleAttrDeclarationBlock;
use gecko_bindings::bindings::Gecko_GetStyleContext;
+use gecko_bindings::bindings::Gecko_MatchStringArgPseudo;
use gecko_bindings::bindings::Gecko_UpdateAnimations;
use gecko_bindings::structs;
use gecko_bindings::structs::{RawGeckoElement, RawGeckoNode};
use gecko_bindings::structs::{nsIAtom, nsIContent, nsStyleContext};
use gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
use gecko_bindings::structs::NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
use gecko_bindings::sugar::ownership::HasArcFFI;
@@ -657,17 +658,34 @@ impl<'le> ::selectors::Element for Gecko
NonTSPseudoClass::ReadOnly => {
!self.get_state().contains(pseudo_class.state_flag())
}
NonTSPseudoClass::MozTableBorderNonzero |
NonTSPseudoClass::MozBrowserFrame => unsafe {
Gecko_MatchesElement(pseudo_class.to_gecko_pseudoclasstype().unwrap(), self.0)
},
- _ => false
+ NonTSPseudoClass::MozSystemMetric(ref s) |
+ NonTSPseudoClass::MozLocaleDir(ref s) |
+ NonTSPseudoClass::MozEmptyExceptChildrenWithLocalname(ref s) |
+ NonTSPseudoClass::Dir(ref s) |
+ NonTSPseudoClass::Lang(ref s) => {
+ use selectors::matching::HAS_SLOW_SELECTOR;
+ unsafe {
+ let mut set_slow_selector = false;
+ let matches = Gecko_MatchStringArgPseudo(self.0,
+ pseudo_class.to_gecko_pseudoclasstype().unwrap(),
+ s.as_ptr(),
+ self.as_node().owner_doc(), &mut set_slow_selector);
+ if set_slow_selector {
+ self.set_selector_flags(HAS_SLOW_SELECTOR);
+ }
+ matches
+ }
+ }
}
}
fn get_id(&self) -> Option<Atom> {
let ptr = unsafe {
bindings::Gecko_AtomAttrValue(self.0,
atom!("id").as_ptr())
};
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -982,16 +982,23 @@ extern "C" {
}
extern "C" {
pub fn Gecko_GetLookAndFeelSystemColor(color_id: i32,
pres_context:
RawGeckoPresContextBorrowed)
-> nscolor;
}
extern "C" {
+ pub fn Gecko_MatchStringArgPseudo(element: RawGeckoElementBorrowed,
+ type_: CSSPseudoClassType,
+ ident: *const u16,
+ document: RawGeckoDocumentBorrowed,
+ set_slow_selector: *mut bool) -> bool;
+}
+extern "C" {
pub fn Gecko_Construct_Default_nsStyleFont(ptr: *mut nsStyleFont,
pres_context:
RawGeckoPresContextBorrowed);
}
extern "C" {
pub fn Gecko_CopyConstruct_nsStyleFont(ptr: *mut nsStyleFont,
other: *const nsStyleFont);
}