Skip to content

Commit

Permalink
#399: ChildNode-ParentNode DOM4 M911477 M1301777 M1308922 M1104955 M1…
Browse files Browse the repository at this point in the history
…054759 M1258163
  • Loading branch information
classilla committed Dec 17, 2017
1 parent 956fd74 commit 82f1d58
Show file tree
Hide file tree
Showing 31 changed files with 850 additions and 35 deletions.
189 changes: 189 additions & 0 deletions dom/base/nsINode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,168 @@ nsINode::GetNextElementSibling() const
return nullptr;
}

static already_AddRefed<nsINode>
GetNodeFromNodeOrString(const OwningNodeOrString& aNode,
nsIDocument* aDocument)
{
if (aNode.IsNode()) {
nsCOMPtr<nsINode> node = aNode.GetAsNode();
return node.forget();
}

if (aNode.IsString()){
RefPtr<nsTextNode> textNode =
aDocument->CreateTextNode(aNode.GetAsString());
return textNode.forget();
}

MOZ_CRASH("Impossible type");
}

/**
* Implement the algorithm specified at
* https://dom.spec.whatwg.org/#converting-nodes-into-a-node for |prepend()|,
* |append()|, |before()|, |after()|, and |replaceWith()| APIs.
*/
static already_AddRefed<nsINode>
ConvertNodesOrStringsIntoNode(const Sequence<OwningNodeOrString>& aNodes,
nsIDocument* aDocument,
ErrorResult& aRv)
{
if (aNodes.Length() == 1) {
return GetNodeFromNodeOrString(aNodes[0], aDocument);
}

nsCOMPtr<nsINode> fragment = aDocument->CreateDocumentFragment();

for (const auto& node : aNodes) {
nsCOMPtr<nsINode> childNode = GetNodeFromNodeOrString(node, aDocument);
fragment->AppendChild(*childNode, aRv);
if (aRv.Failed()) {
return nullptr;
}
}

return fragment.forget();
}

static void
InsertNodesIntoHashset(const Sequence<OwningNodeOrString>& aNodes,
nsTHashtable<nsPtrHashKey<nsINode>>& aHashset)
{
for (const auto& node : aNodes) {
if (node.IsNode()) {
aHashset.PutEntry(node.GetAsNode());
}
}
}

static nsINode*
FindViablePreviousSibling(const nsINode& aNode,
const Sequence<OwningNodeOrString>& aNodes)
{
nsTHashtable<nsPtrHashKey<nsINode>> nodeSet(16);
InsertNodesIntoHashset(aNodes, nodeSet);

nsINode* viablePreviousSibling = nullptr;
for (nsINode* sibling = aNode.GetPreviousSibling(); sibling;
sibling = sibling->GetPreviousSibling()) {
if (!nodeSet.Contains(sibling)) {
viablePreviousSibling = sibling;
break;
}
}

return viablePreviousSibling;
}

static nsINode*
FindViableNextSibling(const nsINode& aNode,
const Sequence<OwningNodeOrString>& aNodes)
{
nsTHashtable<nsPtrHashKey<nsINode>> nodeSet(16);
InsertNodesIntoHashset(aNodes, nodeSet);

nsINode* viableNextSibling = nullptr;
for (nsINode* sibling = aNode.GetNextSibling(); sibling;
sibling = sibling->GetNextSibling()) {
if (!nodeSet.Contains(sibling)) {
viableNextSibling = sibling;
break;
}
}

return viableNextSibling;
}

void
nsINode::Before(const Sequence<OwningNodeOrString>& aNodes,
ErrorResult& aRv)
{
nsCOMPtr<nsINode> parent = GetParentNode();
if (!parent) {
return;
}

nsCOMPtr<nsINode> viablePreviousSibling =
FindViablePreviousSibling(*this, aNodes);

nsCOMPtr<nsINode> node =
ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
if (aRv.Failed()) {
return;
}

viablePreviousSibling = viablePreviousSibling ?
viablePreviousSibling->GetNextSibling() : parent->GetFirstChild();

parent->InsertBefore(*node, viablePreviousSibling, aRv);
}

void
nsINode::After(const Sequence<OwningNodeOrString>& aNodes,
ErrorResult& aRv)
{
nsCOMPtr<nsINode> parent = GetParentNode();
if (!parent) {
return;
}

nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes);

nsCOMPtr<nsINode> node =
ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
if (aRv.Failed()) {
return;
}

parent->InsertBefore(*node, viableNextSibling, aRv);
}

void
nsINode::ReplaceWith(const Sequence<OwningNodeOrString>& aNodes,
ErrorResult& aRv)
{
nsCOMPtr<nsINode> parent = GetParentNode();
if (!parent) {
return;
}

nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes);

nsCOMPtr<nsINode> node =
ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
if (aRv.Failed()) {
return;
}

if (parent == GetParentNode()) {
parent->ReplaceChild(*node, *this, aRv);
} else {
parent->InsertBefore(*node, viableNextSibling, aRv);
}
}

void
nsINode::Remove()
{
Expand Down Expand Up @@ -1693,6 +1855,33 @@ nsINode::GetLastElementChild() const
return nullptr;
}

void
nsINode::Prepend(const Sequence<OwningNodeOrString>& aNodes,
ErrorResult& aRv)
{
nsCOMPtr<nsINode> node =
ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
if (aRv.Failed()) {
return;
}

nsCOMPtr<nsINode> refNode = mFirstChild;
InsertBefore(*node, refNode, aRv);
}

void
nsINode::Append(const Sequence<OwningNodeOrString>& aNodes,
ErrorResult& aRv)
{
nsCOMPtr<nsINode> node =
ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
if (aRv.Failed()) {
return;
}

AppendChild(*node, aRv);
}

void
nsINode::doRemoveChildAt(uint32_t aIndex, bool aNotify,
nsIContent* aKid, nsAttrAndChildArray& aChildArray)
Expand Down
14 changes: 14 additions & 0 deletions dom/base/nsINode.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class DOMRectReadOnly;
class Element;
class EventHandlerNonNull;
template<typename T> class Optional;
class OwningNodeOrString;
template<typename> class Sequence;
class Text;
class TextOrElementOrDocument;
struct DOMPointInit;
Expand Down Expand Up @@ -267,9 +269,13 @@ class nsINode : public mozilla::dom::EventTarget
typedef mozilla::dom::DOMPointInit DOMPointInit;
typedef mozilla::dom::DOMQuad DOMQuad;
typedef mozilla::dom::DOMRectReadOnly DOMRectReadOnly;
typedef mozilla::dom::OwningNodeOrString OwningNodeOrString;
typedef mozilla::dom::TextOrElementOrDocument TextOrElementOrDocument;
typedef mozilla::ErrorResult ErrorResult;

template<class T>
using Sequence = mozilla::dom::Sequence<T>;

NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODE_IID)

// Among the sub-classes that inherit (directly or indirectly) from nsINode,
Expand Down Expand Up @@ -1807,6 +1813,11 @@ class nsINode : public mozilla::dom::EventTarget
// ChildNode methods
mozilla::dom::Element* GetPreviousElementSibling() const;
mozilla::dom::Element* GetNextElementSibling() const;

void Before(const Sequence<OwningNodeOrString>& aNodes, ErrorResult& aRv);
void After(const Sequence<OwningNodeOrString>& aNodes, ErrorResult& aRv);
void ReplaceWith(const Sequence<OwningNodeOrString>& aNodes,
ErrorResult& aRv);
/**
* Remove this node from its parent, if any.
*/
Expand All @@ -1816,6 +1827,9 @@ class nsINode : public mozilla::dom::EventTarget
mozilla::dom::Element* GetFirstElementChild() const;
mozilla::dom::Element* GetLastElementChild() const;

void Prepend(const Sequence<OwningNodeOrString>& aNodes, ErrorResult& aRv);
void Append(const Sequence<OwningNodeOrString>& aNodes, ErrorResult& aRv);

void GetBoxQuads(const BoxQuadOptions& aOptions,
nsTArray<RefPtr<DOMQuad> >& aResult,
mozilla::ErrorResult& aRv);
Expand Down
31 changes: 28 additions & 3 deletions dom/bindings/BindingUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,8 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
JS::Handle<JSObject*> parentProto,
const js::Class* protoClass,
const NativeProperties* properties,
const NativeProperties* chromeOnlyProperties)
const NativeProperties* chromeOnlyProperties,
const char* const* unscopableNames)
{
JS::Rooted<JSObject*> ourProto(cx,
JS_NewObjectWithUniqueType(cx, Jsvalify(protoClass), parentProto));
Expand All @@ -778,6 +779,28 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
return nullptr;
}

if (unscopableNames) {
JS::Rooted<JSObject*> unscopableObj(cx, JS_NewPlainObject(cx));
if (!unscopableObj) {
return nullptr;
}

for (; *unscopableNames; ++unscopableNames) {
if (!JS_DefineProperty(cx, unscopableObj, *unscopableNames,
JS::TrueHandleValue, JSPROP_ENUMERATE)) {
return nullptr;
}
}

JS::Rooted<jsid> unscopableId(cx,
SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::unscopables)));
// Readonly and non-enumerable to match Array.prototype.
if (!JS_DefinePropertyById(cx, ourProto, unscopableId, unscopableObj,
JSPROP_READONLY)) {
return nullptr;
}
}

return ourProto;
}

Expand Down Expand Up @@ -833,7 +856,8 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
JS::Heap<JSObject*>* constructorCache,
const NativeProperties* properties,
const NativeProperties* chromeOnlyProperties,
const char* name, bool defineOnGlobal)
const char* name, bool defineOnGlobal,
const char* const* unscopableNames)
{
MOZ_ASSERT(protoClass || constructorClass || constructor,
"Need at least one class or a constructor!");
Expand Down Expand Up @@ -864,7 +888,8 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
if (protoClass) {
proto =
CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
properties, chromeOnlyProperties);
properties, chromeOnlyProperties,
unscopableNames);
if (!proto) {
return;
}
Expand Down
5 changes: 4 additions & 1 deletion dom/bindings/BindingUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,8 @@ struct NamedConstructor
* false in situations where we want the properties to only
* appear on privileged Xrays but not on the unprivileged
* underlying global.
* unscopableNames if not null it points to a null-terminated list of const
* char* names of the unscopable properties for this interface.
*
* At least one of protoClass, constructorClass or constructor should be
* non-null. If constructorClass or constructor are non-null, the resulting
Expand All @@ -627,7 +629,8 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
JS::Heap<JSObject*>* constructorCache,
const NativeProperties* regularProperties,
const NativeProperties* chromeOnlyProperties,
const char* name, bool defineOnGlobal);
const char* name, bool defineOnGlobal,
const char* const* unscopableNames);

/**
* Define the properties (regular and chrome-only) on obj.
Expand Down
Loading

0 comments on commit 82f1d58

Please sign in to comment.