Bug 895974. Implement ParentNode on document fragments and documents and move previousElementSibling and nextElementSibling to ChildNode. r=smaug

This commit is contained in:
Boris Zbarsky 2013-07-22 08:15:43 -04:00
parent c3d0e79b5c
commit 0d417e5c5f
16 changed files with 203 additions and 76 deletions

View File

@ -38,7 +38,6 @@
#include "nsEvent.h"
#include "nsAttrValue.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "nsIHTMLCollection.h"
#include "Units.h"
class nsIDOMEventListener;
@ -600,36 +599,6 @@ public:
ErrorResult& aError);
already_AddRefed<nsIHTMLCollection>
GetElementsByClassName(const nsAString& aClassNames);
Element* GetFirstElementChild() const;
Element* GetLastElementChild() const;
Element* GetPreviousElementSibling() const
{
nsIContent* previousSibling = GetPreviousSibling();
while (previousSibling) {
if (previousSibling->IsElement()) {
return previousSibling->AsElement();
}
previousSibling = previousSibling->GetPreviousSibling();
}
return nullptr;
}
Element* GetNextElementSibling() const
{
nsIContent* nextSibling = GetNextSibling();
while (nextSibling) {
if (nextSibling->IsElement()) {
return nextSibling->AsElement();
}
nextSibling = nextSibling->GetNextSibling();
}
return nullptr;
}
uint32_t ChildElementCount()
{
return Children()->Length();
}
bool MozMatchesSelector(const nsAString& aSelector,
ErrorResult& aError);
void SetCapture(bool aRetargetToElement)

View File

@ -23,6 +23,7 @@
#include "nsINodeList.h" // base class
#include "nsIWeakReference.h" // base class
#include "nsNodeUtils.h" // class member nsNodeUtils::CloneNodeImpl
#include "nsIHTMLCollection.h"
class ContentUnbinder;
class nsContentList;
@ -32,7 +33,6 @@ class nsIControllers;
class nsICSSDeclaration;
class nsIDocument;
class nsDOMStringMap;
class nsIHTMLCollection;
class nsINodeInfo;
class nsIURI;
@ -229,6 +229,10 @@ public:
NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) MOZ_OVERRIDE;
nsIHTMLCollection* Children();
uint32_t ChildElementCount()
{
return Children()->Length();
}
public:
/**

View File

@ -53,6 +53,7 @@ class nsIDOMElement;
class nsIDOMNodeList;
class nsIDOMXPathExpression;
class nsIDOMXPathNSResolver;
class nsIHTMLCollection;
class nsILayoutHistoryState;
class nsIObjectLoadingContent;
class nsIObserver;
@ -2120,6 +2121,10 @@ public:
void ObsoleteSheet(const nsAString& aSheetURI, mozilla::ErrorResult& rv);
// ParentNode
nsIHTMLCollection* Children();
uint32_t ChildElementCount();
virtual nsHTMLDocument* AsHTMLDocument() { return nullptr; }
virtual JSObject* WrapObject(JSContext *aCx,
@ -2211,6 +2216,9 @@ protected:
nsPropertyTable mPropertyTable;
nsTArray<nsAutoPtr<nsPropertyTable> > mExtraPropertyTables;
// Our cached .children collection
nsCOMPtr<nsIHTMLCollection> mChildrenCollection;
// Compatibility mode
nsCompatibility mCompatMode;

View File

@ -1590,11 +1590,18 @@ public:
return rv.ErrorCode();
}
// ChildNode methods
mozilla::dom::Element* GetPreviousElementSibling() const;
mozilla::dom::Element* GetNextElementSibling() const;
/**
* Remove this node from its parent, if any.
*/
void Remove();
// ParentNode methods
mozilla::dom::Element* GetFirstElementChild() const;
mozilla::dom::Element* GetLastElementChild() const;
protected:
// Override this function to create a custom slots class.

View File

@ -416,34 +416,6 @@ Element::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aScope)
return obj;
}
Element*
Element::GetFirstElementChild() const
{
uint32_t i, count = mAttrsAndChildren.ChildCount();
for (i = 0; i < count; ++i) {
nsIContent* child = mAttrsAndChildren.ChildAt(i);
if (child->IsElement()) {
return child->AsElement();
}
}
return nullptr;
}
Element*
Element::GetLastElementChild() const
{
uint32_t i = mAttrsAndChildren.ChildCount();
while (i > 0) {
nsIContent* child = mAttrsAndChildren.ChildAt(--i);
if (child->IsElement()) {
return child->AsElement();
}
}
return nullptr;
}
nsDOMTokenList*
Element::GetClassList()
{

View File

@ -1793,6 +1793,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
// Traverse all our nsCOMArrays.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
@ -1877,6 +1878,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
tmp->mParentDocument = nullptr;
@ -9475,6 +9477,25 @@ nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv)
}
}
nsIHTMLCollection*
nsIDocument::Children()
{
if (!mChildrenCollection) {
mChildrenCollection = new nsContentList(this, kNameSpaceID_Wildcard,
nsGkAtoms::_asterix,
nsGkAtoms::_asterix,
false);
}
return mChildrenCollection;
}
uint32_t
nsIDocument::ChildElementCount()
{
return Children()->Length();
}
namespace mozilla {
// Singleton class to manage the list of fullscreen documents which are the

View File

@ -1404,6 +1404,34 @@ nsINode::doInsertChildAt(nsIContent* aKid, uint32_t aIndex,
return NS_OK;
}
Element*
nsINode::GetPreviousElementSibling() const
{
nsIContent* previousSibling = GetPreviousSibling();
while (previousSibling) {
if (previousSibling->IsElement()) {
return previousSibling->AsElement();
}
previousSibling = previousSibling->GetPreviousSibling();
}
return nullptr;
}
Element*
nsINode::GetNextElementSibling() const
{
nsIContent* nextSibling = GetNextSibling();
while (nextSibling) {
if (nextSibling->IsElement()) {
return nextSibling->AsElement();
}
nextSibling = nextSibling->GetNextSibling();
}
return nullptr;
}
void
nsINode::Remove()
{
@ -1419,6 +1447,34 @@ nsINode::Remove()
parent->RemoveChildAt(uint32_t(index), true);
}
Element*
nsINode::GetFirstElementChild() const
{
for (nsIContent* child = GetFirstChild();
child;
child = child->GetNextSibling()) {
if (child->IsElement()) {
return child->AsElement();
}
}
return nullptr;
}
Element*
nsINode::GetLastElementChild() const
{
for (nsIContent* child = GetLastChild();
child;
child = child->GetPreviousSibling()) {
if (child->IsElement()) {
return child->AsElement();
}
}
return nullptr;
}
void
nsINode::doRemoveChildAt(uint32_t aIndex, bool aNotify,
nsIContent* aKid, nsAttrAndChildArray& aChildArray)

View File

@ -249,6 +249,7 @@ MOCHITEST_FILES_A = \
test_bug820909.html \
test_bug704063.html \
test_bug894874.html \
test_bug895974.html \
$(NULL)
MOCHITEST_FILES_B = \

View File

@ -0,0 +1,69 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=895974
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 895974</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 895974 **/
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
var frag = document.createDocumentFragment();
var span = document.createElement("span");
var div = document.createElement("div");
var text = document.createTextNode("help");
frag.appendChild(document.createTextNode("fail"));
frag.appendChild(span);
frag.appendChild(text);
frag.appendChild(div);
frag.appendChild(document.createTextNode("fail"));
is(text.nextElementSibling, div, "nextElementSibling should work on text");
is(text.previousElementSibling, span,
"previousElementSibling should work on text");
is(document.firstElementChild, document.documentElement,
"firstElementChild should work on document");
is(document.lastElementChild, document.documentElement,
"lastElementChild should work on document");
is(document.children.length, 1, "Document has one element kid");
is(document.children[0], document.documentElement,
"Document only element child is <html>");
is(frag.firstElementChild, span,
"firstElementChild should work on document fragment");
is(frag.lastElementChild, div,
"lastElementChild should work on document fragment");
is(frag.children.length, 2, "Document fragment has two element kids");
is(frag.children[0], span, "Document fragment first element child is span");
is(frag.children[1], div, "Document fragment second element child is div");
is(document.documentElement.firstElementChild, document.head,
"firstElementChild should work on element");
is(document.documentElement.lastElementChild, document.body,
"lastElementChild should work on element");
is(document.documentElement.children.length, 2, "<html> has two element kids");
is(document.documentElement.children[0], document.head,
"<html> first element child is head");
is(document.documentElement.children[1], document.body,
"<html> second element child is body");
SimpleTest.finish();
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=895974">Mozilla Bug 895974</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -14,6 +14,9 @@ if (navigator.platform.startsWith("Win")) {
SimpleTest.expectAssertions(0, 1);
} else if (navigator.platform.startsWith("Mac")) {
SimpleTest.expectAssertions(0, 2);
} else if (navigator.platform.startsWith("Linux")) {
// Bug 897024
SimpleTest.expectAssertions(0, 2);
}
let manager = new MediaTestManager;

View File

@ -9,9 +9,10 @@
[NoInterfaceObject]
interface ChildNode {
// On Element:
// readonly attribute Element? previousElementSibling;
// readonly attribute Element? nextElementSibling;
[Pure]
readonly attribute Element? previousElementSibling;
[Pure]
readonly attribute Element? nextElementSibling;
// Not implemented yet:
// void before((Node or DOMString)... nodes);

View File

@ -334,3 +334,4 @@ Document implements XPathEvaluator;
Document implements GlobalEventHandlers;
Document implements NodeEventHandlers;
Document implements TouchEventHandlers;
Document implements ParentNode;

View File

@ -29,3 +29,5 @@ partial interface DocumentFragment {
[Throws]
NodeList querySelectorAll(DOMString selectors);
};
DocumentFragment implements ParentNode;

View File

@ -55,19 +55,6 @@ interface Element : Node {
HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
HTMLCollection getElementsByClassName(DOMString classNames);
[Constant]
readonly attribute HTMLCollection children;
[Pure]
readonly attribute Element? firstElementChild;
[Pure]
readonly attribute Element? lastElementChild;
[Pure]
readonly attribute Element? previousElementSibling;
[Pure]
readonly attribute Element? nextElementSibling;
[Pure]
readonly attribute unsigned long childElementCount;
/**
* The ratio of font-size-inflated text font size to computed font
* size for this element. This will query the element for its primary frame,
@ -199,3 +186,4 @@ partial interface Element {
};
Element implements ChildNode;
Element implements ParentNode;

View File

@ -0,0 +1,24 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* http://dom.spec.whatwg.org/#interface-parentnode
*/
[NoInterfaceObject]
interface ParentNode {
[Constant]
readonly attribute HTMLCollection children;
[Pure]
readonly attribute Element? firstElementChild;
[Pure]
readonly attribute Element? lastElementChild;
[Pure]
readonly attribute unsigned long childElementCount;
// Not implemented yet
// void prepend((Node or DOMString)... nodes);
// void append((Node or DOMString)... nodes);
};

View File

@ -209,6 +209,7 @@ webidl_files = \
PaintRequest.webidl \
PaintRequestList.webidl \
PannerNode.webidl \
ParentNode.webidl \
Performance.webidl \
PerformanceNavigation.webidl \
PerformanceTiming.webidl \