зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
ad25493719
Коммит
b37c0dbc21
|
@ -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>
|
Загрузка…
Ссылка в новой задаче