mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 11:57:32 +09:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
commit
3298c8907d
21 changed files with 619 additions and 124 deletions
|
|
@ -9,6 +9,7 @@
|
||||||
#include "mozilla/dom/DocumentFragment.h"
|
#include "mozilla/dom/DocumentFragment.h"
|
||||||
#include "ChildIterator.h"
|
#include "ChildIterator.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
|
#include "nsLayoutUtils.h"
|
||||||
#include "nsDOMClassInfoID.h"
|
#include "nsDOMClassInfoID.h"
|
||||||
#include "nsIDOMHTMLElement.h"
|
#include "nsIDOMHTMLElement.h"
|
||||||
#include "nsIStyleSheetLinkingElement.h"
|
#include "nsIStyleSheetLinkingElement.h"
|
||||||
|
|
@ -162,6 +163,19 @@ ShadowRoot::AddSlot(HTMLSlotElement* aSlot)
|
||||||
|
|
||||||
oldSlot->RemoveAssignedNode(assignedNode);
|
oldSlot->RemoveAssignedNode(assignedNode);
|
||||||
currentSlot->AppendAssignedNode(assignedNode);
|
currentSlot->AppendAssignedNode(assignedNode);
|
||||||
|
|
||||||
|
Element* restyleElement;
|
||||||
|
if (assignedNode->IsElement()) {
|
||||||
|
restyleElement = assignedNode->AsElement();
|
||||||
|
} else {
|
||||||
|
// This is likely a text node. Use the host instead.
|
||||||
|
restyleElement = GetHost();
|
||||||
|
}
|
||||||
|
if (restyleElement) {
|
||||||
|
nsLayoutUtils::PostRestyleEvent(
|
||||||
|
restyleElement, eRestyle_Subtree, nsChangeHint(0));
|
||||||
|
}
|
||||||
|
|
||||||
doEnqueueSlotChange = true;
|
doEnqueueSlotChange = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p id="display"></p>
|
<p id="display"></p>
|
||||||
|
<p><input type="text" id="input"></p>
|
||||||
|
<p><input type="text" id="input_readonly" readonly></p>
|
||||||
|
<p><textarea id="textarea"></textarea></p>
|
||||||
<div id="content" style="display: none">
|
<div id="content" style="display: none">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -293,10 +296,163 @@ function testSynthesizedKeyLocation()
|
||||||
window.removeEventListener("keyup", handler, true);
|
window.removeEventListener("keyup", handler, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We're using TextEventDispatcher to decide if we should keypress event
|
||||||
|
// on content in the default event group. So, we can test if keypress
|
||||||
|
// event is NOT fired unexpectedly with synthesizeKey().
|
||||||
|
function testEnterKeyPressEvent()
|
||||||
|
{
|
||||||
|
let keydownFired, keypressFired, beforeinputFired;
|
||||||
|
function onEvent(aEvent) {
|
||||||
|
switch (aEvent.type) {
|
||||||
|
case "keydown":
|
||||||
|
keydownFired = true;
|
||||||
|
return;
|
||||||
|
case "keypress":
|
||||||
|
keypressFired = true;
|
||||||
|
return;
|
||||||
|
case "beforeinput":
|
||||||
|
beforeinputFired = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let targetId of ["input", "textarea", "input_readonly"]) {
|
||||||
|
let target = document.getElementById(targetId);
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
keydownFired = keypressFired = beforeinputFired = false;
|
||||||
|
target.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
target.addEventListener("keydown", onEvent);
|
||||||
|
target.addEventListener("keypress", onEvent);
|
||||||
|
target.addEventListener("beforeinput", onEvent);
|
||||||
|
|
||||||
|
const kDescription = "<" + targetId.replace("_", " ") + ">: ";
|
||||||
|
let isEditable = kDescription.includes("readonly");
|
||||||
|
let isTextarea = kDescription.includes("textarea");
|
||||||
|
|
||||||
|
target.focus();
|
||||||
|
|
||||||
|
reset();
|
||||||
|
synthesizeKey("KEY_Enter", {});
|
||||||
|
is(keydownFired, true,
|
||||||
|
kDescription + "keydown event should be fired when Enter key is pressed");
|
||||||
|
is(keypressFired, true,
|
||||||
|
kDescription + "keypress event should be fired when Enter key is pressed");
|
||||||
|
if (isEditable) {
|
||||||
|
todo_is(beforeinputFired, true,
|
||||||
|
kDescription + "beforeinput event should be fired when Enter key is pressed");
|
||||||
|
} else {
|
||||||
|
is(beforeinputFired, false,
|
||||||
|
kDescription + "beforeinput event shouldn't be fired when Enter key is pressed");
|
||||||
|
}
|
||||||
|
if (isTextarea) {
|
||||||
|
is(target.value, "\n",
|
||||||
|
kDescription + "Enter key should cause inputting a line break in <textarea>");
|
||||||
|
} else {
|
||||||
|
is(target.value, "",
|
||||||
|
kDescription + "Enter key should not cause inputting a line break");
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
|
synthesizeKey("KEY_Enter", {shiftKey: true});
|
||||||
|
is(keydownFired, true,
|
||||||
|
kDescription + "keydown event should be fired when Shift + Enter key is pressed");
|
||||||
|
is(keypressFired, true,
|
||||||
|
kDescription + "keypress event should be fired when Shift + Enter key is pressed");
|
||||||
|
if (isEditable) {
|
||||||
|
todo_is(beforeinputFired, true,
|
||||||
|
kDescription + "beforeinput event should be fired when Shift + Enter key is pressed");
|
||||||
|
} else {
|
||||||
|
is(beforeinputFired, false,
|
||||||
|
kDescription + "beforeinput event shouldn't be fired when Shift + Enter key is pressed");
|
||||||
|
}
|
||||||
|
if (isTextarea) {
|
||||||
|
is(target.value, "\n",
|
||||||
|
kDescription + "Shift + Enter key should cause inputting a line break in <textarea>");
|
||||||
|
} else {
|
||||||
|
is(target.value, "",
|
||||||
|
kDescription + "Shift + Enter key should not cause inputting a line break");
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
|
synthesizeKey("KEY_Enter", {ctrlKey: true});
|
||||||
|
is(keydownFired, true,
|
||||||
|
kDescription + "keydown event should be fired when Ctrl + Enter key is pressed");
|
||||||
|
is(keypressFired, true,
|
||||||
|
kDescription + "keypress event should be fired when Ctrl + Enter key is pressed");
|
||||||
|
is(beforeinputFired, false,
|
||||||
|
kDescription + "beforeinput event shouldn't be fired when Ctrl + Enter key is pressed");
|
||||||
|
is(target.value, "",
|
||||||
|
kDescription + "Ctrl + Enter key should not cause inputting a line break");
|
||||||
|
|
||||||
|
reset();
|
||||||
|
synthesizeKey("KEY_Enter", {altKey: true});
|
||||||
|
is(keydownFired, true,
|
||||||
|
kDescription + "keydown event should be fired when Alt + Enter key is pressed");
|
||||||
|
is(keypressFired, !kStrictKeyPressEvents,
|
||||||
|
kDescription + "keypress event shouldn't be fired when Alt + Enter key is pressed in strict keypress dispatching mode");
|
||||||
|
is(beforeinputFired, false,
|
||||||
|
kDescription + "beforeinput event shouldn't be fired when Alt + Enter key is pressed");
|
||||||
|
is(target.value, "",
|
||||||
|
kDescription + "Alt + Enter key should not cause inputting a line break");
|
||||||
|
|
||||||
|
reset();
|
||||||
|
synthesizeKey("KEY_Enter", {metaKey: true});
|
||||||
|
is(keydownFired, true,
|
||||||
|
kDescription + "keydown event should be fired when Meta + Enter key is pressed");
|
||||||
|
is(keypressFired, !kStrictKeyPressEvents,
|
||||||
|
kDescription + "keypress event shouldn't be fired when Meta + Enter key is pressed in strict keypress dispatching mode");
|
||||||
|
is(beforeinputFired, false,
|
||||||
|
kDescription + "beforeinput event shouldn't be fired when Meta + Enter key is pressed");
|
||||||
|
is(target.value, "",
|
||||||
|
kDescription + "Meta + Enter key should not cause inputting a line break");
|
||||||
|
|
||||||
|
reset();
|
||||||
|
synthesizeKey("KEY_Enter", {shiftKey: true, ctrlKey: true});
|
||||||
|
is(keydownFired, true,
|
||||||
|
kDescription + "keydown event should be fired when Ctrl + Shift + Enter key is pressed");
|
||||||
|
is(keypressFired, !kStrictKeyPressEvents,
|
||||||
|
kDescription + "keypress event shouldn't be fired when Ctrl + Shift + Enter key is pressed in strict keypress dispatching mode");
|
||||||
|
is(beforeinputFired, false,
|
||||||
|
kDescription + "beforeinput event shouldn't be fired when Ctrl + Shift + Enter key is pressed");
|
||||||
|
is(target.value, "",
|
||||||
|
kDescription + "Ctrl + Shift + Enter key should not cause inputting a line break");
|
||||||
|
|
||||||
|
reset();
|
||||||
|
synthesizeKey("KEY_Enter", {shiftKey: true, altKey: true});
|
||||||
|
is(keydownFired, true,
|
||||||
|
kDescription + "keydown event should be fired when Alt + Shift + Enter key is pressed");
|
||||||
|
is(keypressFired, !kStrictKeyPressEvents,
|
||||||
|
kDescription + "keypress event shouldn't be fired when Alt + Shift + Enter key is pressed in strict keypress dispatching mode");
|
||||||
|
is(beforeinputFired, false,
|
||||||
|
kDescription + "beforeinput event shouldn't be fired when Alt + Shift + Enter key is pressed");
|
||||||
|
is(target.value, "",
|
||||||
|
kDescription + "Alt + Shift + Enter key should not cause inputting a line break");
|
||||||
|
|
||||||
|
reset();
|
||||||
|
synthesizeKey("KEY_Enter", {shiftKey: true, metaKey: true});
|
||||||
|
is(keydownFired, true,
|
||||||
|
kDescription + "keydown event should be fired when Meta + Shift + Enter key is pressed");
|
||||||
|
is(keypressFired, !kStrictKeyPressEvents,
|
||||||
|
kDescription + "keypress event shouldn't be fired when Meta + Shift + Enter key is pressed in strict keypress dispatching mode");
|
||||||
|
is(beforeinputFired, false,
|
||||||
|
kDescription + "beforeinput event shouldn't be fired when Meta + Shift + Enter key is pressed");
|
||||||
|
is(target.value, "",
|
||||||
|
kDescription + "Meta + Shift + Enter key should not cause inputting a line break");
|
||||||
|
|
||||||
|
target.removeEventListener("keydown", onEvent);
|
||||||
|
target.removeEventListener("keypress", onEvent);
|
||||||
|
target.removeEventListener("beforeinput", onEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function runTests()
|
function runTests()
|
||||||
{
|
{
|
||||||
testInitializingUntrustedEvent();
|
testInitializingUntrustedEvent();
|
||||||
testSynthesizedKeyLocation();
|
testSynthesizedKeyLocation();
|
||||||
|
testEnterKeyPressEvent();
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,13 @@ HTMLSlotElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nsIContent* parent = GetParent()) {
|
||||||
|
if (parent->IsElement()) {
|
||||||
|
nsLayoutUtils::PostRestyleEvent(
|
||||||
|
parent->AsElement(), eRestyle_Subtree, nsChangeHint(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
|
return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
|
||||||
aOldValue, aNotify);
|
aOldValue, aNotify);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -681,6 +681,47 @@ nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||||
aData->mElementIsFeatureless = false;
|
aData->mElementIsFeatureless = false;
|
||||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = false;
|
aData->mTreeMatchContext.mOnlyMatchHostPseudo = false;
|
||||||
|
|
||||||
|
// Walk the rules in shadow root for ::slotted() pseudo-element rules
|
||||||
|
// if we have an assigned slot.
|
||||||
|
if (aData->mElement->GetAssignedSlot()) {
|
||||||
|
aData->mTreeMatchContext.mRestrictToSlottedPseudo = true;
|
||||||
|
|
||||||
|
AutoTArray<nsXBLBinding*, 2> stack;
|
||||||
|
bool foundTopmostScope = false;
|
||||||
|
for (nsIContent* parent = aData->mElement->GetFlattenedTreeParent();
|
||||||
|
parent;
|
||||||
|
parent = parent->GetFlattenedTreeParent()) {
|
||||||
|
ShadowRoot* currentShadow = parent->GetShadowRoot();
|
||||||
|
if (!currentShadow) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsXBLBinding* binding = currentShadow->GetAssociatedBinding();
|
||||||
|
if (!binding) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
stack.AppendElement(binding);
|
||||||
|
|
||||||
|
if (!foundTopmostScope) {
|
||||||
|
aData->mTreeMatchContext.mScopedRoot = parent;
|
||||||
|
foundTopmostScope = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!stack.IsEmpty()) {
|
||||||
|
uint32_t index = stack.Length() - 1;
|
||||||
|
nsXBLBinding* binding = stack.ElementAt(index);
|
||||||
|
stack.RemoveElementAt(index);
|
||||||
|
|
||||||
|
aData->mTreeMatchContext.mForAssignedSlot = true;
|
||||||
|
aData->mTreeMatchContext.mIsTopmostScope = (index == 0);
|
||||||
|
binding->WalkRules(aFunc, aData);
|
||||||
|
}
|
||||||
|
aData->mTreeMatchContext.mForAssignedSlot = false;
|
||||||
|
|
||||||
|
aData->mTreeMatchContext.mRestrictToSlottedPseudo = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Walk the binding scope chain, starting with the binding attached to our
|
// Walk the binding scope chain, starting with the binding attached to our
|
||||||
// content, up till we run out of scopes or we get cut off.
|
// content, up till we run out of scopes or we get cut off.
|
||||||
nsIContent *content = aData->mElement;
|
nsIContent *content = aData->mElement;
|
||||||
|
|
|
||||||
|
|
@ -3839,8 +3839,13 @@ RestyleManager::ComputeAndProcessStyleChange(nsStyleContext* aNewContext,
|
||||||
MOZ_ASSERT(mReframingStyleContexts, "should have rsc");
|
MOZ_ASSERT(mReframingStyleContexts, "should have rsc");
|
||||||
MOZ_ASSERT(aNewContext->StyleDisplay()->mDisplay == StyleDisplay::Contents);
|
MOZ_ASSERT(aNewContext->StyleDisplay()->mDisplay == StyleDisplay::Contents);
|
||||||
nsIFrame* frame = GetNearestAncestorFrame(aElement);
|
nsIFrame* frame = GetNearestAncestorFrame(aElement);
|
||||||
MOZ_ASSERT(frame, "display:contents node in map although it's a "
|
// Return early if we don't have a frame.
|
||||||
"display:none descendant?");
|
if (!frame) {
|
||||||
|
NS_ASSERTION(frame,
|
||||||
|
"display:contents node in map although it's a "
|
||||||
|
"display:none descendant?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
TreeMatchContext treeMatchContext(true,
|
TreeMatchContext treeMatchContext(true,
|
||||||
nsRuleWalker::eRelevantLinkUnvisited,
|
nsRuleWalker::eRelevantLinkUnvisited,
|
||||||
frame->PresContext()->Document());
|
frame->PresContext()->Document());
|
||||||
|
|
|
||||||
|
|
@ -318,7 +318,8 @@ nsCSSSelector::nsCSSSelector(void)
|
||||||
mNext(nullptr),
|
mNext(nullptr),
|
||||||
mNameSpace(kNameSpaceID_Unknown),
|
mNameSpace(kNameSpaceID_Unknown),
|
||||||
mOperator(0),
|
mOperator(0),
|
||||||
mPseudoType(CSSPseudoElementType::NotPseudo)
|
mPseudoType(CSSPseudoElementType::NotPseudo),
|
||||||
|
mHybridPseudoType(CSSPseudoElementType::NotPseudo)
|
||||||
{
|
{
|
||||||
MOZ_COUNT_CTOR(nsCSSSelector);
|
MOZ_COUNT_CTOR(nsCSSSelector);
|
||||||
}
|
}
|
||||||
|
|
@ -915,6 +916,11 @@ nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations
|
||||||
|
|
||||||
// Append each pseudo-class in the linked list
|
// Append each pseudo-class in the linked list
|
||||||
for (nsPseudoClassList* list = mPseudoClassList; list; list = list->mNext) {
|
for (nsPseudoClassList* list = mPseudoClassList; list; list = list->mNext) {
|
||||||
|
// Serialize pseudo-elements that were treated as if they were a
|
||||||
|
// pseudo-class to the two colon syntax.
|
||||||
|
if (nsCSSPseudoClasses::IsHybridPseudoElement(list->mType)) {
|
||||||
|
aString.Append(char16_t(':'));
|
||||||
|
}
|
||||||
nsCSSPseudoClasses::PseudoTypeToString(list->mType, temp);
|
nsCSSPseudoClasses::PseudoTypeToString(list->mType, temp);
|
||||||
// This should not be escaped since (a) the pseudo-class string
|
// This should not be escaped since (a) the pseudo-class string
|
||||||
// has a ":" that can't be escaped and (b) all pseudo-classes at
|
// has a ":" that can't be escaped and (b) all pseudo-classes at
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,10 @@ public:
|
||||||
return mLowercaseTag && !mCasedTag;
|
return mLowercaseTag && !mCasedTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool IsHybridPseudoElement() const {
|
||||||
|
return HybridPseudoType() != mozilla::CSSPseudoElementType::NotPseudo;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate the specificity of this selector (not including its mNext!).
|
// Calculate the specificity of this selector (not including its mNext!).
|
||||||
int32_t CalcWeight() const;
|
int32_t CalcWeight() const;
|
||||||
|
|
||||||
|
|
@ -218,6 +222,12 @@ public:
|
||||||
void SetPseudoType(mozilla::CSSPseudoElementType aType) {
|
void SetPseudoType(mozilla::CSSPseudoElementType aType) {
|
||||||
mPseudoType = aType;
|
mPseudoType = aType;
|
||||||
}
|
}
|
||||||
|
mozilla::CSSPseudoElementType HybridPseudoType() const {
|
||||||
|
return mHybridPseudoType;
|
||||||
|
}
|
||||||
|
void SetHybridPseudoType(mozilla::CSSPseudoElementType aType) {
|
||||||
|
mHybridPseudoType = aType;
|
||||||
|
}
|
||||||
|
|
||||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||||
|
|
||||||
|
|
@ -241,6 +251,7 @@ private:
|
||||||
// The underlying type of CSSPseudoElementType is uint8_t and
|
// The underlying type of CSSPseudoElementType is uint8_t and
|
||||||
// it packs well with mOperator. (char16_t + uint8_t is less than 32bits.)
|
// it packs well with mOperator. (char16_t + uint8_t is less than 32bits.)
|
||||||
mozilla::CSSPseudoElementType mPseudoType;
|
mozilla::CSSPseudoElementType mPseudoType;
|
||||||
|
mozilla::CSSPseudoElementType mHybridPseudoType;
|
||||||
|
|
||||||
nsCSSSelector(const nsCSSSelector& aCopy) = delete;
|
nsCSSSelector(const nsCSSSelector& aCopy) = delete;
|
||||||
nsCSSSelector& operator=(const nsCSSSelector& aCopy) = delete;
|
nsCSSSelector& operator=(const nsCSSSelector& aCopy) = delete;
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,8 @@ enum class SelectorParsingFlags {
|
||||||
eIsForgiving = 1 << 1,
|
eIsForgiving = 1 << 1,
|
||||||
eDisallowCombinators = 1 << 2,
|
eDisallowCombinators = 1 << 2,
|
||||||
eDisallowPseudoElements = 1 << 3,
|
eDisallowPseudoElements = 1 << 3,
|
||||||
eInheritNamespace = 1 << 4
|
eInheritNamespace = 1 << 4,
|
||||||
|
eForceEmptyList = 1 << 5
|
||||||
};
|
};
|
||||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SelectorParsingFlags)
|
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SelectorParsingFlags)
|
||||||
|
|
||||||
|
|
@ -776,7 +777,7 @@ protected:
|
||||||
// aFlags is not set.
|
// aFlags is not set.
|
||||||
nsSelectorParsingStatus ParsePseudoSelector(int32_t& aDataMask,
|
nsSelectorParsingStatus ParsePseudoSelector(int32_t& aDataMask,
|
||||||
nsCSSSelector& aSelector,
|
nsCSSSelector& aSelector,
|
||||||
SelectorParsingFlags aFlags,
|
SelectorParsingFlags& aFlags,
|
||||||
nsIAtom** aPseudoElement,
|
nsIAtom** aPseudoElement,
|
||||||
nsAtomList** aPseudoElementArgs,
|
nsAtomList** aPseudoElementArgs,
|
||||||
CSSPseudoElementType* aPseudoElementType);
|
CSSPseudoElementType* aPseudoElementType);
|
||||||
|
|
@ -784,9 +785,9 @@ protected:
|
||||||
nsSelectorParsingStatus ParseAttributeSelector(int32_t& aDataMask,
|
nsSelectorParsingStatus ParseAttributeSelector(int32_t& aDataMask,
|
||||||
nsCSSSelector& aSelector);
|
nsCSSSelector& aSelector);
|
||||||
|
|
||||||
nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t& aDataMask,
|
nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t& aDataMask,
|
||||||
nsCSSSelector& aSelector,
|
nsCSSSelector& aSelector,
|
||||||
SelectorParsingFlags aFlags);
|
SelectorParsingFlags& aFlags);
|
||||||
|
|
||||||
nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
|
nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
|
||||||
CSSPseudoClassType aType);
|
CSSPseudoClassType aType);
|
||||||
|
|
@ -796,22 +797,22 @@ protected:
|
||||||
|
|
||||||
nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
|
nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
|
||||||
CSSPseudoClassType aType,
|
CSSPseudoClassType aType,
|
||||||
SelectorParsingFlags aFlags);
|
SelectorParsingFlags& aFlags);
|
||||||
|
|
||||||
nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t& aDataMask,
|
nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t& aDataMask,
|
||||||
nsCSSSelector& aSelector,
|
nsCSSSelector& aSelector,
|
||||||
SelectorParsingFlags aFlags);
|
SelectorParsingFlags& aFlags);
|
||||||
|
|
||||||
// If aStopChar is non-zero, the selector list is done when we hit
|
// If aStopChar is non-zero, the selector list is done when we hit
|
||||||
// aStopChar. Otherwise, it's done when we hit EOF.
|
// aStopChar. Otherwise, it's done when we hit EOF.
|
||||||
bool ParseSelectorList(nsCSSSelectorList*& aListHead,
|
bool ParseSelectorList(nsCSSSelectorList*& aListHead,
|
||||||
char16_t aStopChar,
|
char16_t aStopChar,
|
||||||
SelectorParsingFlags aFlags = SelectorParsingFlags::eNone);
|
SelectorParsingFlags& aFlags);
|
||||||
bool ParseSelectorGroup(nsCSSSelectorList*& aListHead,
|
bool ParseSelectorGroup(nsCSSSelectorList*& aListHead,
|
||||||
SelectorParsingFlags aFlags);
|
SelectorParsingFlags& aFlags);
|
||||||
bool ParseSelector(nsCSSSelectorList* aList,
|
bool ParseSelector(nsCSSSelectorList* aList,
|
||||||
char16_t aPrevCombinator,
|
char16_t aPrevCombinator,
|
||||||
SelectorParsingFlags aFlags);
|
SelectorParsingFlags& aFlags);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
eParseDeclaration_InBraces = 1 << 0,
|
eParseDeclaration_InBraces = 1 << 0,
|
||||||
|
|
@ -2351,7 +2352,8 @@ CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
|
||||||
css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
|
css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
|
||||||
InitScanner(scanner, reporter, aURI, aURI, nullptr);
|
InitScanner(scanner, reporter, aURI, aURI, nullptr);
|
||||||
|
|
||||||
bool success = ParseSelectorList(*aSelectorList, char16_t(0));
|
SelectorParsingFlags flags = SelectorParsingFlags::eNone;
|
||||||
|
bool success = ParseSelectorList(*aSelectorList, char16_t(0), flags);
|
||||||
|
|
||||||
// We deliberately do not call OUTPUT_ERROR here, because all our
|
// We deliberately do not call OUTPUT_ERROR here, because all our
|
||||||
// callers map a failure return to a JS exception, and if that JS
|
// callers map a failure return to a JS exception, and if that JS
|
||||||
|
|
@ -5465,9 +5467,10 @@ CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData,
|
||||||
{
|
{
|
||||||
// First get the list of selectors for the rule
|
// First get the list of selectors for the rule
|
||||||
nsCSSSelectorList* slist = nullptr;
|
nsCSSSelectorList* slist = nullptr;
|
||||||
|
SelectorParsingFlags flags = SelectorParsingFlags::eNone;
|
||||||
uint32_t linenum, colnum;
|
uint32_t linenum, colnum;
|
||||||
if (!GetNextTokenLocation(true, &linenum, &colnum) ||
|
if (!GetNextTokenLocation(true, &linenum, &colnum) ||
|
||||||
!ParseSelectorList(slist, char16_t('{'))) {
|
!ParseSelectorList(slist, char16_t('{'), flags)) {
|
||||||
REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
|
REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
|
||||||
OUTPUT_ERROR();
|
OUTPUT_ERROR();
|
||||||
SkipRuleSet(aInsideBraces);
|
SkipRuleSet(aInsideBraces);
|
||||||
|
|
@ -5504,7 +5507,7 @@ CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData,
|
||||||
bool
|
bool
|
||||||
CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
|
CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
|
||||||
char16_t aStopChar,
|
char16_t aStopChar,
|
||||||
SelectorParsingFlags aFlags)
|
SelectorParsingFlags& aFlags)
|
||||||
{
|
{
|
||||||
nsCSSSelectorList* list = nullptr;
|
nsCSSSelectorList* list = nullptr;
|
||||||
if (! ParseSelectorGroup(list, aFlags)) {
|
if (! ParseSelectorGroup(list, aFlags)) {
|
||||||
|
|
@ -5588,7 +5591,7 @@ static bool IsUniversalSelector(const nsCSSSelector& aSelector)
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList,
|
CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList,
|
||||||
SelectorParsingFlags aFlags)
|
SelectorParsingFlags& aFlags)
|
||||||
{
|
{
|
||||||
char16_t combinator = 0;
|
char16_t combinator = 0;
|
||||||
nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList());
|
nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList());
|
||||||
|
|
@ -5689,9 +5692,9 @@ CSSParserImpl::ParseClassSelector(int32_t& aDataMask,
|
||||||
// namespace|type or namespace|* or *|* or *
|
// namespace|type or namespace|* or *|* or *
|
||||||
//
|
//
|
||||||
CSSParserImpl::nsSelectorParsingStatus
|
CSSParserImpl::nsSelectorParsingStatus
|
||||||
CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask,
|
CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask,
|
||||||
nsCSSSelector& aSelector,
|
nsCSSSelector& aSelector,
|
||||||
SelectorParsingFlags aFlags)
|
SelectorParsingFlags& aFlags)
|
||||||
{
|
{
|
||||||
nsAutoString buffer;
|
nsAutoString buffer;
|
||||||
if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
|
if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
|
||||||
|
|
@ -6056,7 +6059,7 @@ CSSParserImpl::ParseAttributeSelector(int32_t& aDataMask,
|
||||||
CSSParserImpl::nsSelectorParsingStatus
|
CSSParserImpl::nsSelectorParsingStatus
|
||||||
CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
||||||
nsCSSSelector& aSelector,
|
nsCSSSelector& aSelector,
|
||||||
SelectorParsingFlags aFlags,
|
SelectorParsingFlags& aFlags,
|
||||||
nsIAtom** aPseudoElement,
|
nsIAtom** aPseudoElement,
|
||||||
nsAtomList** aPseudoElementArgs,
|
nsAtomList** aPseudoElementArgs,
|
||||||
CSSPseudoElementType* aPseudoElementType)
|
CSSPseudoElementType* aPseudoElementType)
|
||||||
|
|
@ -6103,10 +6106,14 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
||||||
CSSEnabledState enabledState = EnabledState();
|
CSSEnabledState enabledState = EnabledState();
|
||||||
CSSPseudoElementType pseudoElementType =
|
CSSPseudoElementType pseudoElementType =
|
||||||
nsCSSPseudoElements::GetPseudoType(pseudo, enabledState);
|
nsCSSPseudoElements::GetPseudoType(pseudo, enabledState);
|
||||||
|
bool pseudoElementIsTreeAbiding =
|
||||||
|
nsCSSPseudoElements::IsTreeAbidingPseudoElement(pseudoElementType);
|
||||||
CSSPseudoClassType pseudoClassType =
|
CSSPseudoClassType pseudoClassType =
|
||||||
nsCSSPseudoClasses::GetPseudoType(pseudo, enabledState);
|
nsCSSPseudoClasses::GetPseudoType(pseudo, enabledState);
|
||||||
bool pseudoClassIsUserAction =
|
bool pseudoClassIsUserAction =
|
||||||
nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType);
|
nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType);
|
||||||
|
bool pseudoClassHasForgivingSelectorListArg =
|
||||||
|
nsCSSPseudoClasses::HasForgivingSelectorListArg(pseudoClassType);
|
||||||
|
|
||||||
if (nsCSSAnonBoxes::IsNonElement(pseudo)) {
|
if (nsCSSAnonBoxes::IsNonElement(pseudo)) {
|
||||||
// Non-element anonymous boxes should not match any rule.
|
// Non-element anonymous boxes should not match any rule.
|
||||||
|
|
@ -6127,6 +6134,23 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We handle certain pseudo-elements as if they were a pseudo-class.
|
||||||
|
// Our platform does not have the mechanism to handle multiple
|
||||||
|
// pseudo-elements and proper storage if they have an argument.
|
||||||
|
CSSPseudoElementType hybridPseudoElementType =
|
||||||
|
CSSPseudoElementType::NotPseudo;
|
||||||
|
if (parsingPseudoElement &&
|
||||||
|
nsCSSPseudoElements::IsHybridPseudoElement(pseudoElementType)) {
|
||||||
|
hybridPseudoElementType = pseudoElementType;
|
||||||
|
pseudoElementType = CSSPseudoElementType::NotPseudo;
|
||||||
|
parsingPseudoElement = false;
|
||||||
|
|
||||||
|
if (hybridPseudoElementType == CSSPseudoElementType::slotted) {
|
||||||
|
pseudoClassType = CSSPseudoClassType::slotted;
|
||||||
|
aFlags |= SelectorParsingFlags::eDisallowCombinators;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MOZ_XUL
|
#ifdef MOZ_XUL
|
||||||
isTreePseudo = (pseudoElementType == CSSPseudoElementType::XULTree);
|
isTreePseudo = (pseudoElementType == CSSPseudoElementType::XULTree);
|
||||||
// If a tree pseudo-element is using the function syntax, it will
|
// If a tree pseudo-element is using the function syntax, it will
|
||||||
|
|
@ -6186,33 +6210,74 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
||||||
return eSelectorParsingStatus_Error;
|
return eSelectorParsingStatus_Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aSelector.IsPseudoElement()) {
|
bool forceEmptyList = false;
|
||||||
CSSPseudoElementType type = aSelector.PseudoType();
|
if (aSelector.IsPseudoElement() || aSelector.IsHybridPseudoElement()) {
|
||||||
|
CSSPseudoElementType type = aSelector.IsPseudoElement() ?
|
||||||
|
aSelector.PseudoType() :
|
||||||
|
aSelector.HybridPseudoType();
|
||||||
|
bool supportsTreeAbiding =
|
||||||
|
nsCSSPseudoElements::PseudoElementSupportsTreeAbiding(type);
|
||||||
|
bool supportsUserAction =
|
||||||
|
nsCSSPseudoElements::PseudoElementSupportsUserActionState(type);
|
||||||
if (type >= CSSPseudoElementType::Count ||
|
if (type >= CSSPseudoElementType::Count ||
|
||||||
!nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
|
(!supportsTreeAbiding && !supportsUserAction)) {
|
||||||
// We only allow user action pseudo-classes on certain pseudo-elements.
|
// We only allow user action pseudo-classes and/or tree-abiding
|
||||||
|
// pseudo-elements on certain pseudo-elements.
|
||||||
REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
|
REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
|
||||||
UngetToken();
|
UngetToken();
|
||||||
return eSelectorParsingStatus_Error;
|
return eSelectorParsingStatus_Error;
|
||||||
}
|
}
|
||||||
if (!isPseudoClass || !pseudoClassIsUserAction) {
|
|
||||||
// CSS 4 Selectors says that pseudo-elements can only be followed by
|
if (isPseudoClass) {
|
||||||
// a user action pseudo-class.
|
if (pseudoClassHasForgivingSelectorListArg) {
|
||||||
|
// XXX: Pseudo-classes with a forgiving selector list argument are
|
||||||
|
// allowed to follow a pseudo-element, but must treat any selector
|
||||||
|
// that is not of the same type as invalid. It doesn't make any
|
||||||
|
// sense, but that's the behavior of other tainted browsers.
|
||||||
|
forceEmptyList = true;
|
||||||
|
} else if (!supportsUserAction || !pseudoClassIsUserAction) {
|
||||||
|
// CSS 4 Selectors says that pseudo-elements can only be followed by
|
||||||
|
// a user action pseudo-class.
|
||||||
|
REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
|
||||||
|
UngetToken();
|
||||||
|
return eSelectorParsingStatus_Error;
|
||||||
|
}
|
||||||
|
} else if (isPseudoElement &&
|
||||||
|
(!supportsTreeAbiding || !pseudoElementIsTreeAbiding)) {
|
||||||
REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
|
REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
|
||||||
UngetToken();
|
UngetToken();
|
||||||
return eSelectorParsingStatus_Error;
|
return eSelectorParsingStatus_Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool disallowPseudoElements =
|
||||||
|
!!(aFlags & SelectorParsingFlags::eDisallowPseudoElements);
|
||||||
if (!parsingPseudoElement && isPseudoClass) {
|
if (!parsingPseudoElement && isPseudoClass) {
|
||||||
aDataMask |= SEL_MASK_PCLASS;
|
aDataMask |= SEL_MASK_PCLASS;
|
||||||
|
|
||||||
|
// Only pseudo-classes with a forgiving selector list argument
|
||||||
|
// are allowed if we're forced to be empty.
|
||||||
|
if ((aFlags & SelectorParsingFlags::eForceEmptyList) &&
|
||||||
|
!pseudoClassHasForgivingSelectorListArg) {
|
||||||
|
if (eCSSToken_Function == mToken.mType) {
|
||||||
|
SkipUntil(')');
|
||||||
|
}
|
||||||
|
return eSelectorParsingStatus_Continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (eCSSToken_Function == mToken.mType) {
|
if (eCSSToken_Function == mToken.mType) {
|
||||||
nsSelectorParsingStatus parsingStatus;
|
nsSelectorParsingStatus parsingStatus;
|
||||||
// Only the combinators restriction should be passed down the chain.
|
|
||||||
SelectorParsingFlags flags =
|
// Pass only a few parsing flags down the chain.
|
||||||
(aFlags & SelectorParsingFlags::eDisallowCombinators) ?
|
SelectorParsingFlags flags = SelectorParsingFlags::eNone;
|
||||||
SelectorParsingFlags::eDisallowCombinators :
|
if (aFlags & SelectorParsingFlags::eDisallowCombinators) {
|
||||||
SelectorParsingFlags::eNone;
|
flags |= SelectorParsingFlags::eDisallowCombinators;
|
||||||
|
}
|
||||||
|
if (aFlags & SelectorParsingFlags::eForceEmptyList ||
|
||||||
|
forceEmptyList) {
|
||||||
|
flags |= SelectorParsingFlags::eForceEmptyList;
|
||||||
|
}
|
||||||
|
|
||||||
if (sLegacyNegationPseudoClassEnabled &&
|
if (sLegacyNegationPseudoClassEnabled &&
|
||||||
CSSPseudoClassType::negation == pseudoClassType) {
|
CSSPseudoClassType::negation == pseudoClassType) {
|
||||||
// :not() can't be itself negated
|
// :not() can't be itself negated
|
||||||
|
|
@ -6228,6 +6293,13 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
||||||
return parsingStatus;
|
return parsingStatus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (nsCSSPseudoClasses::IsHybridPseudoElement(pseudoClassType) &&
|
||||||
|
hybridPseudoElementType == CSSPseudoElementType::NotPseudo) {
|
||||||
|
// Reject the single colon syntax for hybrid pseudo-elements.
|
||||||
|
REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
|
||||||
|
UngetToken();
|
||||||
|
return eSelectorParsingStatus_Error;
|
||||||
|
}
|
||||||
else if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
|
else if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
|
||||||
parsingStatus =
|
parsingStatus =
|
||||||
ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
|
ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
|
||||||
|
|
@ -6239,6 +6311,14 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
||||||
else {
|
else {
|
||||||
MOZ_ASSERT(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
|
MOZ_ASSERT(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
|
||||||
"unexpected pseudo with function token");
|
"unexpected pseudo with function token");
|
||||||
|
if (hybridPseudoElementType != CSSPseudoElementType::NotPseudo) {
|
||||||
|
aSelector.SetHybridPseudoType(hybridPseudoElementType);
|
||||||
|
// Ensure hybrid pseudo-elements are rejected if they're not allowed.
|
||||||
|
if (disallowPseudoElements) {
|
||||||
|
UngetToken();
|
||||||
|
return eSelectorParsingStatus_Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
|
parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
|
||||||
pseudoClassType,
|
pseudoClassType,
|
||||||
flags);
|
flags);
|
||||||
|
|
@ -6265,7 +6345,7 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
||||||
}
|
}
|
||||||
// Pseudo-elements might not be allowed from appearing
|
// Pseudo-elements might not be allowed from appearing
|
||||||
// (e.g. as an argument to the functional part of a pseudo-class).
|
// (e.g. as an argument to the functional part of a pseudo-class).
|
||||||
if (aFlags & SelectorParsingFlags::eDisallowPseudoElements) {
|
if (disallowPseudoElements) {
|
||||||
UngetToken();
|
UngetToken();
|
||||||
return eSelectorParsingStatus_Error;
|
return eSelectorParsingStatus_Error;
|
||||||
}
|
}
|
||||||
|
|
@ -6344,9 +6424,9 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
||||||
// Parse the argument of a negation pseudo-class :not()
|
// Parse the argument of a negation pseudo-class :not()
|
||||||
//
|
//
|
||||||
CSSParserImpl::nsSelectorParsingStatus
|
CSSParserImpl::nsSelectorParsingStatus
|
||||||
CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask,
|
CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask,
|
||||||
nsCSSSelector& aSelector,
|
nsCSSSelector& aSelector,
|
||||||
SelectorParsingFlags aFlags)
|
SelectorParsingFlags& aFlags)
|
||||||
{
|
{
|
||||||
aFlags |= SelectorParsingFlags::eIsNegated;
|
aFlags |= SelectorParsingFlags::eIsNegated;
|
||||||
|
|
||||||
|
|
@ -6624,7 +6704,7 @@ CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
|
||||||
CSSParserImpl::nsSelectorParsingStatus
|
CSSParserImpl::nsSelectorParsingStatus
|
||||||
CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
|
CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
|
||||||
CSSPseudoClassType aType,
|
CSSPseudoClassType aType,
|
||||||
SelectorParsingFlags aFlags)
|
SelectorParsingFlags& aFlags)
|
||||||
{
|
{
|
||||||
bool isSingleSelector =
|
bool isSingleSelector =
|
||||||
nsCSSPseudoClasses::HasSingleSelectorArg(aType);
|
nsCSSPseudoClasses::HasSingleSelectorArg(aType);
|
||||||
|
|
@ -6692,7 +6772,7 @@ CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
|
||||||
bool
|
bool
|
||||||
CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
|
CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
|
||||||
char16_t aPrevCombinator,
|
char16_t aPrevCombinator,
|
||||||
SelectorParsingFlags aFlags)
|
SelectorParsingFlags& aFlags)
|
||||||
{
|
{
|
||||||
if (! GetToken(true)) {
|
if (! GetToken(true)) {
|
||||||
REPORT_UNEXPECTED_EOF(PESelectorEOF);
|
REPORT_UNEXPECTED_EOF(PESelectorEOF);
|
||||||
|
|
@ -6727,7 +6807,8 @@ CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
|
||||||
selector->mClassList = pseudoElementArgs.forget();
|
selector->mClassList = pseudoElementArgs.forget();
|
||||||
selector->SetPseudoType(pseudoElementType);
|
selector->SetPseudoType(pseudoElementType);
|
||||||
}
|
}
|
||||||
} else if (selector->IsPseudoElement()) {
|
} else if (selector->IsPseudoElement() ||
|
||||||
|
selector->IsHybridPseudoElement()) {
|
||||||
// Once we parsed a pseudo-element, we can only parse
|
// Once we parsed a pseudo-element, we can only parse
|
||||||
// pseudo-classes (and only a limited set, which
|
// pseudo-classes (and only a limited set, which
|
||||||
// ParsePseudoSelector knows how to handle).
|
// ParsePseudoSelector knows how to handle).
|
||||||
|
|
@ -6761,6 +6842,14 @@ CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Treat every other selector as invalid.
|
||||||
|
if ((aFlags & SelectorParsingFlags::eForceEmptyList) &&
|
||||||
|
(selector->mIDList || selector->mClassList ||
|
||||||
|
selector->mAttrList || selector->mNegations ||
|
||||||
|
!selector->mPseudoClassList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (parsingStatus == eSelectorParsingStatus_Error) {
|
if (parsingStatus == eSelectorParsingStatus_Error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,9 @@ CSS_PSEUDO_CLASS(nthLastChild, ":nth-last-child", 0, "")
|
||||||
CSS_PSEUDO_CLASS(nthOfType, ":nth-of-type", 0, "")
|
CSS_PSEUDO_CLASS(nthOfType, ":nth-of-type", 0, "")
|
||||||
CSS_PSEUDO_CLASS(nthLastOfType, ":nth-last-of-type", 0, "")
|
CSS_PSEUDO_CLASS(nthLastOfType, ":nth-last-of-type", 0, "")
|
||||||
|
|
||||||
|
// Match slot nodes.
|
||||||
|
CSS_PSEUDO_CLASS(slotted, ":slotted", 0, "layout.css.slotted-pseudo.enabled")
|
||||||
|
|
||||||
// Match nodes that are HTML but not XHTML
|
// Match nodes that are HTML but not XHTML
|
||||||
CSS_PSEUDO_CLASS(mozIsHTML, ":-moz-is-html", 0, "")
|
CSS_PSEUDO_CLASS(mozIsHTML, ":-moz-is-html", 0, "")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,8 @@ bool
|
||||||
nsCSSPseudoClasses::HasSingleSelectorArg(Type aType)
|
nsCSSPseudoClasses::HasSingleSelectorArg(Type aType)
|
||||||
{
|
{
|
||||||
return aType == Type::host ||
|
return aType == Type::host ||
|
||||||
aType == Type::hostContext;
|
aType == Type::hostContext ||
|
||||||
|
aType == Type::slotted;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -126,7 +127,8 @@ nsCSSPseudoClasses::HasSelectorListArg(Type aType)
|
||||||
aType == Type::mozAny ||
|
aType == Type::mozAny ||
|
||||||
aType == Type::mozAnyPrivate ||
|
aType == Type::mozAnyPrivate ||
|
||||||
aType == Type::host ||
|
aType == Type::host ||
|
||||||
aType == Type::hostContext;
|
aType == Type::hostContext ||
|
||||||
|
aType == Type::slotted;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -169,3 +171,9 @@ nsCSSPseudoClasses::IsUserActionPseudoClass(Type aType)
|
||||||
aType == Type::active ||
|
aType == Type::active ||
|
||||||
aType == Type::focus;
|
aType == Type::focus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
nsCSSPseudoClasses::IsHybridPseudoElement(Type aType)
|
||||||
|
{
|
||||||
|
return aType == Type::slotted;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ public:
|
||||||
static bool HasOptionalSelectorListArg(Type aType);
|
static bool HasOptionalSelectorListArg(Type aType);
|
||||||
static bool IsHiddenFromSerialization(Type aType);
|
static bool IsHiddenFromSerialization(Type aType);
|
||||||
static bool IsUserActionPseudoClass(Type aType);
|
static bool IsUserActionPseudoClass(Type aType);
|
||||||
|
static bool IsHybridPseudoElement(Type aType);
|
||||||
|
|
||||||
// Should only be used on types other than Count and NotPseudoClass
|
// Should only be used on types other than Count and NotPseudoClass
|
||||||
static void PseudoTypeToString(Type aType, nsAString& aString);
|
static void PseudoTypeToString(Type aType, nsAString& aString);
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,11 @@
|
||||||
CSS_PSEUDO_ELEMENT(after, ":after", CSS_PSEUDO_ELEMENT_IS_CSS2)
|
CSS_PSEUDO_ELEMENT(after, ":after", CSS_PSEUDO_ELEMENT_IS_CSS2)
|
||||||
CSS_PSEUDO_ELEMENT(before, ":before", CSS_PSEUDO_ELEMENT_IS_CSS2)
|
CSS_PSEUDO_ELEMENT(before, ":before", CSS_PSEUDO_ELEMENT_IS_CSS2)
|
||||||
|
|
||||||
|
// XXX: ::slotted() is treated as if it were a pseudo-class, and
|
||||||
|
// is never parsed as a pseudo-element.
|
||||||
|
CSS_PSEUDO_ELEMENT(slotted, ":slotted",
|
||||||
|
CSS_PSEUDO_ELEMENT_SUPPORTS_TREE_ABIDING)
|
||||||
|
|
||||||
CSS_PSEUDO_ELEMENT(backdrop, ":backdrop", 0)
|
CSS_PSEUDO_ELEMENT(backdrop, ":backdrop", 0)
|
||||||
|
|
||||||
CSS_PSEUDO_ELEMENT(firstLetter, ":first-letter",
|
CSS_PSEUDO_ELEMENT(firstLetter, ":first-letter",
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,22 @@ nsCSSPseudoElements::IsCSS2PseudoElement(nsIAtom *aAtom)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
nsCSSPseudoElements::IsHybridPseudoElement(CSSPseudoElementType aType)
|
||||||
|
{
|
||||||
|
return aType == CSSPseudoElementType::slotted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
nsCSSPseudoElements::IsTreeAbidingPseudoElement(CSSPseudoElementType aType)
|
||||||
|
{
|
||||||
|
// TODO: ::marker should be added here once we have support for it.
|
||||||
|
return aType == CSSPseudoElementType::after ||
|
||||||
|
aType == CSSPseudoElementType::before ||
|
||||||
|
aType == CSSPseudoElementType::mozPlaceholder ||
|
||||||
|
aType == CSSPseudoElementType::placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
/* static */ CSSPseudoElementType
|
/* static */ CSSPseudoElementType
|
||||||
nsCSSPseudoElements::GetPseudoType(nsIAtom *aAtom, EnabledState aEnabledState)
|
nsCSSPseudoElements::GetPseudoType(nsIAtom *aAtom, EnabledState aEnabledState)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,10 @@
|
||||||
// API for creating pseudo-implementing native anonymous content in JS with this
|
// API for creating pseudo-implementing native anonymous content in JS with this
|
||||||
// pseudo-element?
|
// pseudo-element?
|
||||||
#define CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC (1<<5)
|
#define CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC (1<<5)
|
||||||
|
// Is this pseudo-element a pseudo-element that supports a tree-abiding
|
||||||
|
// pseudo-element following it, such as ::after or ::before? See
|
||||||
|
// https://w3c.github.io/csswg-drafts/css-pseudo-4/#tree-abiding.
|
||||||
|
#define CSS_PSEUDO_ELEMENT_SUPPORTS_TREE_ABIDING (1<<6)
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
|
@ -80,6 +84,10 @@ public:
|
||||||
|
|
||||||
static bool IsCSS2PseudoElement(nsIAtom *aAtom);
|
static bool IsCSS2PseudoElement(nsIAtom *aAtom);
|
||||||
|
|
||||||
|
static bool IsHybridPseudoElement(Type aType);
|
||||||
|
|
||||||
|
static bool IsTreeAbidingPseudoElement(Type aType);
|
||||||
|
|
||||||
#define CSS_PSEUDO_ELEMENT(_name, _value, _flags) \
|
#define CSS_PSEUDO_ELEMENT(_name, _value, _flags) \
|
||||||
static nsICSSPseudoElement* _name;
|
static nsICSSPseudoElement* _name;
|
||||||
#include "nsCSSPseudoElementList.h"
|
#include "nsCSSPseudoElementList.h"
|
||||||
|
|
@ -107,6 +115,12 @@ public:
|
||||||
return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC);
|
return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool PseudoElementSupportsTreeAbiding(const Type aType)
|
||||||
|
{
|
||||||
|
return PseudoElementHasFlags(aType,
|
||||||
|
CSS_PSEUDO_ELEMENT_SUPPORTS_TREE_ABIDING);
|
||||||
|
}
|
||||||
|
|
||||||
static bool IsEnabled(Type aType, EnabledState aEnabledState)
|
static bool IsEnabled(Type aType, EnabledState aEnabledState)
|
||||||
{
|
{
|
||||||
return !PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) ||
|
return !PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) ||
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
#include "nsCSSRules.h"
|
#include "nsCSSRules.h"
|
||||||
#include "nsStyleSet.h"
|
#include "nsStyleSet.h"
|
||||||
#include "mozilla/dom/Element.h"
|
#include "mozilla/dom/Element.h"
|
||||||
|
#include "mozilla/dom/HTMLSlotElement.h"
|
||||||
#include "mozilla/dom/ShadowRoot.h"
|
#include "mozilla/dom/ShadowRoot.h"
|
||||||
#include "nsNthIndexCache.h"
|
#include "nsNthIndexCache.h"
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
|
|
@ -684,6 +685,7 @@ void RuleHash::EnumerateAllRules(Element* aElement, ElementDependentRuleProcesso
|
||||||
filter->AssertHasAllAncestors(aElement);
|
filter->AssertHasAllAncestors(aElement);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
bool isForAssignedSlot = aData->mTreeMatchContext.mForAssignedSlot;
|
||||||
// Merge the lists while there are still multiple lists to merge.
|
// Merge the lists while there are still multiple lists to merge.
|
||||||
while (valueCount > 1) {
|
while (valueCount > 1) {
|
||||||
int32_t valueIndex = 0;
|
int32_t valueIndex = 0;
|
||||||
|
|
@ -696,6 +698,7 @@ void RuleHash::EnumerateAllRules(Element* aElement, ElementDependentRuleProcesso
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const RuleValue *cur = mEnumList[valueIndex].mCurValue;
|
const RuleValue *cur = mEnumList[valueIndex].mCurValue;
|
||||||
|
aData->mTreeMatchContext.mForAssignedSlot = isForAssignedSlot;
|
||||||
ContentEnumFunc(*cur, cur->mSelector, aData, aNodeContext, filter);
|
ContentEnumFunc(*cur, cur->mSelector, aData, aNodeContext, filter);
|
||||||
cur++;
|
cur++;
|
||||||
if (cur == mEnumList[valueIndex].mEnd) {
|
if (cur == mEnumList[valueIndex].mEnd) {
|
||||||
|
|
@ -709,6 +712,7 @@ void RuleHash::EnumerateAllRules(Element* aElement, ElementDependentRuleProcesso
|
||||||
for (const RuleValue *value = mEnumList[0].mCurValue,
|
for (const RuleValue *value = mEnumList[0].mCurValue,
|
||||||
*end = mEnumList[0].mEnd;
|
*end = mEnumList[0].mEnd;
|
||||||
value != end; ++value) {
|
value != end; ++value) {
|
||||||
|
aData->mTreeMatchContext.mForAssignedSlot = isForAssignedSlot;
|
||||||
ContentEnumFunc(*value, value->mSelector, aData, aNodeContext, filter);
|
ContentEnumFunc(*value, value->mSelector, aData, aNodeContext, filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1380,8 +1384,9 @@ enum class SelectorMatchesFlags : uint8_t {
|
||||||
// pseudo-element.
|
// pseudo-element.
|
||||||
IS_PSEUDO_CLASS_ARGUMENT = 1 << 2,
|
IS_PSEUDO_CLASS_ARGUMENT = 1 << 2,
|
||||||
|
|
||||||
// The selector should be blocked from matching the :host pseudo-class.
|
// The selector should be blocked from matching because it is called
|
||||||
IS_HOST_INACCESSIBLE = 1 << 3
|
// from outside the shadow tree.
|
||||||
|
IS_OUTSIDE_SHADOW_TREE = 1 << 3
|
||||||
};
|
};
|
||||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SelectorMatchesFlags)
|
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SelectorMatchesFlags)
|
||||||
|
|
||||||
|
|
@ -1393,13 +1398,14 @@ static inline bool ActiveHoverQuirkMatches(nsCSSSelector* aSelector,
|
||||||
if (aSelector->HasTagSelector() || aSelector->mAttrList ||
|
if (aSelector->HasTagSelector() || aSelector->mAttrList ||
|
||||||
aSelector->mIDList || aSelector->mClassList ||
|
aSelector->mIDList || aSelector->mClassList ||
|
||||||
aSelector->IsPseudoElement() ||
|
aSelector->IsPseudoElement() ||
|
||||||
|
aSelector->IsHybridPseudoElement() ||
|
||||||
// Having this quirk means that some selectors will no longer match,
|
// Having this quirk means that some selectors will no longer match,
|
||||||
// so it's better to return false when we aren't sure (i.e., the
|
// so it's better to return false when we aren't sure (i.e., the
|
||||||
// flags are unknown).
|
// flags are unknown).
|
||||||
aSelectorFlags & (SelectorMatchesFlags::UNKNOWN |
|
aSelectorFlags & (SelectorMatchesFlags::UNKNOWN |
|
||||||
SelectorMatchesFlags::HAS_PSEUDO_ELEMENT |
|
SelectorMatchesFlags::HAS_PSEUDO_ELEMENT |
|
||||||
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT |
|
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT |
|
||||||
SelectorMatchesFlags::IS_HOST_INACCESSIBLE)) {
|
SelectorMatchesFlags::IS_OUTSIDE_SHADOW_TREE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1725,24 +1731,29 @@ static bool SelectorMatches(Element* aElement,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Element* targetElement = aElement;
|
||||||
|
if (aTreeMatchContext.mForAssignedSlot) {
|
||||||
|
targetElement = aElement->GetAssignedSlot()->AsElement();
|
||||||
|
}
|
||||||
|
|
||||||
// namespace/tag match
|
// namespace/tag match
|
||||||
// optimization : bail out early if we can
|
// optimization : bail out early if we can
|
||||||
if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
|
if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
|
||||||
aElement->GetNameSpaceID() != aSelector->mNameSpace))
|
targetElement->GetNameSpaceID() != aSelector->mNameSpace))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (aSelector->mLowercaseTag) {
|
if (aSelector->mLowercaseTag) {
|
||||||
nsIAtom* selectorTag =
|
nsIAtom* selectorTag =
|
||||||
(aTreeMatchContext.mIsHTMLDocument && aElement->IsHTMLElement()) ?
|
(aTreeMatchContext.mIsHTMLDocument && targetElement->IsHTMLElement()) ?
|
||||||
aSelector->mLowercaseTag : aSelector->mCasedTag;
|
aSelector->mLowercaseTag : aSelector->mCasedTag;
|
||||||
if (selectorTag != aElement->NodeInfo()->NameAtom()) {
|
if (selectorTag != targetElement->NodeInfo()->NameAtom()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAtomList* IDList = aSelector->mIDList;
|
nsAtomList* IDList = aSelector->mIDList;
|
||||||
if (IDList) {
|
if (IDList) {
|
||||||
nsIAtom* id = aElement->GetID();
|
nsIAtom* id = targetElement->GetID();
|
||||||
if (id) {
|
if (id) {
|
||||||
// case sensitivity: bug 93371
|
// case sensitivity: bug 93371
|
||||||
const bool isCaseSensitive =
|
const bool isCaseSensitive =
|
||||||
|
|
@ -1777,7 +1788,7 @@ static bool SelectorMatches(Element* aElement,
|
||||||
nsAtomList* classList = aSelector->mClassList;
|
nsAtomList* classList = aSelector->mClassList;
|
||||||
if (classList) {
|
if (classList) {
|
||||||
// test for class match
|
// test for class match
|
||||||
const nsAttrValue *elementClasses = aElement->GetClasses();
|
const nsAttrValue *elementClasses = targetElement->GetClasses();
|
||||||
if (!elementClasses) {
|
if (!elementClasses) {
|
||||||
// Element has no classes but we have a class selector
|
// Element has no classes but we have a class selector
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1797,6 +1808,8 @@ static bool SelectorMatches(Element* aElement,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool isOutsideShadowTree =
|
||||||
|
!!(aSelectorFlags & SelectorMatchesFlags::IS_OUTSIDE_SHADOW_TREE);
|
||||||
const bool isNegated = (aDependence != nullptr);
|
const bool isNegated = (aDependence != nullptr);
|
||||||
// The selectors for which we set node bits are, unfortunately, early
|
// The selectors for which we set node bits are, unfortunately, early
|
||||||
// in this function (because they're pseudo-classes, which are
|
// in this function (because they're pseudo-classes, which are
|
||||||
|
|
@ -1963,6 +1976,38 @@ static bool SelectorMatches(Element* aElement,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CSSPseudoClassType::slotted:
|
||||||
|
{
|
||||||
|
if (aTreeMatchContext.mForAssignedSlot) {
|
||||||
|
aTreeMatchContext.mForAssignedSlot = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slottables cannot be matched from the outer tree.
|
||||||
|
if (isOutsideShadowTree) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slot elements cannot be matched.
|
||||||
|
if (aElement->IsHTMLElement(nsGkAtoms::slot)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The current element must have an assigned slot.
|
||||||
|
if (!aElement->GetAssignedSlot()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeMatchContext nodeContext(EventStates(),
|
||||||
|
aNodeMatchContext.mIsRelevantLink);
|
||||||
|
if (!SelectorListMatches(aElement,
|
||||||
|
pseudoClass,
|
||||||
|
nodeContext,
|
||||||
|
aTreeMatchContext)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case CSSPseudoClassType::host:
|
case CSSPseudoClassType::host:
|
||||||
{
|
{
|
||||||
ShadowRoot* shadow = aElement->GetShadowRoot();
|
ShadowRoot* shadow = aElement->GetShadowRoot();
|
||||||
|
|
@ -1973,7 +2018,7 @@ static bool SelectorMatches(Element* aElement,
|
||||||
// style).
|
// style).
|
||||||
if (!shadow ||
|
if (!shadow ||
|
||||||
aSelector->HasFeatureSelectors() ||
|
aSelector->HasFeatureSelectors() ||
|
||||||
aSelectorFlags & SelectorMatchesFlags::IS_HOST_INACCESSIBLE) {
|
isOutsideShadowTree) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2338,7 +2383,7 @@ static bool SelectorMatches(Element* aElement,
|
||||||
bool result = true;
|
bool result = true;
|
||||||
if (aSelector->mAttrList) {
|
if (aSelector->mAttrList) {
|
||||||
// test for attribute match
|
// test for attribute match
|
||||||
if (!aElement->HasAttrs()) {
|
if (!targetElement->HasAttrs()) {
|
||||||
// if no attributes on the content, no match
|
// if no attributes on the content, no match
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2348,7 +2393,7 @@ static bool SelectorMatches(Element* aElement,
|
||||||
|
|
||||||
do {
|
do {
|
||||||
bool isHTML =
|
bool isHTML =
|
||||||
(aTreeMatchContext.mIsHTMLDocument && aElement->IsHTMLElement());
|
(aTreeMatchContext.mIsHTMLDocument && targetElement->IsHTMLElement());
|
||||||
matchAttribute = isHTML ? attr->mLowercaseAttr : attr->mCasedAttr;
|
matchAttribute = isHTML ? attr->mLowercaseAttr : attr->mCasedAttr;
|
||||||
if (attr->mNameSpace == kNameSpaceID_Unknown) {
|
if (attr->mNameSpace == kNameSpaceID_Unknown) {
|
||||||
// Attr selector with a wildcard namespace. We have to examine all
|
// Attr selector with a wildcard namespace. We have to examine all
|
||||||
|
|
@ -2360,7 +2405,7 @@ static bool SelectorMatches(Element* aElement,
|
||||||
// actually has attributes in), short-circuiting if we ever match.
|
// actually has attributes in), short-circuiting if we ever match.
|
||||||
result = false;
|
result = false;
|
||||||
const nsAttrName* attrName;
|
const nsAttrName* attrName;
|
||||||
for (uint32_t i = 0; (attrName = aElement->GetAttrNameAt(i)); ++i) {
|
for (uint32_t i = 0; (attrName = targetElement->GetAttrNameAt(i)); ++i) {
|
||||||
if (attrName->LocalName() != matchAttribute) {
|
if (attrName->LocalName() != matchAttribute) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -2371,7 +2416,7 @@ static bool SelectorMatches(Element* aElement,
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
bool hasAttr =
|
bool hasAttr =
|
||||||
#endif
|
#endif
|
||||||
aElement->GetAttr(attrName->NamespaceID(),
|
targetElement->GetAttr(attrName->NamespaceID(),
|
||||||
attrName->LocalName(), value);
|
attrName->LocalName(), value);
|
||||||
NS_ASSERTION(hasAttr, "GetAttrNameAt lied");
|
NS_ASSERTION(hasAttr, "GetAttrNameAt lied");
|
||||||
result = AttrMatchesValue(attr, value, isHTML);
|
result = AttrMatchesValue(attr, value, isHTML);
|
||||||
|
|
@ -2389,12 +2434,12 @@ static bool SelectorMatches(Element* aElement,
|
||||||
}
|
}
|
||||||
else if (attr->mFunction == NS_ATTR_FUNC_EQUALS) {
|
else if (attr->mFunction == NS_ATTR_FUNC_EQUALS) {
|
||||||
result =
|
result =
|
||||||
aElement->
|
targetElement->
|
||||||
AttrValueIs(attr->mNameSpace, matchAttribute, attr->mValue,
|
AttrValueIs(attr->mNameSpace, matchAttribute, attr->mValue,
|
||||||
attr->IsValueCaseSensitive(isHTML) ? eCaseMatters
|
attr->IsValueCaseSensitive(isHTML) ? eCaseMatters
|
||||||
: eIgnoreCase);
|
: eIgnoreCase);
|
||||||
}
|
}
|
||||||
else if (!aElement->HasAttr(attr->mNameSpace, matchAttribute)) {
|
else if (!targetElement->HasAttr(attr->mNameSpace, matchAttribute)) {
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
else if (attr->mFunction != NS_ATTR_FUNC_SET) {
|
else if (attr->mFunction != NS_ATTR_FUNC_SET) {
|
||||||
|
|
@ -2402,7 +2447,7 @@ static bool SelectorMatches(Element* aElement,
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
bool hasAttr =
|
bool hasAttr =
|
||||||
#endif
|
#endif
|
||||||
aElement->GetAttr(attr->mNameSpace, matchAttribute, value);
|
targetElement->GetAttr(attr->mNameSpace, matchAttribute, value);
|
||||||
NS_ASSERTION(hasAttr, "HasAttr lied");
|
NS_ASSERTION(hasAttr, "HasAttr lied");
|
||||||
result = AttrMatchesValue(attr, value, isHTML);
|
result = AttrMatchesValue(attr, value, isHTML);
|
||||||
}
|
}
|
||||||
|
|
@ -2417,7 +2462,7 @@ static bool SelectorMatches(Element* aElement,
|
||||||
for (nsCSSSelector *negation = aSelector->mNegations;
|
for (nsCSSSelector *negation = aSelector->mNegations;
|
||||||
result && negation; negation = negation->mNegations) {
|
result && negation; negation = negation->mNegations) {
|
||||||
bool dependence = false;
|
bool dependence = false;
|
||||||
result = !SelectorMatches(aElement, negation, aNodeMatchContext,
|
result = !SelectorMatches(targetElement, negation, aNodeMatchContext,
|
||||||
aTreeMatchContext,
|
aTreeMatchContext,
|
||||||
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT,
|
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT,
|
||||||
&dependence);
|
&dependence);
|
||||||
|
|
@ -2542,6 +2587,11 @@ SelectorMatchesTree(Element* aPrevElement,
|
||||||
// The relevant link must be an ancestor of the node being matched.
|
// The relevant link must be an ancestor of the node being matched.
|
||||||
aFlags = SelectorMatchesTreeFlags(aFlags & ~eLookForRelevantLink);
|
aFlags = SelectorMatchesTreeFlags(aFlags & ~eLookForRelevantLink);
|
||||||
nsIContent* parent = prevElement->GetParent();
|
nsIContent* parent = prevElement->GetParent();
|
||||||
|
// Operate on the flattened element tree when matching the
|
||||||
|
// ::slotted() pseudo-element.
|
||||||
|
if (aTreeMatchContext.mRestrictToSlottedPseudo) {
|
||||||
|
parent = prevElement->GetFlattenedTreeParent();
|
||||||
|
}
|
||||||
if (parent) {
|
if (parent) {
|
||||||
if (aTreeMatchContext.mForStyling)
|
if (aTreeMatchContext.mForStyling)
|
||||||
parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
|
parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
|
||||||
|
|
@ -2552,7 +2602,12 @@ SelectorMatchesTree(Element* aPrevElement,
|
||||||
// for descendant combinators and child combinators, the element
|
// for descendant combinators and child combinators, the element
|
||||||
// to test against is the parent
|
// to test against is the parent
|
||||||
else {
|
else {
|
||||||
nsIContent *content = prevElement->GetParent();
|
nsIContent* content = prevElement->GetParent();
|
||||||
|
// Operate on the flattened element tree when matching the
|
||||||
|
// ::slotted() pseudo-element.
|
||||||
|
if (aTreeMatchContext.mRestrictToSlottedPseudo) {
|
||||||
|
content = prevElement->GetFlattenedTreeParent();
|
||||||
|
}
|
||||||
|
|
||||||
// In the shadow tree, the shadow host behaves as if it
|
// In the shadow tree, the shadow host behaves as if it
|
||||||
// is a featureless parent of top-level elements of the shadow
|
// is a featureless parent of top-level elements of the shadow
|
||||||
|
|
@ -2560,11 +2615,12 @@ SelectorMatchesTree(Element* aPrevElement,
|
||||||
// left most selector because ancestors of the host are not in
|
// left most selector because ancestors of the host are not in
|
||||||
// the selector match list.
|
// the selector match list.
|
||||||
ShadowRoot* shadowRoot = content ?
|
ShadowRoot* shadowRoot = content ?
|
||||||
ShadowRoot::FromNode(content) : nullptr;
|
ShadowRoot::FromNode(content) :
|
||||||
|
nullptr;
|
||||||
if (shadowRoot && !selector->mNext && !crossedShadowRootBoundary) {
|
if (shadowRoot && !selector->mNext && !crossedShadowRootBoundary) {
|
||||||
content = shadowRoot->GetHost();
|
content = shadowRoot->GetHost();
|
||||||
crossedShadowRootBoundary = true;
|
crossedShadowRootBoundary = true;
|
||||||
contentIsFeatureless = true;
|
contentIsFeatureless = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetParent could return a document fragment; we only want
|
// GetParent could return a document fragment; we only want
|
||||||
|
|
@ -2662,6 +2718,12 @@ SelectorMatchesTree(Element* aPrevElement,
|
||||||
aTreeMatchContext.mCurrentStyleScope = styleScope;
|
aTreeMatchContext.mCurrentStyleScope = styleScope;
|
||||||
}
|
}
|
||||||
selector = selector->mNext;
|
selector = selector->mNext;
|
||||||
|
if (!selector &&
|
||||||
|
!aTreeMatchContext.mIsTopmostScope &&
|
||||||
|
aTreeMatchContext.mRestrictToSlottedPseudo &&
|
||||||
|
aTreeMatchContext.mScopedRoot != element) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// for adjacent sibling and child combinators, if we didn't find
|
// for adjacent sibling and child combinators, if we didn't find
|
||||||
|
|
@ -2740,6 +2802,52 @@ static bool SelectorListMatches(Element* aElement,
|
||||||
aPreventComplexSelectors);
|
aPreventComplexSelectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
inline bool LookForTargetPseudo(nsCSSSelector* aSelector,
|
||||||
|
TreeMatchContext* aMatchContext,
|
||||||
|
nsRestyleHint* possibleChange) {
|
||||||
|
if (aMatchContext->mOnlyMatchHostPseudo) {
|
||||||
|
while (aSelector && aSelector->mNext != nullptr) {
|
||||||
|
aSelector = aSelector->mNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
|
||||||
|
pseudoClass;
|
||||||
|
pseudoClass = pseudoClass->mNext) {
|
||||||
|
if (pseudoClass->mType == CSSPseudoClassType::host ||
|
||||||
|
pseudoClass->mType == CSSPseudoClassType::hostContext) {
|
||||||
|
if (possibleChange) {
|
||||||
|
// :host-context will walk ancestors looking for a match of a
|
||||||
|
// compound selector, thus any changes to ancestors may require
|
||||||
|
// restyling the subtree.
|
||||||
|
*possibleChange |= eRestyle_Subtree;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (aMatchContext->mRestrictToSlottedPseudo) {
|
||||||
|
for (nsCSSSelector* selector = aSelector;
|
||||||
|
selector;
|
||||||
|
selector = selector->mNext) {
|
||||||
|
if (!selector->mPseudoClassList) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (nsPseudoClassList* pseudoClass = selector->mPseudoClassList;
|
||||||
|
pseudoClass;
|
||||||
|
pseudoClass = pseudoClass->mNext) {
|
||||||
|
if (pseudoClass->mType == CSSPseudoClassType::slotted) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// We're not restricted to a specific pseudo-class.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
|
void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
|
||||||
ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
|
ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
|
||||||
|
|
@ -2748,35 +2856,19 @@ void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
|
||||||
if (nodeContext.mIsRelevantLink) {
|
if (nodeContext.mIsRelevantLink) {
|
||||||
data->mTreeMatchContext.SetHaveRelevantLink();
|
data->mTreeMatchContext.SetHaveRelevantLink();
|
||||||
}
|
}
|
||||||
if (ancestorFilter &&
|
// XXX: Ignore the ancestor filter if we're testing the assigned slot.
|
||||||
|
bool useAncestorFilter = !(data->mTreeMatchContext.mForAssignedSlot);
|
||||||
|
if (useAncestorFilter && ancestorFilter &&
|
||||||
!ancestorFilter->MightHaveMatchingAncestor<RuleValue::eMaxAncestorHashes>(
|
!ancestorFilter->MightHaveMatchingAncestor<RuleValue::eMaxAncestorHashes>(
|
||||||
value.mAncestorSelectorHashes)) {
|
value.mAncestorSelectorHashes)) {
|
||||||
// We won't match; nothing else to do here
|
// We won't match; nothing else to do here
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If mOnlyMatchHostPseudo is set, then we only want to match against
|
|
||||||
// selectors that contain a :host-context pseudo class.
|
|
||||||
if (data->mTreeMatchContext.mOnlyMatchHostPseudo) {
|
|
||||||
nsCSSSelector* selector = aSelector;
|
|
||||||
while (selector && selector->mNext != nullptr) {
|
|
||||||
selector = selector->mNext;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool seenHostPseudo = false;
|
if (!LookForTargetPseudo(aSelector, &data->mTreeMatchContext, nullptr)) {
|
||||||
for (nsPseudoClassList* pseudoClass = selector->mPseudoClassList;
|
return;
|
||||||
pseudoClass;
|
|
||||||
pseudoClass = pseudoClass->mNext) {
|
|
||||||
if (pseudoClass->mType == CSSPseudoClassType::host ||
|
|
||||||
pseudoClass->mType == CSSPseudoClassType::hostContext) {
|
|
||||||
seenHostPseudo = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!seenHostPseudo) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
|
if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
|
||||||
data->mScope)) {
|
data->mScope)) {
|
||||||
// The selector is for a rule in a scoped style sheet, and the subject
|
// The selector is for a rule in a scoped style sheet, and the subject
|
||||||
|
|
@ -2838,7 +2930,13 @@ nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData)
|
||||||
NodeMatchContext nodeContext(EventStates(),
|
NodeMatchContext nodeContext(EventStates(),
|
||||||
nsCSSRuleProcessor::IsLink(aData->mElement),
|
nsCSSRuleProcessor::IsLink(aData->mElement),
|
||||||
aData->mElementIsFeatureless);
|
aData->mElementIsFeatureless);
|
||||||
cascade->mRuleHash.EnumerateAllRules(aData->mElement, aData, nodeContext);
|
// Test against the assigned slot rather than the slottable if we're
|
||||||
|
// matching the ::slotted() pseudo.
|
||||||
|
Element* targetElement = aData->mElement;
|
||||||
|
if (aData->mTreeMatchContext.mForAssignedSlot) {
|
||||||
|
targetElement = aData->mElement->GetAssignedSlot()->AsElement();
|
||||||
|
}
|
||||||
|
cascade->mRuleHash.EnumerateAllRules(targetElement, aData, nodeContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3121,35 +3219,11 @@ AttributeEnumFunc(nsCSSSelector* aSelector,
|
||||||
nsRestyleHint possibleChange =
|
nsRestyleHint possibleChange =
|
||||||
RestyleHintForSelectorWithAttributeChange(aData->change,
|
RestyleHintForSelectorWithAttributeChange(aData->change,
|
||||||
aSelector, aRightmostSelector);
|
aSelector, aRightmostSelector);
|
||||||
// If mOnlyMatchHostPseudo is set, then we only want to match against
|
|
||||||
// selectors that contain a :host-context pseudo class.
|
|
||||||
if (data->mTreeMatchContext.mOnlyMatchHostPseudo) {
|
|
||||||
nsCSSSelector* selector = aSelector;
|
|
||||||
while (selector && selector->mNext != nullptr) {
|
|
||||||
selector = selector->mNext;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool seenHostPseudo = false;
|
if (!LookForTargetPseudo(aSelector, &data->mTreeMatchContext, &possibleChange)) {
|
||||||
for (nsPseudoClassList* pseudoClass = selector->mPseudoClassList;
|
return;
|
||||||
pseudoClass;
|
|
||||||
pseudoClass = pseudoClass->mNext) {
|
|
||||||
if (pseudoClass->mType == CSSPseudoClassType::host ||
|
|
||||||
pseudoClass->mType == CSSPseudoClassType::hostContext) {
|
|
||||||
// :host-context will walk ancestors looking for a match of a compound
|
|
||||||
// selector, thus any changes to ancestors may require restyling the
|
|
||||||
// subtree.
|
|
||||||
possibleChange |= eRestyle_Subtree;
|
|
||||||
seenHostPseudo = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!seenHostPseudo) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// If, ignoring eRestyle_SomeDescendants, enumData->change already includes
|
// If, ignoring eRestyle_SomeDescendants, enumData->change already includes
|
||||||
// all the bits of possibleChange, don't bother calling SelectorMatches, since
|
// all the bits of possibleChange, don't bother calling SelectorMatches, since
|
||||||
// even if it returns false enumData->change won't change. If possibleChange
|
// even if it returns false enumData->change won't change. If possibleChange
|
||||||
|
|
@ -4187,7 +4261,7 @@ nsCSSRuleProcessor::RestrictedSelectorListMatches(Element* aElement,
|
||||||
NodeMatchContext nodeContext(EventStates(), false);
|
NodeMatchContext nodeContext(EventStates(), false);
|
||||||
SelectorMatchesFlags flags = aElement->IsInShadowTree() ?
|
SelectorMatchesFlags flags = aElement->IsInShadowTree() ?
|
||||||
SelectorMatchesFlags::NONE :
|
SelectorMatchesFlags::NONE :
|
||||||
SelectorMatchesFlags::IS_HOST_INACCESSIBLE;
|
SelectorMatchesFlags::IS_OUTSIDE_SHADOW_TREE;
|
||||||
return SelectorListMatches(aElement,
|
return SelectorListMatches(aElement,
|
||||||
aSelectorList,
|
aSelectorList,
|
||||||
nodeContext,
|
nodeContext,
|
||||||
|
|
|
||||||
|
|
@ -371,6 +371,9 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||||
// match.
|
// match.
|
||||||
bool mOnlyMatchHostPseudo;
|
bool mOnlyMatchHostPseudo;
|
||||||
|
|
||||||
|
// Restrict matching to selectors that contain a :slotted() pseudo-class.
|
||||||
|
bool mRestrictToSlottedPseudo;
|
||||||
|
|
||||||
// Root of scoped stylesheet (set and unset by the supplier of the
|
// Root of scoped stylesheet (set and unset by the supplier of the
|
||||||
// scoped stylesheet).
|
// scoped stylesheet).
|
||||||
nsIContent* mScopedRoot;
|
nsIContent* mScopedRoot;
|
||||||
|
|
@ -403,6 +406,13 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||||
// for an HTML5 scoped style sheet.
|
// for an HTML5 scoped style sheet.
|
||||||
bool mForScopedStyle;
|
bool mForScopedStyle;
|
||||||
|
|
||||||
|
// Whether we're currently in the topmost scope for shadow DOM.
|
||||||
|
bool mIsTopmostScope;
|
||||||
|
|
||||||
|
// Whether we're testing for the assigned slot instead of the slottable
|
||||||
|
// when matching type/class/ID/attribute.
|
||||||
|
bool mForAssignedSlot;
|
||||||
|
|
||||||
enum MatchVisited {
|
enum MatchVisited {
|
||||||
eNeverMatchVisited,
|
eNeverMatchVisited,
|
||||||
eMatchVisitedDefault
|
eMatchVisitedDefault
|
||||||
|
|
@ -429,6 +439,7 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||||
, mVisitedHandling(aVisitedHandling)
|
, mVisitedHandling(aVisitedHandling)
|
||||||
, mDocument(aDocument)
|
, mDocument(aDocument)
|
||||||
, mOnlyMatchHostPseudo(false)
|
, mOnlyMatchHostPseudo(false)
|
||||||
|
, mRestrictToSlottedPseudo(false)
|
||||||
, mScopedRoot(nullptr)
|
, mScopedRoot(nullptr)
|
||||||
, mIsHTMLDocument(aDocument->IsHTMLDocument())
|
, mIsHTMLDocument(aDocument->IsHTMLDocument())
|
||||||
, mCompatMode(aDocument->GetCompatibilityMode())
|
, mCompatMode(aDocument->GetCompatibilityMode())
|
||||||
|
|
@ -436,6 +447,8 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||||
, mSkippingParentDisplayBasedStyleFixup(false)
|
, mSkippingParentDisplayBasedStyleFixup(false)
|
||||||
, mForScopedStyle(false)
|
, mForScopedStyle(false)
|
||||||
, mCurrentStyleScope(nullptr)
|
, mCurrentStyleScope(nullptr)
|
||||||
|
, mIsTopmostScope(false)
|
||||||
|
, mForAssignedSlot(false)
|
||||||
{
|
{
|
||||||
if (aMatchVisited != eNeverMatchVisited) {
|
if (aMatchVisited != eNeverMatchVisited) {
|
||||||
nsILoadContext* loadContext = mDocument->GetLoadContext();
|
nsILoadContext* loadContext = mDocument->GetLoadContext();
|
||||||
|
|
|
||||||
|
|
@ -893,3 +893,9 @@ rtc > rt {
|
||||||
ruby, rb, rt, rtc {
|
ruby, rb, rt, rtc {
|
||||||
unicode-bidi: isolate;
|
unicode-bidi: isolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Shadow DOM v1
|
||||||
|
* https://drafts.csswg.org/css-scoping/#slots-in-shadow-tree */
|
||||||
|
slot {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -474,9 +474,3 @@ div:-moz-native-anonymous.moz-custom-content-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shadow DOM v1
|
|
||||||
* https://drafts.csswg.org/css-scoping/#slots-in-shadow-tree */
|
|
||||||
slot {
|
|
||||||
display: contents;
|
|
||||||
}
|
|
||||||
|
|
@ -2562,6 +2562,9 @@ pref("layout.css.legacy-negation-pseudo.enabled", false);
|
||||||
// Is support for the :is() and :where() selectors enabled?
|
// Is support for the :is() and :where() selectors enabled?
|
||||||
pref("layout.css.is-where-pseudo.enabled", true);
|
pref("layout.css.is-where-pseudo.enabled", true);
|
||||||
|
|
||||||
|
// Is support for the ::slotted() selector enabled?
|
||||||
|
pref("layout.css.slotted-pseudo.enabled", true);
|
||||||
|
|
||||||
// Is support for the :scope selector enabled?
|
// Is support for the :scope selector enabled?
|
||||||
pref("layout.css.scope-pseudo.enabled", true);
|
pref("layout.css.scope-pseudo.enabled", true);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -538,8 +538,7 @@ TextEventDispatcher::DispatchKeyboardEventInternal(
|
||||||
|
|
||||||
if (!sDispatchKeyPressEventNonPrintableInContent &&
|
if (!sDispatchKeyPressEventNonPrintableInContent &&
|
||||||
keyEvent.mMessage == eKeyPress &&
|
keyEvent.mMessage == eKeyPress &&
|
||||||
!keyEvent.IsInputtingText() &&
|
!keyEvent.ShouldKeyPressEventBeFiredOnContent()) {
|
||||||
!keyEvent.IsInputtingLineBreak()) {
|
|
||||||
keyEvent.mFlags.mOnlySystemGroupDispatchInContent = true;
|
keyEvent.mFlags.mOnlySystemGroupDispatchInContent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,36 @@ public:
|
||||||
MODIFIER_OS));
|
MODIFIER_OS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ShouldKeyPressEventBeFiredOnContent() should be called only when the
|
||||||
|
* instance is eKeyPress event. This returns true when the eKeyPress
|
||||||
|
* event should be fired even on content in the default event group.
|
||||||
|
*/
|
||||||
|
bool ShouldKeyPressEventBeFiredOnContent() const
|
||||||
|
{
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(mMessage == eKeyPress);
|
||||||
|
// Case 1: Inputting text or a line break: always fire keypress event.
|
||||||
|
if (IsInputtingText() || IsInputtingLineBreak()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Case 2: Ctrl + Enter
|
||||||
|
// Ctrl + Enter won't cause actual input in our editor so should not fire the event if people would be consistent
|
||||||
|
// in setting rules for themselves (foreshadowing... ;P).
|
||||||
|
// However, the other browsers fire the keypress event in this case (but not with other modifiers).
|
||||||
|
// So, for compatibility with them, we should fire the keypress event for Ctrl + Enter too.
|
||||||
|
if (mMessage == eKeyPress &&
|
||||||
|
mKeyNameIndex == KEY_NAME_INDEX_Enter &&
|
||||||
|
!(mModifiers & (MODIFIER_ALT |
|
||||||
|
MODIFIER_META |
|
||||||
|
MODIFIER_OS |
|
||||||
|
MODIFIER_SHIFT))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default: dont't fire in the default event group.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
virtual WidgetEvent* Duplicate() const override
|
virtual WidgetEvent* Duplicate() const override
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mClass == eKeyboardEventClass,
|
MOZ_ASSERT(mClass == eKeyboardEventClass,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue