зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1100912, make dir-state to propagate through shadow DOM, r=mrbkap
This commit is contained in:
Родитель
30d6e52bc7
Коммит
e909e859a5
|
@ -213,6 +213,7 @@
|
|||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/HTMLSlotElement.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "nsUnicodeProperties.h"
|
||||
#include "nsTextFragment.h"
|
||||
|
@ -225,6 +226,50 @@ namespace mozilla {
|
|||
using mozilla::dom::Element;
|
||||
using mozilla::dom::ShadowRoot;
|
||||
|
||||
static nsIContent*
|
||||
GetParentOrHostOrSlot(nsIContent* aContent,
|
||||
bool* aCrossedShadowBoundary = nullptr)
|
||||
{
|
||||
HTMLSlotElement* slot = aContent->GetAssignedSlot();
|
||||
if (slot) {
|
||||
if (aCrossedShadowBoundary) {
|
||||
*aCrossedShadowBoundary = true;
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
nsIContent* parent = aContent->GetParent();
|
||||
if (parent) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
ShadowRoot* sr = ShadowRoot::FromNode(aContent);
|
||||
if (sr) {
|
||||
if (aCrossedShadowBoundary) {
|
||||
*aCrossedShadowBoundary = true;
|
||||
}
|
||||
return sr->Host();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
AncestorChainCrossesShadowBoundary(nsIContent* aDescendant,
|
||||
nsIContent* aAncestor)
|
||||
{
|
||||
bool crossedShadowBoundary = false;
|
||||
nsIContent* content = aDescendant;
|
||||
while(content && content != aAncestor) {
|
||||
content = GetParentOrHostOrSlot(content, &crossedShadowBoundary);
|
||||
if (crossedShadowBoundary) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if aElement is one of the elements whose text content should not
|
||||
* affect its own direction, nor the direction of ancestors with dir=auto.
|
||||
|
@ -236,14 +281,15 @@ using mozilla::dom::ShadowRoot;
|
|||
* unicode-bidi: plaintext and is handled automatically in bidi resolution.
|
||||
*/
|
||||
static bool
|
||||
DoesNotParticipateInAutoDirection(const Element* aElement)
|
||||
DoesNotParticipateInAutoDirection(const nsIContent* aContent)
|
||||
{
|
||||
mozilla::dom::NodeInfo* nodeInfo = aElement->NodeInfo();
|
||||
return (!aElement->IsHTMLElement() ||
|
||||
nodeInfo->Equals(nsGkAtoms::script) ||
|
||||
nodeInfo->Equals(nsGkAtoms::style) ||
|
||||
nodeInfo->Equals(nsGkAtoms::textarea) ||
|
||||
aElement->IsInAnonymousSubtree());
|
||||
mozilla::dom::NodeInfo* nodeInfo = aContent->NodeInfo();
|
||||
return ((!aContent->IsHTMLElement() ||
|
||||
nodeInfo->Equals(nsGkAtoms::script) ||
|
||||
nodeInfo->Equals(nsGkAtoms::style) ||
|
||||
nodeInfo->Equals(nsGkAtoms::textarea) ||
|
||||
aContent->IsInAnonymousSubtree())) &&
|
||||
!aContent->IsShadowRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -278,12 +324,10 @@ GetDirectionFromChar(uint32_t ch)
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME(bug 1100912): Should ShadowRoot children affect the host if it's
|
||||
// dir=auto? Probably not at least in closed mode.
|
||||
inline static bool
|
||||
NodeAffectsDirAutoAncestor(nsINode* aTextNode)
|
||||
NodeAffectsDirAutoAncestor(nsIContent* aTextNode)
|
||||
{
|
||||
Element* parent = aTextNode->GetParentElement();
|
||||
nsIContent* parent = GetParentOrHostOrSlot(aTextNode);
|
||||
return (parent &&
|
||||
!DoesNotParticipateInAutoDirection(parent) &&
|
||||
parent->NodeOrAncestorHasDirAuto() &&
|
||||
|
@ -365,6 +409,58 @@ GetDirectionFromText(const nsTextFragment* aFrag,
|
|||
aFirstStrong);
|
||||
}
|
||||
|
||||
static nsTextNode*
|
||||
WalkDescendantsAndGetDirectionFromText(nsINode* aRoot,
|
||||
nsINode* aSkip,
|
||||
Directionality* aDirectionality)
|
||||
{
|
||||
nsIContent* child = aRoot->GetFirstChild();
|
||||
while (child) {
|
||||
if ((child->IsElement() &&
|
||||
DoesNotAffectDirectionOfAncestors(child->AsElement())) ||
|
||||
child->GetAssignedSlot()) {
|
||||
child = child->GetNextNonChildNode(aRoot);
|
||||
continue;
|
||||
}
|
||||
|
||||
HTMLSlotElement* slot = HTMLSlotElement::FromNode(child);
|
||||
if (slot) {
|
||||
const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
|
||||
for (uint32_t i = 0; i < assignedNodes.Length(); ++i) {
|
||||
nsIContent* assignedNode = assignedNodes[i]->AsContent();
|
||||
if (assignedNode->NodeType() == nsINode::TEXT_NODE) {
|
||||
if (assignedNode != aSkip) {
|
||||
Directionality textNodeDir = GetDirectionFromText(assignedNode->GetText());
|
||||
if (textNodeDir != eDir_NotSet) {
|
||||
*aDirectionality = textNodeDir;
|
||||
return static_cast<nsTextNode*>(assignedNode);
|
||||
}
|
||||
}
|
||||
} else if (assignedNode->IsElement() &&
|
||||
!DoesNotAffectDirectionOfAncestors(assignedNode->AsElement())) {
|
||||
nsTextNode* text = WalkDescendantsAndGetDirectionFromText(
|
||||
assignedNode, aSkip, aDirectionality);
|
||||
if (text) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (child->NodeType() == nsINode::TEXT_NODE &&
|
||||
child != aSkip) {
|
||||
Directionality textNodeDir = GetDirectionFromText(child->GetText());
|
||||
if (textNodeDir != eDir_NotSet) {
|
||||
*aDirectionality = textNodeDir;
|
||||
return static_cast<nsTextNode*>(child);
|
||||
}
|
||||
}
|
||||
child = child->GetNextNode(aRoot);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the directionality of a node with dir=auto as defined in
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-directionality
|
||||
|
@ -385,25 +481,26 @@ WalkDescendantsSetDirectionFromText(Element* aElement, bool aNotify,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent* child = aElement->GetFirstChild();
|
||||
while (child) {
|
||||
if (child->IsElement() &&
|
||||
DoesNotAffectDirectionOfAncestors(child->AsElement())) {
|
||||
child = child->GetNextNonChildNode(aElement);
|
||||
continue;
|
||||
}
|
||||
Directionality textNodeDir = eDir_NotSet;
|
||||
|
||||
if (child->NodeType() == nsINode::TEXT_NODE &&
|
||||
child != aChangedNode) {
|
||||
Directionality textNodeDir = GetDirectionFromText(child->GetText());
|
||||
if (textNodeDir != eDir_NotSet) {
|
||||
// We found a descendant text node with strong directional characters.
|
||||
// Set the directionality of aElement to the corresponding value.
|
||||
aElement->SetDirectionality(textNodeDir, aNotify);
|
||||
return static_cast<nsTextNode*>(child);
|
||||
}
|
||||
// Check the text in Shadow DOM.
|
||||
if (ShadowRoot* shadowRoot = aElement->GetShadowRoot()) {
|
||||
nsTextNode* text = WalkDescendantsAndGetDirectionFromText(shadowRoot,
|
||||
aChangedNode,
|
||||
&textNodeDir);
|
||||
if (text) {
|
||||
aElement->SetDirectionality(textNodeDir, aNotify);
|
||||
return text;
|
||||
}
|
||||
child = child->GetNextNode(aElement);
|
||||
}
|
||||
|
||||
// Check the text in light DOM.
|
||||
nsTextNode* text = WalkDescendantsAndGetDirectionFromText(aElement,
|
||||
aChangedNode,
|
||||
&textNodeDir);
|
||||
if (text) {
|
||||
aElement->SetDirectionality(textNodeDir, aNotify);
|
||||
return text;
|
||||
}
|
||||
|
||||
// We walked all the descendants without finding a text node with strong
|
||||
|
@ -634,7 +731,7 @@ RecomputeDirectionality(Element* aElement, bool aNotify)
|
|||
}
|
||||
|
||||
Directionality dir = eDir_LTR;
|
||||
if (nsINode* parent = aElement->GetParentNode()) {
|
||||
if (nsIContent* parent = GetParentOrHostOrSlot(aElement)) {
|
||||
if (ShadowRoot* shadow = ShadowRoot::FromNode(parent)) {
|
||||
parent = shadow->GetHost();
|
||||
}
|
||||
|
@ -672,14 +769,32 @@ SetDirectionalityOnDescendantsInternal(nsINode* aNode,
|
|||
}
|
||||
|
||||
Element* element = child->AsElement();
|
||||
if (element->HasValidDir() || element->HasDirAuto()) {
|
||||
if (element->HasValidDir() || element->HasDirAuto() ||
|
||||
element->GetAssignedSlot()) {
|
||||
child = child->GetNextNonChildNode(aNode);
|
||||
continue;
|
||||
}
|
||||
if (ShadowRoot* shadow = element->GetShadowRoot()) {
|
||||
SetDirectionalityOnDescendantsInternal(shadow, aDir, aNotify);
|
||||
}
|
||||
|
||||
HTMLSlotElement* slot = HTMLSlotElement::FromNode(child);
|
||||
if (slot) {
|
||||
const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
|
||||
for (uint32_t i = 0; i < assignedNodes.Length(); ++i) {
|
||||
nsINode* node = assignedNodes[i];
|
||||
Element* assignedElement =
|
||||
node->IsElement() ? node->AsElement() : nullptr;
|
||||
if (assignedElement && !assignedElement->HasValidDir() &&
|
||||
!assignedElement->HasDirAuto()) {
|
||||
assignedElement->SetDirectionality(aDir, aNotify);
|
||||
SetDirectionalityOnDescendantsInternal(assignedElement, aDir, aNotify);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
element->SetDirectionality(aDir, aNotify);
|
||||
|
||||
child = child->GetNextNode(aNode);
|
||||
}
|
||||
}
|
||||
|
@ -692,6 +807,33 @@ SetDirectionalityOnDescendants(Element* aElement, Directionality aDir,
|
|||
return SetDirectionalityOnDescendantsInternal(aElement, aDir, aNotify);
|
||||
}
|
||||
|
||||
static void
|
||||
ResetAutoDirection(Element* aElement, bool aNotify)
|
||||
{
|
||||
if (aElement->HasDirAutoSet()) {
|
||||
// If the parent has the DirAutoSet flag, its direction is determined by
|
||||
// some text node descendant.
|
||||
// Remove it from the map and reset its direction by the downward
|
||||
// propagation algorithm
|
||||
nsTextNode* setByNode =
|
||||
static_cast<nsTextNode*>(aElement->GetProperty(nsGkAtoms::dirAutoSetBy));
|
||||
if (setByNode) {
|
||||
nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode,
|
||||
aElement);
|
||||
}
|
||||
}
|
||||
|
||||
if (aElement->HasDirAuto()) {
|
||||
nsTextNode* setByNode =
|
||||
WalkDescendantsSetDirectionFromText(aElement, aNotify);
|
||||
if (setByNode) {
|
||||
nsTextNodeDirectionalityMap::AddEntryToMap(setByNode, aElement);
|
||||
}
|
||||
SetDirectionalityOnDescendants(aElement, aElement->GetDirectionality(),
|
||||
aNotify);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk the parent chain of a text node whose dir attribute has been removed and
|
||||
* reset the direction of any of its ancestors which have dir=auto and whose
|
||||
|
@ -701,9 +843,14 @@ void
|
|||
WalkAncestorsResetAutoDirection(Element* aElement, bool aNotify)
|
||||
{
|
||||
nsTextNode* setByNode;
|
||||
Element* parent = aElement->GetParentElement();
|
||||
|
||||
nsIContent* parent = GetParentOrHostOrSlot(aElement);
|
||||
while (parent && parent->NodeOrAncestorHasDirAuto()) {
|
||||
if (!parent->IsElement()) {
|
||||
parent = GetParentOrHostOrSlot(parent);
|
||||
continue;
|
||||
}
|
||||
|
||||
Element* parentElement = parent->AsElement();
|
||||
if (parent->HasDirAutoSet()) {
|
||||
// If the parent has the DirAutoSet flag, its direction is determined by
|
||||
// some text node descendant.
|
||||
|
@ -712,17 +859,52 @@ WalkAncestorsResetAutoDirection(Element* aElement, bool aNotify)
|
|||
setByNode =
|
||||
static_cast<nsTextNode*>(parent->GetProperty(nsGkAtoms::dirAutoSetBy));
|
||||
if (setByNode) {
|
||||
nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode, parent);
|
||||
nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode,
|
||||
parentElement);
|
||||
}
|
||||
}
|
||||
if (parent->HasDirAuto()) {
|
||||
setByNode = WalkDescendantsSetDirectionFromText(parent, aNotify);
|
||||
if (parentElement->HasDirAuto()) {
|
||||
setByNode = WalkDescendantsSetDirectionFromText(parentElement, aNotify);
|
||||
if (setByNode) {
|
||||
nsTextNodeDirectionalityMap::AddEntryToMap(setByNode, parent);
|
||||
nsTextNodeDirectionalityMap::AddEntryToMap(setByNode, parentElement);
|
||||
}
|
||||
SetDirectionalityOnDescendants(parentElement, parentElement->GetDirectionality(),
|
||||
aNotify);
|
||||
break;
|
||||
}
|
||||
parent = parent->GetParentElement();
|
||||
parent = GetParentOrHostOrSlot(parent);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SlotStateChanged(HTMLSlotElement* aSlot)
|
||||
{
|
||||
if (!aSlot) {
|
||||
return;
|
||||
}
|
||||
if (aSlot->HasDirAuto()) {
|
||||
ResetAutoDirection(aSlot, true);
|
||||
}
|
||||
if (aSlot->NodeOrAncestorHasDirAuto()) {
|
||||
WalkAncestorsResetAutoDirection(aSlot, true);
|
||||
}
|
||||
|
||||
const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
|
||||
for (uint32_t i = 0; i < assignedNodes.Length(); ++i) {
|
||||
nsINode* node = assignedNodes[i];
|
||||
Element* assignedElement =
|
||||
node->IsElement() ? node->AsElement() : nullptr;
|
||||
// Try to optimize out state changes when possible.
|
||||
if (assignedElement && !assignedElement->HasValidDir() &&
|
||||
!assignedElement->HasDirAuto() &&
|
||||
assignedElement->GetDirectionality() !=
|
||||
aSlot->GetDirectionality()) {
|
||||
assignedElement->SetDirectionality(aSlot->GetDirectionality(),
|
||||
true);
|
||||
SetDirectionalityOnDescendantsInternal(assignedElement,
|
||||
aSlot->GetDirectionality(),
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -747,6 +929,51 @@ WalkDescendantsResetAutoDirection(Element* aElement)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SetAncestorHasDirAutoOnDescendants(nsINode* aRoot);
|
||||
|
||||
static void
|
||||
MaybeSetAncestorHasDirAutoOnShadowDOM(nsINode* aNode)
|
||||
{
|
||||
if (aNode->IsElement()) {
|
||||
if (ShadowRoot* sr = aNode->AsElement()->GetShadowRoot()) {
|
||||
sr->SetAncestorHasDirAuto();
|
||||
SetAncestorHasDirAutoOnDescendants(sr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SetAncestorHasDirAutoOnDescendants(nsINode* aRoot)
|
||||
{
|
||||
MaybeSetAncestorHasDirAutoOnShadowDOM(aRoot);
|
||||
|
||||
nsIContent* child = aRoot->GetFirstChild();
|
||||
while (child) {
|
||||
if (child->IsElement() &&
|
||||
DoesNotAffectDirectionOfAncestors(child->AsElement())) {
|
||||
child = child->GetNextNonChildNode(aRoot);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the child is assigned to a slot, it should inherit the state from
|
||||
// that.
|
||||
if (!child->GetAssignedSlot()) {
|
||||
MaybeSetAncestorHasDirAutoOnShadowDOM(child);
|
||||
child->SetAncestorHasDirAuto();
|
||||
HTMLSlotElement* slot = HTMLSlotElement::FromNode(child);
|
||||
if (slot) {
|
||||
const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
|
||||
for (uint32_t i = 0; i < assignedNodes.Length(); ++i) {
|
||||
assignedNodes[i]->SetAncestorHasDirAuto();
|
||||
SetAncestorHasDirAutoOnDescendants(assignedNodes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
child = child->GetNextNode(aRoot);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WalkDescendantsSetDirAuto(Element* aElement, bool aNotify)
|
||||
{
|
||||
|
@ -756,31 +983,9 @@ WalkDescendantsSetDirAuto(Element* aElement, bool aNotify)
|
|||
// <bdi>, we *do* want to set AncestorHasDirAuto on its descendants, unlike
|
||||
// in SetDirOnBind where we don't propagate AncestorHasDirAuto to a <bdi>
|
||||
// being bound to an existing node with dir=auto.
|
||||
if (!DoesNotParticipateInAutoDirection(aElement)) {
|
||||
|
||||
bool setAncestorDirAutoFlag =
|
||||
#ifdef DEBUG
|
||||
true;
|
||||
#else
|
||||
!aElement->AncestorHasDirAuto();
|
||||
#endif
|
||||
|
||||
if (setAncestorDirAutoFlag) {
|
||||
nsIContent* child = aElement->GetFirstChild();
|
||||
while (child) {
|
||||
if (child->IsElement() &&
|
||||
DoesNotAffectDirectionOfAncestors(child->AsElement())) {
|
||||
child = child->GetNextNonChildNode(aElement);
|
||||
continue;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!aElement->AncestorHasDirAuto() ||
|
||||
child->AncestorHasDirAuto(),
|
||||
"AncestorHasDirAuto set on node but not its children");
|
||||
child->SetAncestorHasDirAuto();
|
||||
child = child->GetNextNode(aElement);
|
||||
}
|
||||
}
|
||||
if (!DoesNotParticipateInAutoDirection(aElement) &&
|
||||
!aElement->AncestorHasDirAuto()) {
|
||||
SetAncestorHasDirAutoOnDescendants(aElement);
|
||||
}
|
||||
|
||||
nsTextNode* textNode = WalkDescendantsSetDirectionFromText(aElement, aNotify);
|
||||
|
@ -790,17 +995,49 @@ WalkDescendantsSetDirAuto(Element* aElement, bool aNotify)
|
|||
}
|
||||
|
||||
void
|
||||
WalkDescendantsClearAncestorDirAuto(Element* aElement)
|
||||
WalkDescendantsClearAncestorDirAuto(nsIContent* aContent)
|
||||
{
|
||||
nsIContent* child = aElement->GetFirstChild();
|
||||
if (aContent->IsElement()) {
|
||||
if (ShadowRoot* shadowRoot = aContent->AsElement()->GetShadowRoot()) {
|
||||
shadowRoot->ClearAncestorHasDirAuto();
|
||||
WalkDescendantsClearAncestorDirAuto(shadowRoot);
|
||||
}
|
||||
}
|
||||
|
||||
nsIContent* child = aContent->GetFirstChild();
|
||||
while (child) {
|
||||
if (child->IsElement() && child->AsElement()->HasDirAuto()) {
|
||||
child = child->GetNextNonChildNode(aElement);
|
||||
if (child->GetAssignedSlot()) {
|
||||
// If the child node is assigned to a slot, nodes state is inherited from
|
||||
// the slot, not from element's parent.
|
||||
child = child->GetNextNonChildNode(aContent);
|
||||
continue;
|
||||
}
|
||||
if (child->IsElement()) {
|
||||
if (child->AsElement()->HasDirAuto()) {
|
||||
child = child->GetNextNonChildNode(aContent);
|
||||
continue;
|
||||
}
|
||||
|
||||
HTMLSlotElement* slot = HTMLSlotElement::FromNode(child);
|
||||
if (slot) {
|
||||
const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
|
||||
for (uint32_t i = 0; i < assignedNodes.Length(); ++i) {
|
||||
if (assignedNodes[i]->IsElement()) {
|
||||
Element* slottedElement = assignedNodes[i]->AsElement();
|
||||
if (slottedElement->HasDirAuto()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
nsIContent* content = assignedNodes[i]->AsContent();
|
||||
content->ClearAncestorHasDirAuto();
|
||||
WalkDescendantsClearAncestorDirAuto(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
child->ClearAncestorHasDirAuto();
|
||||
child = child->GetNextNode(aElement);
|
||||
child = child->GetNextNode(aContent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -811,13 +1048,21 @@ SetAncestorDirectionIfAuto(nsTextNode* aTextNode, Directionality aDir,
|
|||
MOZ_ASSERT(aTextNode->NodeType() == nsINode::TEXT_NODE,
|
||||
"Must be a text node");
|
||||
|
||||
Element* parent = aTextNode->GetParentElement();
|
||||
bool crossedShadowBoundary = false;
|
||||
nsIContent* parent = GetParentOrHostOrSlot(aTextNode, &crossedShadowBoundary);
|
||||
while (parent && parent->NodeOrAncestorHasDirAuto()) {
|
||||
if (DoesNotParticipateInAutoDirection(parent) || parent->HasFixedDir()) {
|
||||
if (!parent->IsElement()) {
|
||||
parent = GetParentOrHostOrSlot(parent, &crossedShadowBoundary);
|
||||
continue;
|
||||
}
|
||||
|
||||
Element* parentElement = parent->AsElement();
|
||||
if (DoesNotParticipateInAutoDirection(parentElement) ||
|
||||
parentElement->HasFixedDir()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (parent->HasDirAuto()) {
|
||||
if (parentElement->HasDirAuto()) {
|
||||
bool resetDirection = false;
|
||||
nsTextNode* directionWasSetByTextNode =
|
||||
static_cast<nsTextNode*>(parent->GetProperty(nsGkAtoms::dirAutoSetBy));
|
||||
|
@ -836,6 +1081,15 @@ SetAncestorDirectionIfAuto(nsTextNode* aTextNode, Directionality aDir,
|
|||
if (!directionWasSetByTextNode) {
|
||||
resetDirection = true;
|
||||
} else if (directionWasSetByTextNode != aTextNode) {
|
||||
if (crossedShadowBoundary ||
|
||||
AncestorChainCrossesShadowBoundary(directionWasSetByTextNode,
|
||||
parent)) {
|
||||
// Need to take the slow path when the path from either the old or
|
||||
// new text node to the dir=auto element crosses shadow boundary.
|
||||
ResetAutoDirection(parentElement, aNotify);
|
||||
return;
|
||||
}
|
||||
|
||||
nsIContent* child = aTextNode->GetNextNode(parent);
|
||||
while (child) {
|
||||
if (child->IsElement() &&
|
||||
|
@ -859,12 +1113,12 @@ SetAncestorDirectionIfAuto(nsTextNode* aTextNode, Directionality aDir,
|
|||
if (resetDirection) {
|
||||
if (directionWasSetByTextNode) {
|
||||
nsTextNodeDirectionalityMap::RemoveElementFromMap(
|
||||
directionWasSetByTextNode, parent
|
||||
directionWasSetByTextNode, parentElement
|
||||
);
|
||||
}
|
||||
parent->SetDirectionality(aDir, aNotify);
|
||||
nsTextNodeDirectionalityMap::AddEntryToMap(aTextNode, parent);
|
||||
SetDirectionalityOnDescendants(parent, aDir, aNotify);
|
||||
parentElement->SetDirectionality(aDir, aNotify);
|
||||
nsTextNodeDirectionalityMap::AddEntryToMap(aTextNode, parentElement);
|
||||
SetDirectionalityOnDescendants(parentElement, aDir, aNotify);
|
||||
}
|
||||
|
||||
// Since we found an element with dir=auto, we can stop walking the
|
||||
|
@ -872,7 +1126,7 @@ SetAncestorDirectionIfAuto(nsTextNode* aTextNode, Directionality aDir,
|
|||
// any of its descendants.
|
||||
return;
|
||||
}
|
||||
parent = parent->GetParentElement();
|
||||
parent = GetParentOrHostOrSlot(parent, &crossedShadowBoundary);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -927,7 +1181,7 @@ SetDirectionFromNewTextNode(nsTextNode* aTextNode)
|
|||
return;
|
||||
}
|
||||
|
||||
Element* parent = aTextNode->GetParentElement();
|
||||
nsIContent* parent = GetParentOrHostOrSlot(aTextNode);
|
||||
if (parent && parent->NodeOrAncestorHasDirAuto()) {
|
||||
aTextNode->SetAncestorHasDirAuto();
|
||||
}
|
||||
|
@ -1028,22 +1282,9 @@ SetDirOnBind(Element* aElement, nsIContent* aParent)
|
|||
aParent && aParent->NodeOrAncestorHasDirAuto()) {
|
||||
aElement->SetAncestorHasDirAuto();
|
||||
|
||||
nsIContent* child = aElement->GetFirstChild();
|
||||
if (child) {
|
||||
// If we are binding an element to the tree that already has descendants,
|
||||
// and the parent has NodeHasDirAuto or NodeAncestorHasDirAuto, we need
|
||||
// to set NodeAncestorHasDirAuto on all the element's descendants, except
|
||||
// for nodes that don't affect the direction of their ancestors.
|
||||
do {
|
||||
if (child->IsElement() &&
|
||||
DoesNotAffectDirectionOfAncestors(child->AsElement())) {
|
||||
child = child->GetNextNonChildNode(aElement);
|
||||
continue;
|
||||
}
|
||||
SetAncestorHasDirAutoOnDescendants(aElement);
|
||||
|
||||
child->SetAncestorHasDirAuto();
|
||||
child = child->GetNextNode(aElement);
|
||||
} while (child);
|
||||
if (aElement->GetFirstChild() || aElement->GetShadowRoot()) {
|
||||
|
||||
// We may also need to reset the direction of an ancestor with dir=auto
|
||||
WalkAncestorsResetAutoDirection(aElement, true);
|
||||
|
|
|
@ -17,6 +17,7 @@ class nsTextNode;
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Element;
|
||||
class HTMLSlotElement;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -71,6 +72,12 @@ void SetDirectionalityOnDescendants(mozilla::dom::Element* aElement,
|
|||
*/
|
||||
void WalkDescendantsResetAutoDirection(mozilla::dom::Element* aElement);
|
||||
|
||||
/**
|
||||
* In case a slot element was added or removed or its assigned nodes changed,
|
||||
* it may change the directionality of ancestors or assigned nodes.
|
||||
*/
|
||||
void SlotStateChanged(mozilla::dom::HTMLSlotElement* aSlot);
|
||||
|
||||
/**
|
||||
* After setting dir=auto on an element, walk its descendants in tree order.
|
||||
* If the node doesn't have the NODE_ANCESTOR_HAS_DIR_AUTO flag, set the
|
||||
|
@ -87,7 +94,7 @@ void WalkDescendantsSetDirAuto(mozilla::dom::Element* aElement,
|
|||
* skipping any that have dir=auto themselves, and unset the
|
||||
* NODE_ANCESTOR_HAS_DIR_AUTO flag
|
||||
*/
|
||||
void WalkDescendantsClearAncestorDirAuto(mozilla::dom::Element* aElement);
|
||||
void WalkDescendantsClearAncestorDirAuto(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* When the contents of a text node are about to change, retrieve the current
|
||||
|
|
|
@ -1246,6 +1246,10 @@ Element::AttachShadowWithoutNameChecks(ShadowRootMode aMode)
|
|||
|
||||
shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
|
||||
|
||||
if (NodeOrAncestorHasDirAuto()) {
|
||||
shadowRoot->SetAncestorHasDirAuto();
|
||||
}
|
||||
|
||||
/**
|
||||
* 5. Set context object’s shadow root to shadow.
|
||||
*/
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "ChildIterator.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIStyleSheetLinkingElement.h"
|
||||
#include "mozilla/dom/DirectionalityUtils.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/HTMLSlotElement.h"
|
||||
#include "nsXBLPrototypeBinding.h"
|
||||
|
@ -197,6 +198,8 @@ ShadowRoot::AddSlot(HTMLSlotElement* aSlot)
|
|||
if (doEnqueueSlotChange) {
|
||||
oldSlot->EnqueueSlotChangeEvent();
|
||||
currentSlot->EnqueueSlotChangeEvent();
|
||||
SlotStateChanged(oldSlot);
|
||||
SlotStateChanged(currentSlot);
|
||||
}
|
||||
} else {
|
||||
bool doEnqueueSlotChange = false;
|
||||
|
@ -217,6 +220,7 @@ ShadowRoot::AddSlot(HTMLSlotElement* aSlot)
|
|||
|
||||
if (doEnqueueSlotChange) {
|
||||
currentSlot->EnqueueSlotChangeEvent();
|
||||
SlotStateChanged(currentSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -522,6 +526,9 @@ ShadowRoot::MaybeReassignElement(Element* aElement)
|
|||
}
|
||||
assignment.mSlot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
|
||||
SlotStateChanged(oldSlot);
|
||||
SlotStateChanged(assignment.mSlot);
|
||||
}
|
||||
|
||||
Element*
|
||||
|
@ -601,6 +608,8 @@ ShadowRoot::ContentInserted(nsIContent* aChild)
|
|||
assignment.mSlot->AppendAssignedNode(aChild);
|
||||
}
|
||||
assignment.mSlot->EnqueueSlotChangeEvent();
|
||||
|
||||
SlotStateChanged(assignment.mSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1100912
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1100912</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
td {
|
||||
border-right: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1100912">Mozilla Bug 1100912</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<!-- The table is here just to make the web page easier to read -->
|
||||
<table>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td> </td>
|
||||
<td> </td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
|
||||
<tr><td>1.</td><td>
|
||||
<div id="host1" dir="rtl"><span> 3 4 </span></div>
|
||||
</td><td></td><td>rtl on host</td></tr>
|
||||
|
||||
<tr><td>2.</td><td>
|
||||
<div id="host2" dir="rtl"><span> 3 4 </span></div>
|
||||
</td><td></td><td>rtl on host, ltr slot's parent</td></tr>
|
||||
|
||||
<tr><td>3.</td><td>
|
||||
<div id="host3" dir="rtl"><span> 3 4 </span></div>
|
||||
</td><td></td><td>rtl on host, ltr on slot</td></tr>
|
||||
|
||||
<tr><td>4.</td><td>
|
||||
<div id="host4" dir="auto"><span> 1 2 </span></div>
|
||||
</td><td></td><td>auto host, rtl in shadow</td></tr>
|
||||
|
||||
<tr><td>5.</td><td>
|
||||
<div id="host5" dir="auto"><span> أخبار </span></div>
|
||||
</td><td></td><td>auto host, rtl in host (in assigned node)</td></tr>
|
||||
|
||||
<tr><td>6.</td><td>
|
||||
<div id="host6" dir="auto"><span> أخبار </span></div>
|
||||
</td><td></td><td>auto host, rtl in host, no assigned node</td></tr>
|
||||
|
||||
<tr><td>7.</td><td>
|
||||
<div id="host7" dir="auto"><span> أخبار </span></div>
|
||||
</td><td></td><td>auto host, rtl in host, explicit ltr in shadow</td></tr>
|
||||
|
||||
<tr><td>8.</td><td>
|
||||
<div id="host8" dir="auto"><span slot="second">‎1 2 </span><span slot="first"> أخبار </span></div>
|
||||
</td><td></td><td>auto host, ltr in host, rtl in host, reverse order in slots</td></tr>
|
||||
|
||||
<tr><td>9.</td><td>
|
||||
<div id="host9" dir="auto">أخبار</div>
|
||||
</td><td></td><td>auto host, rtl in host (in assigned text node)</td></tr>
|
||||
|
||||
<tr><td>10.</td><td>
|
||||
<div id="host10" dir="auto"> 1 2</div>
|
||||
</td><td></td><td>auto host, 1 2 in host (in assigned text node)</td></tr>
|
||||
|
||||
</table>
|
||||
<script>
|
||||
|
||||
function ltrExpected(element) {
|
||||
opener.is(element.parentNode.querySelector(":dir(ltr)"), element,
|
||||
"Should have got an ltr element.");
|
||||
}
|
||||
|
||||
function rtlExpected(element) {
|
||||
opener.is(element.parentNode.querySelector(":dir(rtl)"), element,
|
||||
"Should have got an rtl element.");
|
||||
}
|
||||
|
||||
const shadowRoot1 = host1.attachShadow({mode: 'closed'});
|
||||
shadowRoot1.innerHTML = '<div> 1 2 <span><slot></slot></span></div>';
|
||||
rtlExpected(host1);
|
||||
rtlExpected(host1.firstChild);
|
||||
rtlExpected(shadowRoot1.firstChild.lastChild); // span in the Shadow DOM
|
||||
|
||||
const shadowRoot2 = host2.attachShadow({mode: 'closed'});
|
||||
shadowRoot2.innerHTML = '<div> 1 2 <span dir="ltr"><slot></slot></span></div>';
|
||||
rtlExpected(host2);
|
||||
ltrExpected(host2.firstChild);
|
||||
|
||||
// This is weird case, and we have similar behavior as Blink. dir= on <slot>
|
||||
// doesn't affect to UI since slot has display: contents by default.
|
||||
const shadowRoot3 = host3.attachShadow({mode: 'closed'});
|
||||
shadowRoot3.innerHTML = '<div> 1 2 <span><slot dir="ltr"></slot></span></div>';
|
||||
rtlExpected(host3);
|
||||
|
||||
const shadowRoot4 = host4.attachShadow({mode: 'closed'});
|
||||
shadowRoot4.innerHTML = '<div> أخبار <span><slot></slot></span></div>';
|
||||
rtlExpected(host4);
|
||||
rtlExpected(host4.firstChild);
|
||||
rtlExpected(shadowRoot4.firstChild.lastChild);
|
||||
|
||||
const shadowRoot5 = host5.attachShadow({mode: 'closed'});
|
||||
shadowRoot5.innerHTML = '<div> 1 2 <span><slot></slot></span></div>';
|
||||
rtlExpected(host5);
|
||||
rtlExpected(host5.firstChild);
|
||||
rtlExpected(shadowRoot5.firstChild.lastChild);
|
||||
|
||||
// This case is different to Blink since it doesn't deal with nodes which aren't
|
||||
// in the flattened tree, so it doesn't detect rtl in child nodes, which
|
||||
// aren't assigned to any slot.
|
||||
const shadowRoot6 = host6.attachShadow({mode: 'closed'});
|
||||
shadowRoot6.innerHTML = '<div> 1 2 <span></span></div>';
|
||||
rtlExpected(host6);
|
||||
rtlExpected(host6.firstChild);
|
||||
rtlExpected(shadowRoot6.firstChild.lastChild);
|
||||
|
||||
const shadowRoot7 = host7.attachShadow({mode: 'closed'});
|
||||
shadowRoot7.innerHTML = '<div> ‎1 2 <span><slot></slot></span></div>';
|
||||
ltrExpected(host7);
|
||||
ltrExpected(host7.firstChild);
|
||||
ltrExpected(shadowRoot7.firstChild.lastChild);
|
||||
|
||||
const shadowRoot8 = host8.attachShadow({mode: 'closed'});
|
||||
shadowRoot8.innerHTML = '<div><slot name="first"></slot><slot name="second"></slot></div>';
|
||||
rtlExpected(host8);
|
||||
rtlExpected(host8.firstChild);
|
||||
rtlExpected(shadowRoot8.firstChild.firstChild);
|
||||
|
||||
const shadowRoot9 = host9.attachShadow({mode: 'closed'});
|
||||
shadowRoot9.innerHTML = '<div> 1 2 <span><slot></slot></span></div>';
|
||||
rtlExpected(host9);
|
||||
rtlExpected(shadowRoot9.firstChild.lastChild);
|
||||
|
||||
const shadowRoot10 = host10.attachShadow({mode: 'closed'});
|
||||
shadowRoot10.innerHTML = '<div> أخبار <span><slot></slot></span></div>';
|
||||
rtlExpected(host10);
|
||||
rtlExpected(shadowRoot10.firstChild.lastChild);
|
||||
|
||||
opener.didRunTests();
|
||||
window.close();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -587,6 +587,8 @@ support-files = test_bug1037687_subframe.html
|
|||
[test_bug1070015.html]
|
||||
[test_bug1075702.html]
|
||||
[test_bug1091883.html]
|
||||
[test_bug1100912.html]
|
||||
support-files = file_bug1100912.html
|
||||
[test_bug1101364.html]
|
||||
skip-if = toolkit == 'android'
|
||||
[test_bug1118689.html]
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1100912
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1100912</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
/** Test for Bug 1100912 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function init() {
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{
|
||||
"set": [["dom.webcomponents.shadowdom.enabled", true]]
|
||||
},
|
||||
runTests);
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
win = window.open("file_bug1100912.html", "");
|
||||
}
|
||||
|
||||
function didRunTests() {
|
||||
setTimeout("SimpleTest.finish()");
|
||||
}
|
||||
|
||||
SimpleTest.waitForFocus(init);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1100912">Mozilla Bug 1100912</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче