Fix handling of dynamic changes for advanced CSS selectors (and avoid the cost of doing so in the normal case). b=401291, 75386, 98997, 229915, 404418 (blocking1.9+) r+sr=bzbarsky a=blocking1.9+ on 404418

This commit is contained in:
dbaron%dbaron.org 2008-02-19 06:17:47 +00:00
Родитель ad25493719
Коммит b37c0dbc21
12 изменённых файлов: 686 добавлений и 23 удалений

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

@ -96,8 +96,30 @@ enum {
NODE_IS_INSERTION_PARENT = 0x00000800U,
// Node has an :empty or :-moz-only-whitespace selector
NODE_HAS_EMPTY_SELECTOR = 0x00001000U,
// A child of the node has a selector such that any insertion,
// removal, or appending of children requires restyling the parent.
NODE_HAS_SLOW_SELECTOR = 0x00002000U,
// A child of the node has a :first-child, :-moz-first-node,
// :only-child, :last-child or :-moz-last-node selector.
NODE_HAS_EDGE_CHILD_SELECTOR = 0x00004000U,
// A child of the node has a selector such that any insertion or
// removal of children requires restyling the parent (but append is
// OK).
NODE_HAS_SLOW_SELECTOR_NOAPPEND
= 0x00008000U,
NODE_ALL_SELECTOR_FLAGS = NODE_HAS_EMPTY_SELECTOR |
NODE_HAS_SLOW_SELECTOR |
NODE_HAS_EDGE_CHILD_SELECTOR |
NODE_HAS_SLOW_SELECTOR_NOAPPEND,
// Four bits for the script-type ID
NODE_SCRIPT_TYPE_OFFSET = 12,
NODE_SCRIPT_TYPE_OFFSET = 16,
NODE_SCRIPT_TYPE_SIZE = 4,

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

@ -120,6 +120,7 @@
#include "nsContentErrors.h"
#include "nsIPrincipal.h"
#include "nsIDOMWindowInternal.h"
#include "nsStyleUtil.h"
#include "nsBox.h"
@ -152,7 +153,6 @@ NS_NewHTMLCanvasFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
#ifdef MOZ_SVG
#include "nsISVGTextContentMetrics.h"
#include "nsStyleUtil.h"
PRBool
NS_SVGEnabled();
@ -13123,6 +13123,207 @@ nsresult nsCSSFrameConstructor::RemoveFixedItems(const nsFrameConstructorState&
return rv;
}
void
nsCSSFrameConstructor::RestyleForAppend(nsIContent* aContainer,
PRInt32 aNewIndexInContainer)
{
NS_ASSERTION(aContainer, "must have container for append");
PRUint32 selectorFlags =
aContainer->GetFlags() & (NODE_ALL_SELECTOR_FLAGS &
~NODE_HAS_SLOW_SELECTOR_NOAPPEND);
if (selectorFlags == 0)
return;
if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
PostRestyleEvent(aContainer, eReStyle_Self, NS_STYLE_HINT_NONE);
// Restyling the container is the most we can do here, so we're done.
return;
}
if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
// see whether we need to restyle the container
PRBool wasEmpty = PR_TRUE; // :empty or :-moz-only-whitespace
for (PRInt32 index = 0; index < aNewIndexInContainer; ++index) {
// We don't know whether we're testing :empty or :-moz-only-whitespace,
// so be conservative and assume :-moz-only-whitespace (i.e., make
// IsSignificantChild less likely to be true, and thus make us more
// likely to restyle).
if (nsStyleUtil::IsSignificantChild(aContainer->GetChildAt(index),
PR_TRUE, PR_FALSE)) {
wasEmpty = PR_FALSE;
break;
}
}
if (wasEmpty) {
PostRestyleEvent(aContainer, eReStyle_Self, NS_STYLE_HINT_NONE);
// Restyling the container is the most we can do here, so we're done.
return;
}
}
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
// restyle the last element child before this node
for (PRInt32 index = aNewIndexInContainer - 1; index >= 0; --index) {
nsIContent *content = aContainer->GetChildAt(index);
if (content->IsNodeOfType(nsINode::eELEMENT)) {
PostRestyleEvent(content, eReStyle_Self, NS_STYLE_HINT_NONE);
break;
}
}
}
}
// Restyling for a ContentInserted or CharacterDataChanged notification.
// This could be used for ContentRemoved as well if we got the
// notification before the removal happened (and sometimes
// CharacterDataChanged is more like a removal than an addition).
// The comments are written and variables are named in terms of it being
// a ContentInserted notification.
void
nsCSSFrameConstructor::RestyleForInsertOrChange(nsIContent* aContainer,
nsIContent* aChild)
{
PRUint32 selectorFlags =
aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
if (selectorFlags == 0)
return;
if (selectorFlags & (NODE_HAS_SLOW_SELECTOR |
NODE_HAS_SLOW_SELECTOR_NOAPPEND)) {
PostRestyleEvent(aContainer, eReStyle_Self, NS_STYLE_HINT_NONE);
// Restyling the container is the most we can do here, so we're done.
return;
}
if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
// see whether we need to restyle the container
PRBool wasEmpty = PR_TRUE; // :empty or :-moz-only-whitespace
for (PRInt32 index = 0; ; ++index) {
nsIContent *child = aContainer->GetChildAt(index);
if (!child) // last child
break;
if (child == aChild)
continue;
// We don't know whether we're testing :empty or :-moz-only-whitespace,
// so be conservative and assume :-moz-only-whitespace (i.e., make
// IsSignificantChild less likely to be true, and thus make us more
// likely to restyle).
if (nsStyleUtil::IsSignificantChild(child, PR_TRUE, PR_FALSE)) {
wasEmpty = PR_FALSE;
break;
}
}
if (wasEmpty) {
PostRestyleEvent(aContainer, eReStyle_Self, NS_STYLE_HINT_NONE);
// Restyling the container is the most we can do here, so we're done.
return;
}
}
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
// restyle the previously-first element child if it is after this node
PRBool passedChild = PR_FALSE;
for (PRInt32 index = 0; ; ++index) {
nsIContent *content = aContainer->GetChildAt(index);
if (!content)
break; // went through all children
if (content == aChild) {
passedChild = PR_TRUE;
continue;
}
if (content->IsNodeOfType(nsINode::eELEMENT)) {
if (passedChild) {
PostRestyleEvent(content, eReStyle_Self, NS_STYLE_HINT_NONE);
}
break;
}
}
// restyle the previously-last element child if it is before this node
passedChild = PR_FALSE;
for (PRInt32 index = aContainer->GetChildCount() - 1;
index >= 0; --index) {
nsIContent *content = aContainer->GetChildAt(index);
if (content == aChild) {
passedChild = PR_TRUE;
continue;
}
if (content->IsNodeOfType(nsINode::eELEMENT)) {
if (passedChild) {
PostRestyleEvent(content, eReStyle_Self, NS_STYLE_HINT_NONE);
}
break;
}
}
}
}
void
nsCSSFrameConstructor::RestyleForRemove(nsIContent* aContainer,
nsIContent* aOldChild,
PRInt32 aIndexInContainer)
{
PRUint32 selectorFlags =
aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
if (selectorFlags == 0)
return;
if (selectorFlags & (NODE_HAS_SLOW_SELECTOR |
NODE_HAS_SLOW_SELECTOR_NOAPPEND)) {
PostRestyleEvent(aContainer, eReStyle_Self, NS_STYLE_HINT_NONE);
// Restyling the container is the most we can do here, so we're done.
return;
}
if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
// see whether we need to restyle the container
PRBool isEmpty = PR_TRUE; // :empty or :-moz-only-whitespace
for (PRInt32 index = 0; ; ++index) {
nsIContent *child = aContainer->GetChildAt(index);
if (!child) // last child
break;
// We don't know whether we're testing :empty or :-moz-only-whitespace,
// so be conservative and assume :-moz-only-whitespace (i.e., make
// IsSignificantChild less likely to be true, and thus make us more
// likely to restyle).
if (nsStyleUtil::IsSignificantChild(child, PR_TRUE, PR_FALSE)) {
isEmpty = PR_FALSE;
break;
}
}
if (isEmpty) {
PostRestyleEvent(aContainer, eReStyle_Self, NS_STYLE_HINT_NONE);
// Restyling the container is the most we can do here, so we're done.
return;
}
}
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
// restyle the previously-first element child if it is after aOldChild
for (PRInt32 index = 0; ; ++index) {
nsIContent *content = aContainer->GetChildAt(index);
if (!content)
break; // went through all children
if (content->IsNodeOfType(nsINode::eELEMENT)) {
if (index >= aIndexInContainer) {
PostRestyleEvent(content, eReStyle_Self, NS_STYLE_HINT_NONE);
}
break;
}
}
// restyle the previously-last element child if it is before aOldChild
for (PRInt32 index = aContainer->GetChildCount() - 1;
index >= 0; --index) {
nsIContent *content = aContainer->GetChildAt(index);
if (content->IsNodeOfType(nsINode::eELEMENT)) {
if (index < aIndexInContainer) {
PostRestyleEvent(content, eReStyle_Self, NS_STYLE_HINT_NONE);
}
break;
}
}
}
}
PR_STATIC_CALLBACK(PLDHashOperator)
CollectRestyles(nsISupports* aContent,
nsCSSFrameConstructor::RestyleData& aData,

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

@ -171,6 +171,22 @@ private:
nsChangeHint aChangeHint);
public:
// Restyling for a ContentInserted (notification after insertion) or
// for a CharacterDataChanged. |aContainer| must be non-null; when
// the container is null, no work is needed.
void RestyleForInsertOrChange(nsIContent* aContainer,
nsIContent* aChild);
// This would be the same as RestyleForInsertOrChange if we got the
// notification before the removal. However, we get it after, so we
// have to use the index. |aContainer| must be non-null; when the
// container is null, no work is needed.
void RestyleForRemove(nsIContent* aContainer, nsIContent* aOldChild,
PRInt32 aIndexInContainer);
// Same for a ContentAppended. |aContainer| must be non-null; when
// the container is null, no work is needed.
void RestyleForAppend(nsIContent* aContainer,
PRInt32 aNewIndexInContainer);
// Note: It's the caller's responsibility to make sure to wrap a
// ProcessPendingRestyles call in a view update batch.
// This function does not call ProcessAttachedQueue() on the binding manager.

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

@ -4553,6 +4553,23 @@ PresShell::CharacterDataChanged(nsIDocument *aDocument,
// frame to the caret.
mCaret->InvalidateOutsideCaret();
}
// Call this here so it only happens for real content mutations and
// not cases when the frame constructor calls its own methods to force
// frame reconstruction.
nsIContent *container = aContent->GetParent();
PRUint32 selectorFlags =
container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
if (selectorFlags != 0) {
PRUint32 index;
if (aInfo->mAppend &&
container->GetChildAt((index = container->GetChildCount() - 1)) ==
aContent)
mFrameConstructor->RestyleForAppend(container, index);
else
mFrameConstructor->RestyleForInsertOrChange(container, aContent);
}
mFrameConstructor->CharacterDataChanged(aContent, aInfo->mAppend);
VERIFY_STYLE_TREE;
DidCauseReflow();
@ -4606,6 +4623,7 @@ PresShell::ContentAppended(nsIDocument *aDocument,
{
NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentAppended");
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
NS_PRECONDITION(aContainer, "must have container");
if (!mDidInitialReflow) {
return;
@ -4615,6 +4633,11 @@ PresShell::ContentAppended(nsIDocument *aDocument,
MOZ_TIMER_DEBUGLOG(("Start: Frame Creation: PresShell::ContentAppended(), this=%p\n", this));
MOZ_TIMER_START(mFrameCreationWatch);
// Call this here so it only happens for real content mutations and
// not cases when the frame constructor calls its own methods to force
// frame reconstruction.
mFrameConstructor->RestyleForAppend(aContainer, aNewIndexInContainer);
mFrameConstructor->ContentAppended(aContainer, aNewIndexInContainer);
VERIFY_STYLE_TREE;
@ -4637,6 +4660,13 @@ PresShell::ContentInserted(nsIDocument* aDocument,
}
WillCauseReflow();
// Call this here so it only happens for real content mutations and
// not cases when the frame constructor calls its own methods to force
// frame reconstruction.
if (aContainer)
mFrameConstructor->RestyleForInsertOrChange(aContainer, aChild);
mFrameConstructor->ContentInserted(aContainer, aChild,
aIndexInContainer, nsnull);
VERIFY_STYLE_TREE;
@ -4662,6 +4692,13 @@ PresShell::ContentRemoved(nsIDocument *aDocument,
mPresContext->EventStateManager()->ContentRemoved(aChild);
WillCauseReflow();
// Call this here so it only happens for real content mutations and
// not cases when the frame constructor calls its own methods to force
// frame reconstruction.
if (aContainer)
mFrameConstructor->RestyleForRemove(aContainer, aChild, aIndexInContainer);
PRBool didReconstruct;
mFrameConstructor->ContentRemoved(aContainer, aChild,
aIndexInContainer, &didReconstruct);

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

@ -986,21 +986,12 @@ inline PRBool IsQuirkEventSensitive(nsIAtom *aContentTag)
}
static PRBool IsSignificantChild(nsIContent* aChild, PRBool aTextIsSignificant, PRBool aWhitespaceIsSignificant)
static inline PRBool
IsSignificantChild(nsIContent* aChild, PRBool aTextIsSignificant,
PRBool aWhitespaceIsSignificant)
{
NS_ASSERTION(!aWhitespaceIsSignificant || aTextIsSignificant,
"Nonsensical arguments");
PRBool isText = aChild->IsNodeOfType(nsINode::eTEXT);
if (!isText && !aChild->IsNodeOfType(nsINode::eCOMMENT) &&
!aChild->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
return PR_TRUE;
}
return aTextIsSignificant && isText && aChild->TextLength() != 0 &&
(aWhitespaceIsSignificant ||
!aChild->TextIsOnlyWhitespace());
return nsStyleUtil::IsSignificantChild(aChild, aTextIsSignificant,
aWhitespaceIsSignificant);
}
// This function is to be called once we have fetched a value for an attribute
@ -1064,6 +1055,12 @@ static PRBool SelectorMatches(RuleProcessorData &data,
PRBool result = PR_TRUE;
const PRBool isNegated = (aDependence != nsnull);
// The selectors for which we set node bits are, unfortunately, early
// in this function (because they're pseudo-classes, which are
// generally quick to test, and thus earlier). If they were later,
// we'd probably avoid setting those bits in more cases where setting
// them is unnecessary.
const PRBool setNodeFlags = aStateMask == 0 && !aAttribute;
// test for pseudo class match
// first-child, root, lang, active, focus, hover, link, visited...
@ -1076,6 +1073,9 @@ static PRBool SelectorMatches(RuleProcessorData &data,
nsIContent *firstChild = nsnull;
nsIContent *parent = data.mParentContent;
if (parent) {
if (setNodeFlags)
parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
PRBool acceptNonWhitespace =
nsCSSPseudoClasses::firstNode == pseudoClass->mAtom;
PRInt32 index = -1;
@ -1093,6 +1093,9 @@ static PRBool SelectorMatches(RuleProcessorData &data,
nsIContent *lastChild = nsnull;
nsIContent *parent = data.mParentContent;
if (parent) {
if (setNodeFlags)
parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
PRBool acceptNonWhitespace =
nsCSSPseudoClasses::lastNode == pseudoClass->mAtom;
PRUint32 index = parent->GetChildCount();
@ -1110,6 +1113,9 @@ static PRBool SelectorMatches(RuleProcessorData &data,
nsIContent *moreChild = nsnull;
nsIContent *parent = data.mParentContent;
if (parent) {
if (setNodeFlags)
parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
PRInt32 index = -1;
do {
onlyChild = parent->GetChildAt(++index);
@ -1133,6 +1139,9 @@ static PRBool SelectorMatches(RuleProcessorData &data,
nsCSSPseudoClasses::empty == pseudoClass->mAtom;
PRInt32 index = -1;
if (setNodeFlags)
element->SetFlags(NODE_HAS_EMPTY_SELECTOR);
do {
child = element->GetChildAt(++index);
// stop at first non-comment (and non-whitespace for
@ -1146,6 +1155,9 @@ static PRBool SelectorMatches(RuleProcessorData &data,
nsIContent *element = data.mContent;
PRInt32 index = -1;
if (setNodeFlags)
element->SetFlags(NODE_HAS_SLOW_SELECTOR);
do {
child = element->GetChildAt(++index);
} while (child &&
@ -1566,6 +1578,8 @@ static PRBool SelectorMatchesTree(RuleProcessorData& aPrevData,
nsIContent* content = prevdata->mContent;
nsIContent* parent = content->GetParent();
if (parent) {
parent->SetFlags(NODE_HAS_SLOW_SELECTOR_NOAPPEND);
PRInt32 index = parent->IndexOf(content);
while (0 <= --index) {
content = parent->GetChildAt(index);

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

@ -568,3 +568,23 @@ nsStyleUtil::ColorComponentToFloat(PRUint8 aAlpha)
}
return rounded;
}
/* static */ PRBool
nsStyleUtil::IsSignificantChild(nsIContent* aChild, PRBool aTextIsSignificant,
PRBool aWhitespaceIsSignificant)
{
NS_ASSERTION(!aWhitespaceIsSignificant || aTextIsSignificant,
"Nonsensical arguments");
PRBool isText = aChild->IsNodeOfType(nsINode::eTEXT);
if (!isText && !aChild->IsNodeOfType(nsINode::eCOMMENT) &&
!aChild->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
return PR_TRUE;
}
return aTextIsSignificant && isText && aChild->TextLength() != 0 &&
(aWhitespaceIsSignificant ||
!aChild->TextIsOnlyWhitespace());
}

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

@ -97,6 +97,12 @@ public:
*/
static float ColorComponentToFloat(PRUint8 aAlpha);
/*
* Does this child count as significant for selector matching?
*/
static PRBool IsSignificantChild(nsIContent* aChild,
PRBool aTextIsSignificant,
PRBool aWhitespaceIsSignificant);
};

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

@ -68,8 +68,11 @@ css_properties.js: host_ListCSSProperties$(HOST_BIN_SUFFIX) css_properties_like_
GARBAGE += css_properties.js
_TEST_FILES = test_bug74880.html \
_TEST_FILES = test_bug73586.html \
test_bug74880.html \
test_bug98997.html \
test_bug221428.html \
test_bug229915.html \
test_bug302186.html \
test_bug319381.html \
test_bug357614.html \

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

@ -0,0 +1,96 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=229915
-->
<head>
<title>Test for Bug 229915</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<style type="text/css">
p { color: black; background: transparent; }
p.prev + p { color: green; }
p.prev ~ p { background: white; }
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=229915">Mozilla Bug 229915</a>
<div id="display">
<div>
<p id="toinsertbefore">After testing, this should turn green.</p>
</div>
<div>
<p id="toreplace">To be replaced.</p>
<p id="replacecolor">After testing, this should turn green.</p>
</div>
<div>
<p class="prev">Previous paragraph.</p>
<p id="toremove">To be removed.</p>
<p id="removecolor">After testing, this should turn green.</p>
</div>
</div>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 229915 **/
const GREEN = "rgb(0, 128, 0)";
const BLACK = "rgb(0, 0, 0)";
const TRANSPARENT = "transparent";
const WHITE = "rgb(255, 255, 255)";
function make_prev() {
var result = document.createElement("p");
result.setAttribute("class", "prev");
var t = document.createTextNode("Dynamically created previous paragraph.");
result.appendChild(t);
return result;
}
function color(id) {
return getComputedStyle(document.getElementById(id), "").color;
}
function bg(id) {
return getComputedStyle(document.getElementById(id), "").backgroundColor;
}
var node;
// test insert
is(color("toinsertbefore"), BLACK, "initial state (insertion test)");
is(bg("toinsertbefore"), TRANSPARENT, "initial state (insertion test)");
node = document.getElementById("toinsertbefore");
node.parentNode.insertBefore(make_prev(), node);
is(color("toinsertbefore"), GREEN, "inserting should turn node green");
is(bg("toinsertbefore"), WHITE, "inserting should turn background white");
// test replace
is(color("replacecolor"), BLACK, "initial state (replacement test)");
is(bg("replacecolor"), TRANSPARENT, "initial state (replacement test)");
node = document.getElementById("toreplace");
node.parentNode.replaceChild(make_prev(), node);
is(color("replacecolor"), GREEN, "replacing should turn node green");
is(bg("replacecolor"), WHITE, "replacing should turn background white");
// test remove
is(color("removecolor"), BLACK, "initial state (removal test)");
is(bg("removecolor"), WHITE, "initial state (removal test; no change)");
node = document.getElementById("toremove");
node.parentNode.removeChild(node);
is(color("removecolor"), GREEN, "removing should turn node green");
is(bg("removecolor"), WHITE, "removing should leave background");
</script>
</pre>
</body>
</html>

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

@ -359,7 +359,7 @@ function dynamicDefault6() {
var but = document.createElement("input");
but.setAttribute("type", "submit");
$('div6').insertBefore(but, $('div6').firstChild);
todo(idColor("6a") == "rgb(0,128,0)", "CSS dynamic-default 6a");
is(idColor("6a"),"rgb(0,128,0)", "CSS dynamic-default 6a");
is(idColor("6b"),"rgb(0,128,0)", "CSS dynamic-default 6b");
}
@ -375,7 +375,7 @@ function dynamicDefault8() {
var but = document.createElement("input");
but.setAttribute("type", "image");
$('div8').insertBefore(but, $('div8').firstChild);
todo(idColor("8a") == "rgb(0,128,0)", "CSS dynamic-default 8a");
is(idColor("8a"),"rgb(0,128,0)", "CSS dynamic-default 8a");
is(idColor("8b"),"rgb(0,128,0)", "CSS dynamic-default 8b");
}
@ -390,7 +390,7 @@ function dynamicDefault9() {
function dynamicDefault10() {
var inputs = $('div10').getElementsByTagName("input");
$('div10').removeChild(inputs[0]);
todo(idColor("10a") == "rgb(0,128,0)", "CSS dynamic-default 10a");
is(idColor("10a"),"rgb(0,128,0)", "CSS dynamic-default 10a");
is(idColor("10b"),"rgb(0,128,0)", "CSS dynamic-default 10b");
}
@ -404,7 +404,7 @@ function dynamicDefault11() {
function dynamicDefault12() {
var inputs = $('div12').getElementsByTagName("input");
$('div12').removeChild(inputs[0]);
todo(idColor("12a") == "rgb(0,128,0)", "CSS dynamic-default 12a");
is(idColor("12a"),"rgb(0,128,0)", "CSS dynamic-default 12a");
is(idColor("12b"),"rgb(0,128,0)", "CSS dynamic-default 12b");
}
@ -464,7 +464,7 @@ function dynamicDefault19() {
newSubmit.setAttribute("type", "submit");
var div1 = document.getElementById("div19");
div1.insertBefore(newSubmit, div1.firstChild);
todo(idColor("19a") == "rgb(0,128,0)", "CSS dynamic-default 19a");
is(idColor("19a"),"rgb(0,128,0)", "CSS dynamic-default 19a");
}
function dynamicDefault20() {
@ -472,7 +472,7 @@ function dynamicDefault20() {
newSubmit.setAttribute("type", "image");
var div1 = document.getElementById("div20");
div1.insertBefore(newSubmit, div1.firstChild);
todo(idColor("20a") == "rgb(0,128,0)", "CSS dynamic-default 20a");
is(idColor("20a"),"rgb(0,128,0)", "CSS dynamic-default 20a");
}
addLoadEvent(dynamicDefault1);

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

@ -0,0 +1,103 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=73586
-->
<head>
<title>Test for Bug 73586</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<style type="text/css">
span { background: white; color: black; border: medium solid black; }
span:first-child { background: lime; }
span:last-child { color: green; }
span:only-child { border: medium solid green; }
span:-moz-first-node { text-decoration: underline; }
span:-moz-last-node { visibility: hidden; }
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=73586">Mozilla Bug 73586</a>
<p id="display">x<span></span><span></span></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 73586 **/
const GREEN = "rgb(0, 128, 0)";
const LIME = "rgb(0, 255, 0)";
const BLACK = "rgb(0, 0, 0)";
const WHITE = "rgb(255, 255, 255)";
var p = document.getElementById("display");
function cs(elt) { return getComputedStyle(elt, ""); }
function check_children() {
var len = p.childNodes.length;
var elts = 0;
var i, elt, child;
for (i = 0; i < len; ++i) {
if (p.childNodes[i].nodeType == Node.ELEMENT_NODE)
++elts;
}
elt = 0;
for (i = 0; i < len; ++i) {
child = p.childNodes[i];
if (child.NodeType != Node.ELEMENT_NODE)
continue;
is(cs(child).backgroundColor, (elt == 0) ? LIME : WHITE,
"child " + i + " should " + ((elt == 0) ? "" : "NOT ") +
" match :first-child");
is(cs(child).color, (elt == elts - 1) ? GREEN : BLACK,
"child " + i + " should " + ((elt == elts - 1) ? "" : "NOT ") +
" match :last-child");
is(cs(child).borderTopColor, (elts == 1) ? GREEN : BLACK,
"child " + i + " should " + ((elts == 1) ? "" : "NOT ") +
" match :only-child");
is(cs(child).textDecoration, (i == 0) ? "underline" : "none",
"child " + i + " should " + ((i == 0) ? "" : "NOT ") +
" match :-moz-first-node");
is(cs(child).visibilty, (i == len - 1) ? "hidden" : "visible",
"child " + i + " should " + ((i == len - 1) ? "" : "NOT ") +
" match :-moz-last-node");
++elt;
}
}
check_children();
var text = p.removeChild(p.childNodes[0]);
check_children();
var span = p.removeChild(p.childNodes[0]);
check_children();
p.appendChild(span);
check_children();
p.removeChild(span);
check_children();
p.insertBefore(span, p.childNodes[0]);
check_children();
p.removeChild(span);
check_children();
p.insertBefore(span, null);
check_children();
p.appendChild(document.createElement("span"));
check_children();
p.insertBefore(document.createElement("span"), p.childNodes[2]);
check_children();
p.appendChild(text);
check_children();
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,145 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=98997
-->
<head>
<title>Test for Bug 98997</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<style type="text/css">
/*
* This test does NOT test any of the cases where :empty and
* :-moz-only-whitespace differ. We should probably have some tests
* for that as well.
*/
div.test { width: 200px; height: 30px; margin: 5px 0; }
div.test.to, div.test.from:empty { background: orange; }
div.test.to:empty, div.test.from { background: green; }
div.test.to, div.test.from:-moz-only-whitespace { color: maroon; }
div.test.to:-moz-only-whitespace, div.test.from { color: navy; }
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=98997">Mozilla Bug 98997</a>
<div id="display">
<div class="test to" onclick="testReplaceChild(this, '')">x</div>
<div class="test to" onclick="testReplaceChild(this, '')"><span>x</span></div>
<div class="test to" onclick="testReplaceChild(this, '')">x<!-- comment --></div>
<div class="test to" onclick="testRemoveChild(this)">x</div>
<div class="test to" onclick="testRemoveChild(this)"><span>x</span></div>
<div class="test to" onclick="testRemoveChild(this)">x<!-- comment --></div>
<div class="test to" onclick="testChangeData(this, '')">x</div>
<div class="test to" onclick="testChangeData(this, '')">x<!-- comment --></div>
<div class="test to" onclick="testDeleteData(this)">x</div>
<div class="test to" onclick="testDeleteData(this)">x<!-- comment --></div>
<div class="test to" onclick="testReplaceData(this, '')">x</div>
<div class="test to" onclick="testReplaceData(this, '')">x<!-- comment --></div>
<div class="test from makeemptytext" onclick="testReplaceChild(this, 'x')"></div>
<div class="test from makeemptytext" onclick="testReplaceChild(this, 'x')"><!-- comment --></div>
<div class="test from" onclick="testReplaceChild(this, 'x')"><!-- comment --></div>
<div class="test from" onclick="testInsertBefore(this, 'x')"></div>
<div class="test from" onclick="testInsertBefore(this, 'x')"><!-- comment --></div>
<div class="test from" onclick="testAppendChild(this, 'x')"></div>
<div class="test from" onclick="testAppendChild(this, 'x')"><!-- comment --></div>
<div class="test from makeemptytext" onclick="testChangeData(this, 'x')"></div>
<div class="test from makeemptytext" onclick="testChangeData(this, 'x')"><!-- comment --></div>
<div class="test from makeemptytext" onclick="testAppendData(this, 'x')"></div>
<div class="test from makeemptytext" onclick="testAppendData(this, 'x')"><!-- comment --></div>
<div class="test from makeemptytext" onclick="testReplaceData(this, 'x')"></div>
<div class="test from makeemptytext" onclick="testReplaceData(this, 'x')"><!-- comment --></div>
</div>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 98997 **/
function testInsertBefore(elt, text) {
elt.insertBefore(document.createTextNode(text), elt.firstChild);
}
function testAppendChild(elt, text) {
elt.appendChild(document.createTextNode(text));
}
function testReplaceChild(elt, text) {
elt.replaceChild(document.createTextNode(text), elt.firstChild);
}
function testRemoveChild(elt) {
elt.removeChild(elt.firstChild);
}
function testChangeData(elt, text) {
elt.firstChild.data = text;
}
function testAppendData(elt, text) {
elt.firstChild.appendData(text);
}
function testDeleteData(elt) {
elt.firstChild.deleteData(0, elt.firstChild.length);
}
function testReplaceData(elt, text) {
elt.firstChild.replaceData(0, elt.firstChild.length, text);
}
var cnodes = document.getElementById("display").childNodes;
var divs = [];
var i;
for (i = 0; i < cnodes.length; ++i) {
if (cnodes[i].nodeName == "DIV")
divs.push(cnodes[i]);
}
for (i in divs) {
var div = divs[i];
if (div.className.match(/makeemptytext/))
div.insertBefore(document.createTextNode(""), div.firstChild);
}
const ORANGE = "rgb(255, 165, 0)";
const MAROON = "rgb(128, 0, 0)";
const GREEN = "rgb(0, 128, 0)";
const NAVY = "rgb(0, 0, 128)";
function color(div) {
return getComputedStyle(div, "").color;
}
function bg(div) {
return getComputedStyle(div, "").backgroundColor;
}
for (i in divs) {
var div = divs[i];
is(bg(div), ORANGE, "should be orange");
is(color(div), MAROON, "should be maroon");
}
for (i in divs) {
var div = divs[i];
var e = document.createEvent("MouseEvents");
e.initEvent("click", true, true);
div.dispatchEvent(e);
}
for (i in divs) {
var div = divs[i];
is(bg(div), GREEN, "should be green");
is(color(div), NAVY, "should be navy");
}
</script>
</pre>
</body>
</html>