зеркало из https://github.com/mozilla/pjs.git
Bug 423355: Never fire mutation events on native-anon content. Also includes fix for nsIContent::IsInNativeAnonymousSubtree. Patch by Smaug. r/sr=sicking a=beltzner
This commit is contained in:
Родитель
48130570fa
Коммит
6dc528af74
|
@ -62,8 +62,8 @@ class nsIDocShell;
|
|||
|
||||
// IID for the nsIContent interface
|
||||
#define NS_ICONTENT_IID \
|
||||
{ 0x14c78a19, 0xe3a3, 0x4809, \
|
||||
{ 0x8a, 0xee, 0x6f, 0x15, 0x4e, 0x1c, 0x16, 0xb6 } }
|
||||
{ 0x0acd0482, 0x09a2, 0x42fd, \
|
||||
{ 0xb6, 0x1b, 0x95, 0xa2, 0x01, 0x6a, 0x55, 0xd3 } }
|
||||
|
||||
/**
|
||||
* A node of content in a document's content model. This interface
|
||||
|
@ -169,7 +169,25 @@ public:
|
|||
/**
|
||||
* Returns PR_TRUE if |this| or any of its ancestors is native anonymous.
|
||||
*/
|
||||
virtual PRBool IsInNativeAnonymousSubtree() const;
|
||||
PRBool IsInNativeAnonymousSubtree() const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (HasFlag(NODE_IS_IN_ANONYMOUS_SUBTREE)) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
nsIContent* content = GetBindingParent();
|
||||
while (content) {
|
||||
if (content->IsNativeAnonymous()) {
|
||||
NS_ERROR("Element not marked to be in native anonymous subtree!");
|
||||
break;
|
||||
}
|
||||
content = content->GetBindingParent();
|
||||
}
|
||||
return PR_FALSE;
|
||||
#else
|
||||
return HasFlag(NODE_IS_IN_ANONYMOUS_SUBTREE);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the namespace that this element's tag is defined in
|
||||
|
|
|
@ -77,43 +77,45 @@ enum {
|
|||
// NOTE: Should only be used on nsIContent nodes
|
||||
NODE_IS_ANONYMOUS = 0x00000008U,
|
||||
|
||||
NODE_IS_IN_ANONYMOUS_SUBTREE = 0x00000010U,
|
||||
|
||||
// Whether this node may have a frame
|
||||
// NOTE: Should only be used on nsIContent nodes
|
||||
NODE_MAY_HAVE_FRAME = 0x00000010U,
|
||||
NODE_MAY_HAVE_FRAME = 0x00000020U,
|
||||
|
||||
// Forces the XBL code to treat this node as if it were
|
||||
// in the document and therefore should get bindings attached.
|
||||
NODE_FORCE_XBL_BINDINGS = 0x00000020U,
|
||||
NODE_FORCE_XBL_BINDINGS = 0x00000040U,
|
||||
|
||||
// Whether a binding manager may have a pointer to this
|
||||
NODE_MAY_BE_IN_BINDING_MNGR = 0x00000040U,
|
||||
NODE_MAY_BE_IN_BINDING_MNGR = 0x00000080U,
|
||||
|
||||
NODE_IS_EDITABLE = 0x00000080U,
|
||||
NODE_IS_EDITABLE = 0x00000100U,
|
||||
|
||||
// Optimizations to quickly check whether element may have ID, class or style
|
||||
// attributes. Not all element implementations may use these!
|
||||
NODE_MAY_HAVE_ID = 0x00000100U,
|
||||
NODE_MAY_HAVE_CLASS = 0x00000200U,
|
||||
NODE_MAY_HAVE_STYLE = 0x00000400U,
|
||||
NODE_MAY_HAVE_ID = 0x00000200U,
|
||||
NODE_MAY_HAVE_CLASS = 0x00000400U,
|
||||
NODE_MAY_HAVE_STYLE = 0x00000800U,
|
||||
|
||||
NODE_IS_INSERTION_PARENT = 0x00000800U,
|
||||
NODE_IS_INSERTION_PARENT = 0x00001000U,
|
||||
|
||||
// Node has an :empty or :-moz-only-whitespace selector
|
||||
NODE_HAS_EMPTY_SELECTOR = 0x00001000U,
|
||||
NODE_HAS_EMPTY_SELECTOR = 0x00002000U,
|
||||
|
||||
// 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,
|
||||
NODE_HAS_SLOW_SELECTOR = 0x00004000U,
|
||||
|
||||
// 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,
|
||||
NODE_HAS_EDGE_CHILD_SELECTOR = 0x00008000U,
|
||||
|
||||
// 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,
|
||||
= 0x00010000U,
|
||||
|
||||
NODE_ALL_SELECTOR_FLAGS = NODE_HAS_EMPTY_SELECTOR |
|
||||
NODE_HAS_SLOW_SELECTOR |
|
||||
|
@ -121,7 +123,7 @@ enum {
|
|||
NODE_HAS_SLOW_SELECTOR_NOAPPEND,
|
||||
|
||||
// Four bits for the script-type ID
|
||||
NODE_SCRIPT_TYPE_OFFSET = 16,
|
||||
NODE_SCRIPT_TYPE_OFFSET = 17,
|
||||
|
||||
NODE_SCRIPT_TYPE_SIZE = 4,
|
||||
|
||||
|
|
|
@ -3193,15 +3193,22 @@ nsContentUtils::HasMutationListeners(nsINode* aNode,
|
|||
return PR_FALSE;
|
||||
}
|
||||
|
||||
doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
|
||||
|
||||
// global object will be null for documents that don't have windows.
|
||||
nsCOMPtr<nsPIDOMWindow> window;
|
||||
window = do_QueryInterface(doc->GetScriptGlobalObject());
|
||||
// This relies on nsEventListenerManager::AddEventListener, which sets
|
||||
// all mutation bits when there is a listener for DOMSubtreeModified event.
|
||||
if (window && !window->HasMutationListeners(aType)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
if (aNode->IsNodeOfType(nsINode::eCONTENT) &&
|
||||
static_cast<nsIContent*>(aNode)->IsInNativeAnonymousSubtree()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
doc->MayDispatchMutationEvent(aTargetForSubtreeModified);
|
||||
|
||||
// If we have a window, we can check it for mutation listeners now.
|
||||
nsCOMPtr<nsPIDOMEventTarget> piTarget(do_QueryInterface(window));
|
||||
if (piTarget) {
|
||||
|
|
|
@ -5860,9 +5860,6 @@ nsDocument::MutationEventDispatched(nsINode* aTarget)
|
|||
nsINode* possibleTarget = mSubtreeModifiedTargets[i];
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget);
|
||||
if (content && content->IsInNativeAnonymousSubtree()) {
|
||||
if (realTargets.IndexOf(possibleTarget) == -1) {
|
||||
realTargets.AppendObject(possibleTarget);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -605,7 +605,15 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
nsDataSlots *slots = GetDataSlots();
|
||||
NS_ENSURE_TRUE(slots, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
NS_ASSERTION(IsNativeAnonymous() || !HasFlag(NODE_IS_IN_ANONYMOUS_SUBTREE) ||
|
||||
aBindingParent->IsInNativeAnonymousSubtree(),
|
||||
"Trying to re-bind content from native anonymous subtree to"
|
||||
"non-native anonymous parent!");
|
||||
slots->mBindingParent = aBindingParent; // Weak, so no addref happens.
|
||||
if (IsNativeAnonymous() ||
|
||||
aBindingParent->IsInNativeAnonymousSubtree()) {
|
||||
SetFlags(NODE_IS_IN_ANONYMOUS_SUBTREE);
|
||||
}
|
||||
}
|
||||
|
||||
// Set parent
|
||||
|
|
|
@ -446,19 +446,6 @@ nsIContent::FindFirstNonNativeAnonymous() const
|
|||
return possibleResult;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsIContent::IsInNativeAnonymousSubtree() const
|
||||
{
|
||||
nsIContent* content = GetBindingParent();
|
||||
while (content) {
|
||||
if (content->IsNativeAnonymous()) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
content = content->GetBindingParent();
|
||||
}
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
nsChildContentList::~nsChildContentList()
|
||||
|
@ -2069,6 +2056,15 @@ nsGenericElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
slots->mBindingParent = aBindingParent; // Weak, so no addref happens.
|
||||
}
|
||||
}
|
||||
NS_ASSERTION(!aBindingParent || IsNativeAnonymous() ||
|
||||
!HasFlag(NODE_IS_IN_ANONYMOUS_SUBTREE) ||
|
||||
aBindingParent->IsInNativeAnonymousSubtree(),
|
||||
"Trying to re-bind content from native anonymous subtree to"
|
||||
"non-native anonymous parent!");
|
||||
if (IsNativeAnonymous() ||
|
||||
aBindingParent && aBindingParent->IsInNativeAnonymousSubtree()) {
|
||||
SetFlags(NODE_IS_IN_ANONYMOUS_SUBTREE);
|
||||
}
|
||||
|
||||
PRBool hadForceXBL = HasFlag(NODE_FORCE_XBL_BINDINGS);
|
||||
|
||||
|
@ -2291,13 +2287,11 @@ nsGenericElement::doPreHandleEvent(nsIContent* aContent,
|
|||
|
||||
nsCOMPtr<nsIContent> parent = aContent->GetParent();
|
||||
if (isAnonForEvents) {
|
||||
// Don't propagate mutation events which are dispatched somewhere inside
|
||||
// native anonymous content.
|
||||
if (aVisitor.mEvent->eventStructType == NS_MUTATION_EVENT) {
|
||||
aVisitor.mParentTarget = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If a DOM event is explicitly dispatched using node.dispatchEvent(), then
|
||||
// all the events are allowed even in the native anonymous content..
|
||||
NS_ASSERTION(aVisitor.mEvent->eventStructType != NS_MUTATION_EVENT ||
|
||||
aVisitor.mDOMEvent,
|
||||
"Mutation event dispatched in native anonymous content!?!");
|
||||
aVisitor.mEventTargetAtParent = parent;
|
||||
} else if (parent) {
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->target));
|
||||
|
|
|
@ -519,7 +519,7 @@ nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
|
|||
NS_ASSERTION(window->IsInnerWindow(),
|
||||
"Setting mutation listener bits on outer window?");
|
||||
// If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all
|
||||
// mutations.
|
||||
// mutations. nsContentUtils::HasMutationListeners relies on this.
|
||||
window->SetMutationListeners((aType == NS_MUTATION_SUBTREEMODIFIED) ?
|
||||
kAllMutationBits :
|
||||
MutationBitForEventType(aType));
|
||||
|
|
|
@ -47,6 +47,7 @@ include $(topsrcdir)/config/rules.mk
|
|||
_TEST_FILES = \
|
||||
test_bug238987.html \
|
||||
test_bug288392.html \
|
||||
test_bug328885.html \
|
||||
test_bug336682_1.html \
|
||||
test_bug336682_2.xul \
|
||||
test_bug336682.js \
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=328885
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 328885</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" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=328885">Mozilla Bug 328885</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<input type="text" id="inputelement"
|
||||
style="position: absolute; left: 0px; top: 0px;">
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 328885 **/
|
||||
|
||||
var inputelement = null;
|
||||
var mutationCount = 0;
|
||||
|
||||
function mutationListener(evt) {
|
||||
++mutationCount;
|
||||
}
|
||||
|
||||
function clickTest() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
inputelement.addEventListener("DOMSubtreeModified", mutationListener, false);
|
||||
inputelement.addEventListener("DOMNodeInserted", mutationListener, false);
|
||||
inputelement.addEventListener("DOMNodeRemoved", mutationListener, false);
|
||||
inputelement.addEventListener("DOMNodeRemovedFromDocument", mutationListener, false);
|
||||
inputelement.addEventListener("DOMNodeInsertedIntoDocument", mutationListener, false);
|
||||
inputelement.addEventListener("DOMAttrModified", mutationListener, false);
|
||||
inputelement.addEventListener("DOMCharacterDataModified", mutationListener, false);
|
||||
|
||||
inputelement.addEventListener('click',
|
||||
function(evt) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
ok(evt.originalTarget instanceof HTMLDivElement, "(1) Wrong originalTarget!");
|
||||
ok(evt.originalTarget.parentNode == inputelement, "(2) Wront parent node!");
|
||||
ok(mutationCount == 0, "(3) No mutations should have happened! [" + mutationCount + "]");
|
||||
evt.originalTarget.textContent = "foo";
|
||||
ok(mutationCount == 0, "(4) Mutation listener shouldn't have been called! [" + mutationCount + "]");
|
||||
evt.originalTarget.innerHTML = "foo2";
|
||||
ok(mutationCount == 0, "(5) Mutation listener shouldn't have been called! [" + mutationCount + "]");
|
||||
evt.originalTarget.lastChild.data = "bar";
|
||||
ok(mutationCount == 0, "(6) Mutation listener shouldn't have been called! [" + mutationCount + "]");
|
||||
|
||||
var r = document.createRange();
|
||||
r.selectNodeContents(evt.originalTarget);
|
||||
r.deleteContents();
|
||||
ok(mutationCount == 0, "(7) Mutation listener shouldn't have been called! [" + mutationCount + "]");
|
||||
|
||||
evt.originalTarget.textContent = "foo";
|
||||
ok(mutationCount == 0, "(8) Mutation listener shouldn't have been called! [" + mutationCount + "]");
|
||||
r = document.createRange();
|
||||
r.selectNodeContents(evt.originalTarget);
|
||||
r.extractContents();
|
||||
ok(mutationCount == 0, "(9) Mutation listener shouldn't have been called! [" + mutationCount + "]");
|
||||
|
||||
evt.originalTarget.setAttribute("foo", "bar");
|
||||
ok(mutationCount == 0, "(10) Mutation listener shouldn't have been called! ["+ mutationCount + "]");
|
||||
|
||||
// Same tests with non-native-anononymous element.
|
||||
// mutationCount should be increased by 2 each time, since there is
|
||||
// first a mutation specific event and then DOMSubtreeModified.
|
||||
inputelement.textContent = "foo";
|
||||
ok(mutationCount == 2, "(11) Mutation listener should have been called! [" + mutationCount + "]");
|
||||
inputelement.lastChild.data = "bar";
|
||||
ok(mutationCount == 4, "(12) Mutation listener should have been called! [" + mutationCount + "]");
|
||||
|
||||
r = document.createRange();
|
||||
r.selectNodeContents(inputelement);
|
||||
r.deleteContents();
|
||||
ok(mutationCount == 6, "(13) Mutation listener should have been called! [" + mutationCount + "]");
|
||||
|
||||
inputelement.textContent = "foo";
|
||||
ok(mutationCount == 8, "(14) Mutation listener should have been called! [" + mutationCount + "]");
|
||||
r = document.createRange();
|
||||
r.selectNodeContents(inputelement);
|
||||
r.extractContents();
|
||||
ok(mutationCount == 10, "(15) Mutation listener should have been called! [" + mutationCount + "]");
|
||||
|
||||
inputelement.setAttribute("foo", "bar");
|
||||
ok(mutationCount == 12, "(16) Mutation listener should have been called! ["+ mutationCount + "]");
|
||||
|
||||
// Then try some mixed mutations. The mutation handler of non-native-a
|
||||
inputelement.addEventListener("DOMAttrModified",
|
||||
function (evt2) {
|
||||
evt.originalTarget.setAttribute("foo", "bar" + mutationCount);
|
||||
ok(evt.originalTarget.getAttribute("foo") == "bar" + mutationCount,
|
||||
"(17) Couldn't update the attribute?!?");
|
||||
}
|
||||
, false);
|
||||
inputelement.setAttribute("foo", "");
|
||||
ok(mutationCount == 14, "(18) Mutation listener should have been called! ["+ mutationCount + "]");
|
||||
|
||||
inputelement.textContent = "foo";
|
||||
ok(mutationCount == 16, "(19) Mutation listener should have been called! ["+ mutationCount + "]");
|
||||
inputelement.addEventListener("DOMCharacterDataModified",
|
||||
function (evt2) {
|
||||
evt.originalTarget.textContent = "bar" + mutationCount;
|
||||
}, false);
|
||||
// This one deletes and inserts a new node, then DOMSubtreeModified.
|
||||
inputelement.textContent = "bar";
|
||||
ok(mutationCount == 19, "(20) Mutation listener should have been called! ["+ mutationCount + "]");
|
||||
}
|
||||
,false);
|
||||
var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
utils.sendMouseEvent("mousedown", 5, 5, 0, 1, 0);
|
||||
utils.sendMouseEvent("mouseup", 5, 5, 0, 1, 0);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function doTest() {
|
||||
inputelement = document.getElementById('inputelement');
|
||||
inputelement.focus();
|
||||
setTimeout(clickTest, 100);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(doTest);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
Загрузка…
Ссылка в новой задаче