Bug 1100912, make dir-state to propagate through shadow DOM, r=mrbkap

This commit is contained in:
Olli Pettay 2018-07-21 13:11:06 +03:00
Родитель 30d6e52bc7
Коммит e909e859a5
7 изменённых файлов: 550 добавлений и 92 удалений

Просмотреть файл

@ -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 objects 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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</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> &#1571;&#1582;&#1576;&#1575;&#1585; </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> &#1571;&#1582;&#1576;&#1575;&#1585; </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> &#1571;&#1582;&#1576;&#1575;&#1585; </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">&lrm;1 2 </span><span slot="first"> &#1571;&#1582;&#1576;&#1575;&#1585; </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">&#1571;&#1582;&#1576;&#1575;&#1585;</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> &#1571;&#1582;&#1576;&#1575;&#1585; <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> &lrm;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> &#1571;&#1582;&#1576;&#1575;&#1585; <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>