2013-03-01 06:53:49 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-06-23 10:57:01 +04:00
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Base class for all DOM nodes.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "nsINode.h"
|
|
|
|
|
2013-02-23 08:46:05 +04:00
|
|
|
#include "AccessCheck.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "jsapi.h"
|
2018-08-20 17:54:45 +03:00
|
|
|
#include "js/JSON.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "mozAutoDocUpdate.h"
|
2014-03-17 10:56:54 +04:00
|
|
|
#include "mozilla/AsyncEventDispatcher.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "mozilla/CORSMode.h"
|
2014-03-18 08:48:21 +04:00
|
|
|
#include "mozilla/EventDispatcher.h"
|
2014-03-17 10:56:53 +04:00
|
|
|
#include "mozilla/EventListenerManager.h"
|
2017-08-04 00:18:50 +03:00
|
|
|
#include "mozilla/HTMLEditor.h"
|
2014-02-27 14:51:15 +04:00
|
|
|
#include "mozilla/InternalMutationEvent.h"
|
2012-10-26 17:32:10 +04:00
|
|
|
#include "mozilla/Likely.h"
|
2013-06-23 16:03:39 +04:00
|
|
|
#include "mozilla/MemoryReporting.h"
|
2016-02-24 04:28:50 +03:00
|
|
|
#include "mozilla/ServoBindings.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "mozilla/Telemetry.h"
|
2017-06-22 09:21:31 +03:00
|
|
|
#include "mozilla/TextEditor.h"
|
2014-07-16 04:02:31 +04:00
|
|
|
#include "mozilla/TimeStamp.h"
|
2018-03-29 01:01:47 +03:00
|
|
|
#include "mozilla/dom/CharacterData.h"
|
2018-03-13 23:24:01 +03:00
|
|
|
#include "mozilla/dom/DocumentType.h"
|
2014-03-05 04:37:43 +04:00
|
|
|
#include "mozilla/dom/Element.h"
|
|
|
|
#include "mozilla/dom/Event.h"
|
2018-02-22 01:07:53 +03:00
|
|
|
#include "mozilla/dom/L10nUtilsBinding.h"
|
|
|
|
#include "mozilla/dom/Promise.h"
|
|
|
|
#include "mozilla/dom/PromiseNativeHandler.h"
|
2014-03-05 04:37:43 +04:00
|
|
|
#include "mozilla/dom/ShadowRoot.h"
|
2018-07-16 17:07:58 +03:00
|
|
|
#include "mozilla/dom/SVGUseElement.h"
|
2018-06-15 02:17:42 +03:00
|
|
|
#include "mozilla/dom/ScriptSettings.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "nsAttrValueOrString.h"
|
|
|
|
#include "nsBindingManager.h"
|
|
|
|
#include "nsCCUncollectableMarker.h"
|
|
|
|
#include "nsContentCreatorFunctions.h"
|
|
|
|
#include "nsContentList.h"
|
|
|
|
#include "nsContentUtils.h"
|
|
|
|
#include "nsCycleCollectionParticipant.h"
|
|
|
|
#include "nsDocument.h"
|
2013-04-09 19:29:44 +04:00
|
|
|
#include "mozilla/dom/Attr.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "nsDOMAttributeMap.h"
|
|
|
|
#include "nsDOMCID.h"
|
|
|
|
#include "nsDOMCSSAttrDeclaration.h"
|
2012-07-27 18:03:27 +04:00
|
|
|
#include "nsError.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "nsDOMMutationObserver.h"
|
|
|
|
#include "nsDOMString.h"
|
|
|
|
#include "nsDOMTokenList.h"
|
|
|
|
#include "nsFocusManager.h"
|
|
|
|
#include "nsFrameSelection.h"
|
|
|
|
#include "nsGenericHTMLElement.h"
|
|
|
|
#include "nsGkAtoms.h"
|
|
|
|
#include "nsIAnonymousContentCreator.h"
|
2017-10-03 01:05:19 +03:00
|
|
|
#include "nsAtom.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "nsIBaseWindow.h"
|
|
|
|
#include "nsICategoryManager.h"
|
2018-08-02 14:54:15 +03:00
|
|
|
#include "nsIContentInlines.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "nsIContentIterator.h"
|
|
|
|
#include "nsIControllers.h"
|
|
|
|
#include "nsIDocument.h"
|
|
|
|
#include "nsIDOMEventListener.h"
|
|
|
|
#include "nsILinkHandler.h"
|
2014-06-20 06:01:40 +04:00
|
|
|
#include "mozilla/dom/NodeInfo.h"
|
|
|
|
#include "mozilla/dom/NodeInfoInlines.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "nsIPresShell.h"
|
|
|
|
#include "nsIScriptError.h"
|
|
|
|
#include "nsIScriptGlobalObject.h"
|
|
|
|
#include "nsIScriptSecurityManager.h"
|
|
|
|
#include "nsIScrollableFrame.h"
|
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsIURL.h"
|
2013-01-03 17:23:11 +04:00
|
|
|
#include "nsView.h"
|
2013-01-05 07:12:24 +04:00
|
|
|
#include "nsViewManager.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "nsIWebNavigation.h"
|
|
|
|
#include "nsIWidget.h"
|
|
|
|
#include "nsLayoutUtils.h"
|
2014-02-28 03:04:46 +04:00
|
|
|
#include "nsNameSpaceManager.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "nsNodeInfoManager.h"
|
|
|
|
#include "nsNodeUtils.h"
|
|
|
|
#include "nsPIBoxObject.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
|
|
|
#include "nsPresContext.h"
|
|
|
|
#include "nsString.h"
|
|
|
|
#include "nsStyleConsts.h"
|
|
|
|
#include "nsSVGUtils.h"
|
|
|
|
#include "nsTextNode.h"
|
|
|
|
#include "nsUnicharUtils.h"
|
|
|
|
#include "nsXBLBinding.h"
|
|
|
|
#include "nsXBLPrototypeBinding.h"
|
2018-04-13 21:04:47 +03:00
|
|
|
#include "nsWindowSizes.h"
|
2014-07-03 17:23:14 +04:00
|
|
|
#include "mozilla/Preferences.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "xpcpublic.h"
|
2013-02-15 20:55:53 +04:00
|
|
|
#include "HTMLLegendElement.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
#include "nsWrapperCacheInlines.h"
|
2012-09-13 00:29:30 +04:00
|
|
|
#include "WrapperFactory.h"
|
2013-01-15 16:22:03 +04:00
|
|
|
#include <algorithm>
|
2013-05-31 01:46:39 +04:00
|
|
|
#include "nsGlobalWindow.h"
|
2013-09-11 23:43:01 +04:00
|
|
|
#include "nsDOMMutationObserver.h"
|
2014-03-12 05:11:38 +04:00
|
|
|
#include "GeometryUtils.h"
|
2015-03-14 08:34:40 +03:00
|
|
|
#include "nsIAnimationObserver.h"
|
2015-05-19 13:33:00 +03:00
|
|
|
#include "nsChildContentList.h"
|
2017-01-15 20:46:00 +03:00
|
|
|
#include "mozilla/dom/NodeBinding.h"
|
2017-02-01 23:43:36 +03:00
|
|
|
#include "mozilla/dom/BindingDeclarations.h"
|
2018-10-24 20:41:46 +03:00
|
|
|
#include "xpcprivate.h"
|
2012-06-23 10:57:01 +04:00
|
|
|
|
2017-06-15 04:49:17 +03:00
|
|
|
#include "XPathGenerator.h"
|
|
|
|
|
2016-08-23 21:15:15 +03:00
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
#include "mozilla/dom/AccessibleNode.h"
|
|
|
|
#endif
|
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
using namespace mozilla;
|
|
|
|
using namespace mozilla::dom;
|
|
|
|
|
2015-05-19 13:33:00 +03:00
|
|
|
nsINode::nsSlots::nsSlots()
|
Bug 1506547 - Align user-select behavior more with other UAs. r=mats
There's a few subtle behavior changes here, which I'll try to break down in the
commit message.
The biggest one is the EditableDescendantCount stuff going away. This
was added in bug 1181130, to prevent clicking on the non-editable div from
selecting the editable div inside. This is problematic for multiple reasons:
* First, I don't think non-editable regions of an editable element should
be user-select: all.
* Second, it just doesn't work in Shadow DOM (the editable descendant count is
not kept up-to-date when not in the uncomposed doc), so nested
contenteditables behave differently inside vs. outside a Shadow Tree.
* Third, I think it's user hostile to just entirely disable selection if you
have a contenteditable descendant as a child of a user-select: all thing.
WebKit behaves like this patch in the following test-case (though not Blink):
https://crisal.io/tmp/user-select-all-contenteditable-descendant.html
Edge doesn't seem to support user-select: all at all (no pun intended).
But we don't allow to select anything at all which looks wrong.
* Fourth, it's not tested at all (which explains how we broke it in Shadow DOM
and not even notice...).
In any case I've verified that this doesn't regress the editor from that bug. If
this regresses anything we can fix it as outlined in the first bullet point
above, which should also make us more compatible with other UAs in that
test-case.
The other change is `all` not overriding everything else. So, something like:
<div style="-webkit-user-select: all">All <div style="-webkit-user-select: none">None</div></div>
Totally ignores the -webkit-user-select: none declaration in Firefox before this
change. This doesn't match any other UA nor the spec, and this patch aligns us
with WebKit / Blink.
This in turn makes us not need -moz-text anymore, whose only purpose was to
avoid this.
This also fixes a variety of bugs uncovered by the previous changes, like the
SetIgnoreUserModify(false) call in editor being completely useless, since
presShell->SetCaretEnabled ended in nsCaret::SetVisible, which overrode it.
This in turn uncovered even more bugs, from bugs in the caret painting code,
like not checking -moz-user-modify on the right frame if you're the last frame
of a line, to even funnier bits where before this patch you show the caret but
can't write at all...
In any case, the new setup I came up with is that when you're editing (the
selection is focused on an editable node) moving the caret forces it to end up
in an editable node, thus jumping over non-editable ones.
This has the nice effect of not completely disabling selection of
-moz-user-select: all elements that have editable descendants (which was a very
ad-hoc hack for bug 1181130, and somewhat broken per the above), and also
not needing the -moz-user-select: all for non-editable bits in contenteditable.css
at all.
This also fixes issues with br-skipping like not being able to insert content in
the following test-case:
<div contenteditable="true"><span contenteditable="false">xyz </span><br>editable</div>
If you start moving to the left from the second line, for example.
I think this yields way better behavior in all the relevant test-cases from bug
1181130 / bug 1109968 / bug 1132768, shouldn't cause any regression, and the
complexity is significantly reduced in some places.
There's still some other broken bits that this patch doesn't fix, but I'll file
follow-ups for those.
Differential Revision: https://phabricator.services.mozilla.com/D12687
--HG--
extra : moz-landing-system : lando
2018-11-26 12:21:37 +03:00
|
|
|
: mWeakReference(nullptr)
|
2015-05-19 13:33:00 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
nsINode::nsSlots::~nsSlots()
|
|
|
|
{
|
|
|
|
if (mChildNodes) {
|
|
|
|
mChildNodes->DropReference();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mWeakReference) {
|
|
|
|
mWeakReference->NoticeNodeDestruction();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsINode::nsSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
|
|
|
|
{
|
|
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildNodes");
|
|
|
|
cb.NoteXPCOMChild(mChildNodes);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsINode::nsSlots::Unlink()
|
|
|
|
{
|
|
|
|
if (mChildNodes) {
|
|
|
|
mChildNodes->DropReference();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
2018-08-06 22:29:27 +03:00
|
|
|
#ifdef MOZILLA_INTERNAL_API
|
2018-09-21 23:45:49 +03:00
|
|
|
nsINode::nsINode(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
|
|
|
|
: mNodeInfo(std::move(aNodeInfo))
|
2018-08-06 22:29:27 +03:00
|
|
|
, mParent(nullptr)
|
|
|
|
#ifndef BOOL_FLAGS_ON_WRAPPER_CACHE
|
|
|
|
, mBoolFlags(0)
|
|
|
|
#endif
|
|
|
|
, mChildCount(0)
|
|
|
|
, mPreviousOrLastSibling(nullptr)
|
|
|
|
, mSubtreeRoot(this)
|
|
|
|
, mSlots(nullptr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
nsINode::~nsINode()
|
|
|
|
{
|
2013-07-09 21:30:58 +04:00
|
|
|
MOZ_ASSERT(!HasSlots(), "nsNodeUtils::LastRelease was not called?");
|
|
|
|
MOZ_ASSERT(mSubtreeRoot == this, "Didn't restore state properly?");
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
2018-11-13 15:48:21 +03:00
|
|
|
nsINode::GetProperty(const nsAtom* aPropertyName, nsresult* aStatus) const
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2017-04-25 11:15:37 +03:00
|
|
|
if (!HasProperties()) { // a fast HasFlag() test
|
|
|
|
if (aStatus) {
|
|
|
|
*aStatus = NS_PROPTABLE_PROP_NOT_THERE;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
2018-03-17 07:30:49 +03:00
|
|
|
return OwnerDoc()->PropertyTable().GetProperty(this, aPropertyName, aStatus);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2018-03-17 07:30:49 +03:00
|
|
|
nsINode::SetProperty(nsAtom* aPropertyName,
|
|
|
|
void* aValue,
|
|
|
|
NSPropertyDtorFunc aDtor,
|
|
|
|
bool aTransfer)
|
|
|
|
{
|
|
|
|
nsresult rv = OwnerDoc()->PropertyTable().SetProperty(this,
|
|
|
|
aPropertyName,
|
|
|
|
aValue,
|
|
|
|
aDtor,
|
|
|
|
nullptr,
|
|
|
|
aTransfer);
|
2012-06-23 10:57:01 +04:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
SetFlags(NODE_HAS_PROPERTIES);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2018-11-13 15:48:21 +03:00
|
|
|
nsINode::DeleteProperty(const nsAtom* aPropertyName)
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2018-03-17 07:30:49 +03:00
|
|
|
OwnerDoc()->PropertyTable().DeleteProperty(this, aPropertyName);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
2018-11-13 15:48:21 +03:00
|
|
|
nsINode::UnsetProperty(const nsAtom* aPropertyName, nsresult* aStatus)
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2018-03-17 07:30:49 +03:00
|
|
|
return OwnerDoc()->PropertyTable().UnsetProperty(this, aPropertyName, aStatus);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsINode::nsSlots*
|
|
|
|
nsINode::CreateSlots()
|
|
|
|
{
|
|
|
|
return new nsSlots();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent*
|
2017-06-22 09:21:31 +03:00
|
|
|
nsINode::GetTextEditorRootContent(TextEditor** aTextEditor)
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2017-06-22 09:21:31 +03:00
|
|
|
if (aTextEditor) {
|
|
|
|
*aTextEditor = nullptr;
|
|
|
|
}
|
2012-10-09 16:31:24 +04:00
|
|
|
for (nsINode* node = this; node; node = node->GetParentNode()) {
|
2012-06-23 10:57:01 +04:00
|
|
|
if (!node->IsElement() ||
|
2015-03-03 14:08:59 +03:00
|
|
|
!node->IsHTMLElement())
|
2012-06-23 10:57:01 +04:00
|
|
|
continue;
|
|
|
|
|
2017-06-22 09:21:31 +03:00
|
|
|
RefPtr<TextEditor> textEditor =
|
|
|
|
static_cast<nsGenericHTMLElement*>(node)->GetTextEditorInternal();
|
|
|
|
if (!textEditor) {
|
2012-06-23 10:57:01 +04:00
|
|
|
continue;
|
2017-06-22 09:21:31 +03:00
|
|
|
}
|
2012-06-23 10:57:01 +04:00
|
|
|
|
2017-06-22 09:21:31 +03:00
|
|
|
MOZ_ASSERT(!textEditor->AsHTMLEditor(),
|
|
|
|
"If it were an HTML editor, needs to use GetRootElement()");
|
|
|
|
Element* rootElement = textEditor->GetRoot();
|
|
|
|
if (aTextEditor) {
|
|
|
|
textEditor.forget(aTextEditor);
|
|
|
|
}
|
|
|
|
return rootElement;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2017-01-15 20:46:00 +03:00
|
|
|
nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions)
|
|
|
|
{
|
|
|
|
if (aOptions.mComposed) {
|
2018-08-20 14:56:27 +03:00
|
|
|
if (nsIDocument* doc = GetComposedDoc()) {
|
|
|
|
return doc;
|
2017-01-15 20:46:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nsINode* node = this;
|
|
|
|
while(node) {
|
|
|
|
node = node->SubtreeRoot();
|
2017-12-31 22:57:32 +03:00
|
|
|
ShadowRoot* shadow = ShadowRoot::FromNode(node);
|
|
|
|
if (!shadow) {
|
|
|
|
break;
|
2017-01-15 20:46:00 +03:00
|
|
|
}
|
2017-12-31 22:57:32 +03:00
|
|
|
node = shadow->GetHost();
|
2017-01-15 20:46:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SubtreeRoot();
|
|
|
|
}
|
|
|
|
|
2018-03-06 03:34:40 +03:00
|
|
|
nsINode*
|
|
|
|
nsINode::GetParentOrHostNode() const
|
|
|
|
{
|
|
|
|
if (mParent) {
|
|
|
|
return mParent;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
|
|
|
|
return shadowRoot ? shadowRoot->GetHost() : nullptr;
|
|
|
|
}
|
|
|
|
|
2014-06-07 12:42:53 +04:00
|
|
|
nsINode*
|
|
|
|
nsINode::SubtreeRoot() const
|
|
|
|
{
|
2018-06-01 00:11:45 +03:00
|
|
|
auto RootOfNode = [](const nsINode* aStart) -> nsINode* {
|
|
|
|
const nsINode* node = aStart;
|
|
|
|
const nsINode* iter = node;
|
|
|
|
while ((iter = iter->GetParentNode())) {
|
|
|
|
node = iter;
|
|
|
|
}
|
|
|
|
return const_cast<nsINode*>(node);
|
|
|
|
};
|
|
|
|
|
2014-06-07 12:42:53 +04:00
|
|
|
// There are four cases of interest here. nsINodes that are really:
|
|
|
|
// 1. nsIDocument nodes - Are always in the document.
|
|
|
|
// 2.a nsIContent nodes not in a shadow tree - Are either in the document,
|
|
|
|
// or mSubtreeRoot is updated in BindToTree/UnbindFromTree.
|
|
|
|
// 2.b nsIContent nodes in a shadow tree - Are never in the document,
|
|
|
|
// ignore mSubtreeRoot and return the containing shadow root.
|
2018-07-13 01:51:52 +03:00
|
|
|
// 4. Attr nodes - Are never in the document, and mSubtreeRoot
|
2014-06-07 12:42:53 +04:00
|
|
|
// is always 'this' (as set in nsINode's ctor).
|
|
|
|
nsINode* node;
|
2016-03-31 13:58:25 +03:00
|
|
|
if (IsInUncomposedDoc()) {
|
2014-06-07 12:42:53 +04:00
|
|
|
node = OwnerDocAsNode();
|
|
|
|
} else if (IsContent()) {
|
|
|
|
ShadowRoot* containingShadow = AsContent()->GetContainingShadow();
|
|
|
|
node = containingShadow ? containingShadow : mSubtreeRoot;
|
2018-06-01 00:11:45 +03:00
|
|
|
if (!node) {
|
|
|
|
NS_WARNING("Using SubtreeRoot() on unlinked element?");
|
|
|
|
node = RootOfNode(this);
|
|
|
|
}
|
2014-06-07 12:42:53 +04:00
|
|
|
} else {
|
|
|
|
node = mSubtreeRoot;
|
|
|
|
}
|
2018-06-01 00:11:45 +03:00
|
|
|
MOZ_ASSERT(node, "Should always have a node here!");
|
2014-06-07 12:42:53 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
2018-06-01 00:11:45 +03:00
|
|
|
const nsINode* slowNode = RootOfNode(this);
|
|
|
|
MOZ_ASSERT(slowNode == node, "These should always be in sync!");
|
2014-06-07 12:42:53 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
static nsIContent* GetRootForContentSubtree(nsIContent* aContent)
|
|
|
|
{
|
2012-07-30 18:20:58 +04:00
|
|
|
NS_ENSURE_TRUE(aContent, nullptr);
|
2013-12-02 14:26:11 +04:00
|
|
|
|
|
|
|
// Special case for ShadowRoot because the ShadowRoot itself is
|
|
|
|
// the root. This is necessary to prevent selection from crossing
|
|
|
|
// the ShadowRoot boundary.
|
|
|
|
ShadowRoot* containingShadow = aContent->GetContainingShadow();
|
|
|
|
if (containingShadow) {
|
|
|
|
return containingShadow;
|
|
|
|
}
|
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
nsIContent* stop = aContent->GetBindingParent();
|
|
|
|
while (aContent) {
|
|
|
|
nsIContent* parent = aContent->GetParent();
|
|
|
|
if (parent == stop) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
aContent = parent;
|
|
|
|
}
|
|
|
|
return aContent;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent*
|
|
|
|
nsINode::GetSelectionRootContent(nsIPresShell* aPresShell)
|
|
|
|
{
|
2012-07-30 18:20:58 +04:00
|
|
|
NS_ENSURE_TRUE(aPresShell, nullptr);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
2018-04-15 12:43:15 +03:00
|
|
|
if (IsDocument())
|
|
|
|
return AsDocument()->GetRootElement();
|
2017-10-25 18:19:11 +03:00
|
|
|
if (!IsContent())
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
|
2016-03-31 15:20:14 +03:00
|
|
|
if (GetComposedDoc() != aPresShell->GetDocument()) {
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (static_cast<nsIContent*>(this)->HasIndependentSelection()) {
|
|
|
|
// This node should be a descendant of input/textarea editor.
|
|
|
|
nsIContent* content = GetTextEditorRootContent();
|
|
|
|
if (content)
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsPresContext* presContext = aPresShell->GetPresContext();
|
|
|
|
if (presContext) {
|
2017-08-07 11:00:44 +03:00
|
|
|
HTMLEditor* htmlEditor = nsContentUtils::GetHTMLEditor(presContext);
|
|
|
|
if (htmlEditor) {
|
2012-06-23 10:57:01 +04:00
|
|
|
// This node is in HTML editor.
|
2016-03-31 15:20:14 +03:00
|
|
|
nsIDocument* doc = GetComposedDoc();
|
2012-06-23 10:57:01 +04:00
|
|
|
if (!doc || doc->HasFlag(NODE_IS_EDITABLE) ||
|
|
|
|
!HasFlag(NODE_IS_EDITABLE)) {
|
2017-08-07 11:00:44 +03:00
|
|
|
nsIContent* editorRoot = htmlEditor->GetRoot();
|
2012-07-30 18:20:58 +04:00
|
|
|
NS_ENSURE_TRUE(editorRoot, nullptr);
|
2012-06-23 10:57:01 +04:00
|
|
|
return nsContentUtils::IsInSameAnonymousTree(this, editorRoot) ?
|
|
|
|
editorRoot :
|
|
|
|
GetRootForContentSubtree(static_cast<nsIContent*>(this));
|
|
|
|
}
|
|
|
|
// If the document isn't editable but this is editable, this is in
|
|
|
|
// contenteditable. Use the editing host element for selection root.
|
|
|
|
return static_cast<nsIContent*>(this)->GetEditingHost();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<nsFrameSelection> fs = aPresShell->FrameSelection();
|
2012-06-23 10:57:01 +04:00
|
|
|
nsIContent* content = fs->GetLimiter();
|
|
|
|
if (!content) {
|
|
|
|
content = fs->GetAncestorLimiter();
|
|
|
|
if (!content) {
|
|
|
|
nsIDocument* doc = aPresShell->GetDocument();
|
2012-07-30 18:20:58 +04:00
|
|
|
NS_ENSURE_TRUE(doc, nullptr);
|
2012-06-23 10:57:01 +04:00
|
|
|
content = doc->GetRootElement();
|
|
|
|
if (!content)
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This node might be in another subtree, if so, we should find this subtree's
|
|
|
|
// root. Otherwise, we can return the content simply.
|
2012-07-30 18:20:58 +04:00
|
|
|
NS_ENSURE_TRUE(content, nullptr);
|
2013-12-02 14:26:11 +04:00
|
|
|
if (!nsContentUtils::IsInSameAnonymousTree(this, content)) {
|
|
|
|
content = GetRootForContentSubtree(static_cast<nsIContent*>(this));
|
|
|
|
// Fixup for ShadowRoot because the ShadowRoot itself does not have a frame.
|
|
|
|
// Use the host as the root.
|
2017-12-31 22:57:32 +03:00
|
|
|
if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(content)) {
|
2013-12-02 14:26:11 +04:00
|
|
|
content = shadowRoot->GetHost();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return content;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsINodeList*
|
2012-10-09 16:31:24 +04:00
|
|
|
nsINode::ChildNodes()
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2012-08-11 02:27:28 +04:00
|
|
|
nsSlots* slots = Slots();
|
2015-04-17 18:49:59 +03:00
|
|
|
if (!slots->mChildNodes) {
|
2018-04-15 16:12:02 +03:00
|
|
|
slots->mChildNodes = IsAttr()
|
|
|
|
? new nsAttrChildContentList(this)
|
|
|
|
: new nsParentNodeChildContentList(this);
|
2015-04-17 18:49:59 +03:00
|
|
|
}
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
return slots->mChildNodes;
|
|
|
|
}
|
|
|
|
|
2018-08-06 22:29:27 +03:00
|
|
|
nsIContent*
|
|
|
|
nsINode::GetLastChild() const
|
|
|
|
{
|
|
|
|
return mFirstChild ? mFirstChild->mPreviousOrLastSibling : nullptr;
|
|
|
|
}
|
|
|
|
|
2017-10-23 10:04:14 +03:00
|
|
|
void
|
|
|
|
nsINode::InvalidateChildNodes()
|
|
|
|
{
|
2018-04-15 16:12:02 +03:00
|
|
|
MOZ_ASSERT(!IsAttr());
|
2017-10-23 10:04:14 +03:00
|
|
|
|
|
|
|
nsSlots* slots = GetExistingSlots();
|
|
|
|
if (!slots || !slots->mChildNodes) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto childNodes =
|
|
|
|
static_cast<nsParentNodeChildContentList*>(slots->mChildNodes.get());
|
|
|
|
childNodes->InvalidateCache();
|
|
|
|
}
|
|
|
|
|
2012-11-27 12:41:02 +04:00
|
|
|
void
|
2017-01-18 07:52:29 +03:00
|
|
|
nsINode::GetTextContentInternal(nsAString& aTextContent, OOMReporter& aError)
|
2012-11-27 12:41:02 +04:00
|
|
|
{
|
|
|
|
SetDOMStringToNull(aTextContent);
|
|
|
|
}
|
|
|
|
|
2018-05-18 20:02:14 +03:00
|
|
|
DocumentOrShadowRoot*
|
2018-05-24 22:46:11 +03:00
|
|
|
nsINode::GetUncomposedDocOrConnectedShadowRoot() const
|
2018-05-18 20:02:14 +03:00
|
|
|
{
|
|
|
|
if (IsInUncomposedDoc()) {
|
|
|
|
return OwnerDoc();
|
|
|
|
}
|
|
|
|
|
2018-08-20 14:56:27 +03:00
|
|
|
if (IsInComposedDoc() && IsInShadowTree()) {
|
2018-05-18 20:02:14 +03:00
|
|
|
return AsContent()->GetContainingShadow();
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
void
|
|
|
|
nsINode::CheckNotNativeAnonymous() const
|
|
|
|
{
|
2017-10-25 18:19:11 +03:00
|
|
|
if (!IsContent())
|
2012-06-23 10:57:01 +04:00
|
|
|
return;
|
|
|
|
nsIContent* content = static_cast<const nsIContent *>(this)->GetBindingParent();
|
|
|
|
while (content) {
|
|
|
|
if (content->IsRootOfNativeAnonymousSubtree()) {
|
|
|
|
NS_ERROR("Element not marked to be in native anonymous subtree!");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
content = content->GetBindingParent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-04-06 23:32:38 +04:00
|
|
|
bool
|
|
|
|
nsINode::IsInAnonymousSubtree() const
|
|
|
|
{
|
|
|
|
if (!IsContent()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return AsContent()->IsInAnonymousSubtree();
|
|
|
|
}
|
|
|
|
|
2015-07-08 22:58:07 +03:00
|
|
|
std::ostream&
|
|
|
|
operator<<(std::ostream& aStream, const nsINode& aNode)
|
|
|
|
{
|
|
|
|
nsAutoString elemDesc;
|
|
|
|
const nsINode* curr = &aNode;
|
|
|
|
while (curr) {
|
|
|
|
const nsString& localName = curr->LocalName();
|
|
|
|
nsString id;
|
|
|
|
if (curr->IsElement()) {
|
|
|
|
curr->AsElement()->GetId(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!elemDesc.IsEmpty()) {
|
|
|
|
elemDesc = elemDesc + NS_LITERAL_STRING(".");
|
|
|
|
}
|
|
|
|
|
|
|
|
elemDesc = elemDesc + localName;
|
|
|
|
|
|
|
|
if (!id.IsEmpty()) {
|
|
|
|
elemDesc = elemDesc + NS_LITERAL_STRING("['") + id +
|
|
|
|
NS_LITERAL_STRING("']");
|
|
|
|
}
|
|
|
|
|
|
|
|
curr = curr->GetParentNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ConvertUTF16toUTF8 str(elemDesc);
|
|
|
|
return aStream << str.get();
|
|
|
|
}
|
|
|
|
|
2018-07-16 17:07:58 +03:00
|
|
|
SVGUseElement*
|
|
|
|
nsINode::DoGetContainingSVGUseShadowHost() const
|
2014-04-15 07:38:54 +04:00
|
|
|
{
|
2018-07-16 17:07:58 +03:00
|
|
|
MOZ_ASSERT(IsInShadowTree());
|
2018-07-22 16:36:13 +03:00
|
|
|
return SVGUseElement::FromNodeOrNull(AsContent()->GetContainingShadowHost());
|
2014-04-15 07:38:54 +04:00
|
|
|
}
|
|
|
|
|
2018-06-29 23:39:46 +03:00
|
|
|
bool
|
|
|
|
nsINode::IsInUAWidget() const
|
|
|
|
{
|
|
|
|
if (!IsInShadowTree()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ShadowRoot* shadowRoot = AsContent()->GetContainingShadow();
|
|
|
|
return shadowRoot && shadowRoot->IsUAWidget();
|
|
|
|
}
|
|
|
|
|
2012-11-27 12:41:02 +04:00
|
|
|
void
|
|
|
|
nsINode::GetNodeValueInternal(nsAString& aNodeValue)
|
|
|
|
{
|
|
|
|
SetDOMStringToNull(aNodeValue);
|
|
|
|
}
|
|
|
|
|
2012-10-09 16:31:24 +04:00
|
|
|
nsINode*
|
|
|
|
nsINode::RemoveChild(nsINode& aOldChild, ErrorResult& aError)
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2018-06-19 12:21:18 +03:00
|
|
|
if (!aOldChild.IsContent()) {
|
2012-08-04 11:43:59 +04:00
|
|
|
// aOldChild can't be one of our children.
|
2012-10-09 16:31:24 +04:00
|
|
|
aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
|
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2012-10-09 16:31:24 +04:00
|
|
|
if (aOldChild.GetParentNode() == this) {
|
2018-05-13 00:46:45 +03:00
|
|
|
nsContentUtils::MaybeFireNodeRemoved(&aOldChild, this);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-06-19 12:21:18 +03:00
|
|
|
// Check again, we may not be the child's parent anymore.
|
|
|
|
// Can be triggered by dom/base/crashtests/293388-1.html
|
|
|
|
if (aOldChild.AsContent()->IsRootOfAnonymousSubtree() ||
|
|
|
|
aOldChild.GetParentNode() != this) {
|
2012-06-23 10:57:01 +04:00
|
|
|
// aOldChild isn't one of our children.
|
2012-10-09 16:31:24 +04:00
|
|
|
aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
|
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-06-19 12:21:18 +03:00
|
|
|
RemoveChildNode(aOldChild.AsContent(), true);
|
2012-10-09 16:31:24 +04:00
|
|
|
return &aOldChild;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2012-10-09 16:31:24 +04:00
|
|
|
void
|
2012-06-23 10:57:01 +04:00
|
|
|
nsINode::Normalize()
|
|
|
|
{
|
|
|
|
// First collect list of nodes to be removed
|
2016-02-02 18:36:30 +03:00
|
|
|
AutoTArray<nsCOMPtr<nsIContent>, 50> nodes;
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
bool canMerge = false;
|
|
|
|
for (nsIContent* node = this->GetFirstChild();
|
|
|
|
node;
|
|
|
|
node = node->GetNextNode(this)) {
|
2018-01-30 07:10:53 +03:00
|
|
|
if (node->NodeType() != TEXT_NODE) {
|
2012-06-23 10:57:01 +04:00
|
|
|
canMerge = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (canMerge || node->TextLength() == 0) {
|
|
|
|
// No need to touch canMerge. That way we can merge across empty
|
|
|
|
// textnodes if and only if the node before is a textnode
|
|
|
|
nodes.AppendElement(node);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
canMerge = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there's no following sibling, then we need to ensure that we don't
|
|
|
|
// collect following siblings of our (grand)parent as to-be-removed
|
|
|
|
canMerge = canMerge && !!node->GetNextSibling();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nodes.IsEmpty()) {
|
2012-10-09 16:31:24 +04:00
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// We're relying on mozAutoSubtreeModified to keep the doc alive here.
|
|
|
|
nsIDocument* doc = OwnerDoc();
|
|
|
|
|
|
|
|
// Batch possible DOMSubtreeModified events.
|
2012-07-30 18:20:58 +04:00
|
|
|
mozAutoSubtreeModified subtree(doc, nullptr);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
// Fire all DOMNodeRemoved events. Optimize the common case of there being
|
|
|
|
// no listeners
|
|
|
|
bool hasRemoveListeners = nsContentUtils::
|
|
|
|
HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED);
|
|
|
|
if (hasRemoveListeners) {
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t i = 0; i < nodes.Length(); ++i) {
|
2013-12-02 22:38:51 +04:00
|
|
|
nsINode* parentNode = nodes[i]->GetParentNode();
|
|
|
|
if (parentNode) { // Node may have already been removed.
|
2018-05-13 00:46:45 +03:00
|
|
|
nsContentUtils::MaybeFireNodeRemoved(nodes[i], parentNode);
|
2013-12-02 22:38:51 +04:00
|
|
|
}
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-15 16:56:38 +03:00
|
|
|
mozAutoDocUpdate batch(doc, true);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
// Merge and remove all nodes
|
|
|
|
nsAutoString tmpStr;
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t i = 0; i < nodes.Length(); ++i) {
|
2012-06-23 10:57:01 +04:00
|
|
|
nsIContent* node = nodes[i];
|
|
|
|
// Merge with previous node unless empty
|
|
|
|
const nsTextFragment* text = node->GetText();
|
|
|
|
if (text->GetLength()) {
|
|
|
|
nsIContent* target = node->GetPreviousSibling();
|
2018-01-30 07:10:53 +03:00
|
|
|
NS_ASSERTION((target && target->NodeType() == TEXT_NODE) ||
|
2012-06-23 10:57:01 +04:00
|
|
|
hasRemoveListeners,
|
|
|
|
"Should always have a previous text sibling unless "
|
|
|
|
"mutation events messed us up");
|
|
|
|
if (!hasRemoveListeners ||
|
2018-01-30 07:10:53 +03:00
|
|
|
(target && target->NodeType() == TEXT_NODE)) {
|
2012-06-23 10:57:01 +04:00
|
|
|
nsTextNode* t = static_cast<nsTextNode*>(target);
|
|
|
|
if (text->Is2b()) {
|
|
|
|
t->AppendTextForNormalize(text->Get2b(), text->GetLength(), true, node);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
tmpStr.Truncate();
|
|
|
|
text->AppendTo(tmpStr);
|
|
|
|
t->AppendTextForNormalize(tmpStr.get(), tmpStr.Length(), true, node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove node
|
2012-10-09 16:31:24 +04:00
|
|
|
nsCOMPtr<nsINode> parent = node->GetParentNode();
|
2012-06-23 10:57:01 +04:00
|
|
|
NS_ASSERTION(parent || hasRemoveListeners,
|
|
|
|
"Should always have a parent unless "
|
|
|
|
"mutation events messed us up");
|
|
|
|
if (parent) {
|
2018-06-19 12:21:18 +03:00
|
|
|
parent->RemoveChildNode(node, true);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-20 15:02:33 +03:00
|
|
|
nsresult
|
2012-10-09 16:31:24 +04:00
|
|
|
nsINode::GetBaseURI(nsAString &aURI) const
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIURI> baseURI = GetBaseURI();
|
|
|
|
|
2012-09-02 06:35:17 +04:00
|
|
|
nsAutoCString spec;
|
2012-06-23 10:57:01 +04:00
|
|
|
if (baseURI) {
|
2016-09-20 15:02:33 +03:00
|
|
|
nsresult rv = baseURI->GetSpec(spec);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
CopyUTF8toUTF16(spec, aURI);
|
2016-09-20 15:02:33 +03:00
|
|
|
return NS_OK;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2013-09-25 01:56:52 +04:00
|
|
|
void
|
2017-02-01 23:43:36 +03:00
|
|
|
nsINode::GetBaseURIFromJS(nsAString& aURI,
|
|
|
|
CallerType aCallerType,
|
|
|
|
ErrorResult& aRv) const
|
2013-09-25 01:56:52 +04:00
|
|
|
{
|
2017-02-01 23:43:36 +03:00
|
|
|
nsCOMPtr<nsIURI> baseURI = GetBaseURI(aCallerType == CallerType::System);
|
2013-09-25 01:56:52 +04:00
|
|
|
nsAutoCString spec;
|
|
|
|
if (baseURI) {
|
2016-09-08 07:17:58 +03:00
|
|
|
nsresult res = baseURI->GetSpec(spec);
|
|
|
|
if (NS_FAILED(res)) {
|
|
|
|
aRv.Throw(res);
|
|
|
|
return;
|
|
|
|
}
|
2013-09-25 01:56:52 +04:00
|
|
|
}
|
|
|
|
CopyUTF8toUTF16(spec, aURI);
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<nsIURI>
|
|
|
|
nsINode::GetBaseURIObject() const
|
|
|
|
{
|
|
|
|
return GetBaseURI(true);
|
|
|
|
}
|
|
|
|
|
2012-10-09 16:31:24 +04:00
|
|
|
void
|
2012-06-23 10:57:01 +04:00
|
|
|
nsINode::LookupPrefix(const nsAString& aNamespaceURI, nsAString& aPrefix)
|
|
|
|
{
|
|
|
|
Element *element = GetNameSpaceElement();
|
|
|
|
if (element) {
|
|
|
|
// XXX Waiting for DOM spec to list error codes.
|
2016-03-25 13:23:53 +03:00
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
// Trace up the content parent chain looking for the namespace
|
|
|
|
// declaration that defines the aNamespaceURI namespace. Once found,
|
|
|
|
// return the prefix (i.e. the attribute localName).
|
|
|
|
for (nsIContent* content = element; content;
|
|
|
|
content = content->GetParent()) {
|
2017-12-05 20:05:51 +03:00
|
|
|
if (!content->IsElement()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Element* element = content->AsElement();
|
|
|
|
uint32_t attrCount = element->GetAttrCount();
|
2016-03-25 13:23:53 +03:00
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t i = 0; i < attrCount; ++i) {
|
2017-12-05 20:05:51 +03:00
|
|
|
const nsAttrName* name = element->GetAttrNameAt(i);
|
2016-03-25 13:23:53 +03:00
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
if (name->NamespaceEquals(kNameSpaceID_XMLNS) &&
|
2017-12-05 20:05:51 +03:00
|
|
|
element->AttrValueIs(kNameSpaceID_XMLNS, name->LocalName(),
|
2012-06-23 10:57:01 +04:00
|
|
|
aNamespaceURI, eCaseMatters)) {
|
|
|
|
// If the localName is "xmlns", the prefix we output should be
|
|
|
|
// null.
|
2017-12-05 20:05:51 +03:00
|
|
|
nsAtom* localName = name->LocalName();
|
2016-03-25 13:23:53 +03:00
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
if (localName != nsGkAtoms::xmlns) {
|
|
|
|
localName->ToString(aPrefix);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
SetDOMStringToNull(aPrefix);
|
|
|
|
}
|
2012-10-09 16:31:24 +04:00
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SetDOMStringToNull(aPrefix);
|
|
|
|
}
|
|
|
|
|
2012-10-09 16:31:24 +04:00
|
|
|
uint16_t
|
2018-09-05 00:31:57 +03:00
|
|
|
nsINode::CompareDocumentPosition(nsINode& aOtherNode,
|
|
|
|
int32_t* aThisIndex,
|
|
|
|
int32_t* aOtherIndex) const
|
2012-10-09 16:31:24 +04:00
|
|
|
{
|
|
|
|
if (this == &aOtherNode) {
|
2012-06-23 10:57:01 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2013-11-28 16:07:43 +04:00
|
|
|
if (GetPreviousSibling() == &aOtherNode) {
|
|
|
|
MOZ_ASSERT(GetParentNode() == aOtherNode.GetParentNode());
|
2018-06-26 00:20:54 +03:00
|
|
|
return Node_Binding::DOCUMENT_POSITION_PRECEDING;
|
2013-11-28 16:07:43 +04:00
|
|
|
}
|
|
|
|
if (GetNextSibling() == &aOtherNode) {
|
|
|
|
MOZ_ASSERT(GetParentNode() == aOtherNode.GetParentNode());
|
2018-06-26 00:20:54 +03:00
|
|
|
return Node_Binding::DOCUMENT_POSITION_FOLLOWING;
|
2013-11-28 16:07:43 +04:00
|
|
|
}
|
2012-06-23 10:57:01 +04:00
|
|
|
|
2016-02-02 18:36:30 +03:00
|
|
|
AutoTArray<const nsINode*, 32> parents1, parents2;
|
2012-06-23 10:57:01 +04:00
|
|
|
|
2018-04-15 16:12:02 +03:00
|
|
|
const nsINode* node1 = &aOtherNode;
|
|
|
|
const nsINode* node2 = this;
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
// Check if either node is an attribute
|
2018-04-15 16:12:02 +03:00
|
|
|
const Attr* attr1 = Attr::FromNode(node1);
|
|
|
|
if (attr1) {
|
2014-04-10 20:09:50 +04:00
|
|
|
const Element* elem = attr1->GetElement();
|
2012-06-23 10:57:01 +04:00
|
|
|
// If there is an owner element add the attribute
|
|
|
|
// to the chain and walk up to the element
|
|
|
|
if (elem) {
|
|
|
|
node1 = elem;
|
2012-10-09 16:31:24 +04:00
|
|
|
parents1.AppendElement(attr1);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
2018-04-15 16:12:02 +03:00
|
|
|
if (auto* attr2 = Attr::FromNode(node2)) {
|
2014-04-10 20:09:50 +04:00
|
|
|
const Element* elem = attr2->GetElement();
|
2012-06-23 10:57:01 +04:00
|
|
|
if (elem == node1 && attr1) {
|
|
|
|
// Both nodes are attributes on the same element.
|
|
|
|
// Compare position between the attributes.
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t i;
|
2012-06-23 10:57:01 +04:00
|
|
|
const nsAttrName* attrName;
|
|
|
|
for (i = 0; (attrName = elem->GetAttrNameAt(i)); ++i) {
|
|
|
|
if (attrName->Equals(attr1->NodeInfo())) {
|
|
|
|
NS_ASSERTION(!attrName->Equals(attr2->NodeInfo()),
|
|
|
|
"Different attrs at same position");
|
2018-06-26 00:20:54 +03:00
|
|
|
return Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
|
|
|
|
Node_Binding::DOCUMENT_POSITION_PRECEDING;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
if (attrName->Equals(attr2->NodeInfo())) {
|
2018-06-26 00:20:54 +03:00
|
|
|
return Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
|
|
|
|
Node_Binding::DOCUMENT_POSITION_FOLLOWING;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
2018-06-18 08:43:11 +03:00
|
|
|
MOZ_ASSERT_UNREACHABLE("neither attribute in the element");
|
2018-06-26 00:20:54 +03:00
|
|
|
return Node_Binding::DOCUMENT_POSITION_DISCONNECTED;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (elem) {
|
|
|
|
node2 = elem;
|
2012-10-09 16:31:24 +04:00
|
|
|
parents2.AppendElement(attr2);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We now know that both nodes are either nsIContents or nsIDocuments.
|
|
|
|
// If either node started out as an attribute, that attribute will have
|
|
|
|
// the same relative position as its ownerElement, except if the
|
|
|
|
// ownerElement ends up being the container for the other node
|
|
|
|
|
|
|
|
// Build the chain of parents
|
|
|
|
do {
|
|
|
|
parents1.AppendElement(node1);
|
2012-10-09 16:31:24 +04:00
|
|
|
node1 = node1->GetParentNode();
|
2012-06-23 10:57:01 +04:00
|
|
|
} while (node1);
|
|
|
|
do {
|
|
|
|
parents2.AppendElement(node2);
|
2012-10-09 16:31:24 +04:00
|
|
|
node2 = node2->GetParentNode();
|
2012-06-23 10:57:01 +04:00
|
|
|
} while (node2);
|
|
|
|
|
|
|
|
// Check if the nodes are disconnected.
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t pos1 = parents1.Length();
|
|
|
|
uint32_t pos2 = parents2.Length();
|
2012-10-09 16:31:24 +04:00
|
|
|
const nsINode* top1 = parents1.ElementAt(--pos1);
|
|
|
|
const nsINode* top2 = parents2.ElementAt(--pos2);
|
2012-06-23 10:57:01 +04:00
|
|
|
if (top1 != top2) {
|
|
|
|
return top1 < top2 ?
|
2018-06-26 00:20:54 +03:00
|
|
|
(Node_Binding::DOCUMENT_POSITION_PRECEDING |
|
|
|
|
Node_Binding::DOCUMENT_POSITION_DISCONNECTED |
|
|
|
|
Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC) :
|
|
|
|
(Node_Binding::DOCUMENT_POSITION_FOLLOWING |
|
|
|
|
Node_Binding::DOCUMENT_POSITION_DISCONNECTED |
|
|
|
|
Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Find where the parent chain differs and check indices in the parent.
|
2012-10-09 16:31:24 +04:00
|
|
|
const nsINode* parent = top1;
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t len;
|
2013-01-15 16:22:03 +04:00
|
|
|
for (len = std::min(pos1, pos2); len > 0; --len) {
|
2012-10-09 16:31:24 +04:00
|
|
|
const nsINode* child1 = parents1.ElementAt(--pos1);
|
|
|
|
const nsINode* child2 = parents2.ElementAt(--pos2);
|
2012-06-23 10:57:01 +04:00
|
|
|
if (child1 != child2) {
|
|
|
|
// child1 or child2 can be an attribute here. This will work fine since
|
2018-01-23 16:30:18 +03:00
|
|
|
// ComputeIndexOf will return -1 for the attribute making the
|
|
|
|
// attribute be considered before any child.
|
2018-09-05 00:31:57 +03:00
|
|
|
int32_t child1Index;
|
|
|
|
bool cachedChild1Index = false;
|
|
|
|
if (&aOtherNode == child1 && aOtherIndex) {
|
|
|
|
cachedChild1Index = true;
|
|
|
|
child1Index = *aOtherIndex != -1 ?
|
|
|
|
*aOtherIndex : parent->ComputeIndexOf(child1);
|
|
|
|
} else {
|
|
|
|
child1Index = parent->ComputeIndexOf(child1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t child2Index;
|
|
|
|
bool cachedChild2Index = false;
|
|
|
|
if (this == child2 && aThisIndex) {
|
|
|
|
cachedChild2Index = true;
|
|
|
|
child2Index = *aThisIndex != -1 ?
|
|
|
|
*aThisIndex : parent->ComputeIndexOf(child2);
|
|
|
|
} else {
|
|
|
|
child2Index = parent->ComputeIndexOf(child2);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t retVal = child1Index < child2Index ?
|
2018-06-26 00:20:54 +03:00
|
|
|
Node_Binding::DOCUMENT_POSITION_PRECEDING :
|
|
|
|
Node_Binding::DOCUMENT_POSITION_FOLLOWING;
|
2018-09-05 00:31:57 +03:00
|
|
|
|
|
|
|
if (cachedChild1Index) {
|
|
|
|
*aOtherIndex = child1Index;
|
|
|
|
}
|
|
|
|
if (cachedChild2Index) {
|
|
|
|
*aThisIndex = child2Index;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retVal;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
parent = child1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We hit the end of one of the parent chains without finding a difference
|
|
|
|
// between the chains. That must mean that one node is an ancestor of the
|
|
|
|
// other. The one with the shortest chain must be the ancestor.
|
|
|
|
return pos1 < pos2 ?
|
2018-06-26 00:20:54 +03:00
|
|
|
(Node_Binding::DOCUMENT_POSITION_PRECEDING |
|
|
|
|
Node_Binding::DOCUMENT_POSITION_CONTAINS) :
|
|
|
|
(Node_Binding::DOCUMENT_POSITION_FOLLOWING |
|
|
|
|
Node_Binding::DOCUMENT_POSITION_CONTAINED_BY);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2016-03-25 13:23:50 +03:00
|
|
|
bool
|
|
|
|
nsINode::IsSameNode(nsINode *other)
|
|
|
|
{
|
|
|
|
return other == this;
|
|
|
|
}
|
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
bool
|
2012-10-09 16:31:24 +04:00
|
|
|
nsINode::IsEqualNode(nsINode* aOther)
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
|
|
|
if (!aOther) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoString string1, string2;
|
|
|
|
|
|
|
|
nsINode* node1 = this;
|
|
|
|
nsINode* node2 = aOther;
|
|
|
|
do {
|
2012-08-22 19:56:38 +04:00
|
|
|
uint16_t nodeType = node1->NodeType();
|
2012-06-23 10:57:01 +04:00
|
|
|
if (nodeType != node2->NodeType()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-06-20 06:01:40 +04:00
|
|
|
mozilla::dom::NodeInfo* nodeInfo1 = node1->mNodeInfo;
|
|
|
|
mozilla::dom::NodeInfo* nodeInfo2 = node2->mNodeInfo;
|
2012-06-23 10:57:01 +04:00
|
|
|
if (!nodeInfo1->Equals(nodeInfo2) ||
|
|
|
|
nodeInfo1->GetExtraName() != nodeInfo2->GetExtraName()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(nodeType) {
|
2018-01-30 07:10:53 +03:00
|
|
|
case ELEMENT_NODE:
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
|
|
|
// Both are elements (we checked that their nodeinfos are equal). Do the
|
|
|
|
// check on attributes.
|
|
|
|
Element* element1 = node1->AsElement();
|
|
|
|
Element* element2 = node2->AsElement();
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t attrCount = element1->GetAttrCount();
|
2012-06-23 10:57:01 +04:00
|
|
|
if (attrCount != element2->GetAttrCount()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Iterate over attributes.
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t i = 0; i < attrCount; ++i) {
|
2012-06-23 10:57:01 +04:00
|
|
|
const nsAttrName* attrName = element1->GetAttrNameAt(i);
|
|
|
|
#ifdef DEBUG
|
|
|
|
bool hasAttr =
|
|
|
|
#endif
|
|
|
|
element1->GetAttr(attrName->NamespaceID(), attrName->LocalName(),
|
|
|
|
string1);
|
|
|
|
NS_ASSERTION(hasAttr, "Why don't we have an attr?");
|
2016-03-25 13:23:53 +03:00
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
if (!element2->AttrValueIs(attrName->NamespaceID(),
|
|
|
|
attrName->LocalName(),
|
|
|
|
string1,
|
|
|
|
eCaseMatters)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-01-30 07:10:53 +03:00
|
|
|
case TEXT_NODE:
|
|
|
|
case COMMENT_NODE:
|
|
|
|
case CDATA_SECTION_NODE:
|
|
|
|
case PROCESSING_INSTRUCTION_NODE:
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2018-03-29 01:01:47 +03:00
|
|
|
MOZ_ASSERT(node1->IsCharacterData());
|
|
|
|
MOZ_ASSERT(node2->IsCharacterData());
|
2012-06-23 10:57:01 +04:00
|
|
|
string1.Truncate();
|
2018-03-29 01:01:47 +03:00
|
|
|
static_cast<CharacterData*>(node1)->AppendTextTo(string1);
|
2012-06-23 10:57:01 +04:00
|
|
|
string2.Truncate();
|
2018-03-29 01:01:47 +03:00
|
|
|
static_cast<CharacterData*>(node2)->AppendTextTo(string2);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
if (!string1.Equals(string2)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2018-01-30 07:10:53 +03:00
|
|
|
case DOCUMENT_NODE:
|
|
|
|
case DOCUMENT_FRAGMENT_NODE:
|
2012-06-23 10:57:01 +04:00
|
|
|
break;
|
2018-01-30 07:10:53 +03:00
|
|
|
case ATTRIBUTE_NODE:
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
|
|
|
NS_ASSERTION(node1 == this && node2 == aOther,
|
|
|
|
"Did we come upon an attribute node while walking a "
|
|
|
|
"subtree?");
|
2012-10-09 16:31:24 +04:00
|
|
|
node1->GetNodeValue(string1);
|
|
|
|
node2->GetNodeValue(string2);
|
2016-03-25 13:23:53 +03:00
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
// Returning here as to not bother walking subtree. And there is no
|
|
|
|
// risk that we're half way through walking some other subtree since
|
|
|
|
// attribute nodes doesn't appear in subtrees.
|
|
|
|
return string1.Equals(string2);
|
|
|
|
}
|
2018-01-30 07:10:53 +03:00
|
|
|
case DOCUMENT_TYPE_NODE:
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2018-03-13 23:24:01 +03:00
|
|
|
DocumentType* docType1 = static_cast<DocumentType*>(node1);
|
|
|
|
DocumentType* docType2 = static_cast<DocumentType*>(node2);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
// Public ID
|
|
|
|
docType1->GetPublicId(string1);
|
|
|
|
docType2->GetPublicId(string2);
|
|
|
|
if (!string1.Equals(string2)) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-03-25 13:23:53 +03:00
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
// System ID
|
|
|
|
docType1->GetSystemId(string1);
|
|
|
|
docType2->GetSystemId(string2);
|
|
|
|
if (!string1.Equals(string2)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(false, "Unknown node type");
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsINode* nextNode = node1->GetFirstChild();
|
|
|
|
if (nextNode) {
|
|
|
|
node1 = nextNode;
|
|
|
|
node2 = node2->GetFirstChild();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (node2->GetFirstChild()) {
|
|
|
|
// node2 has a firstChild, but node1 doesn't
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find next sibling, possibly walking parent chain.
|
|
|
|
while (1) {
|
|
|
|
if (node1 == this) {
|
|
|
|
NS_ASSERTION(node2 == aOther, "Should have reached the start node "
|
|
|
|
"for both trees at the same time");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
nextNode = node1->GetNextSibling();
|
|
|
|
if (nextNode) {
|
|
|
|
node1 = nextNode;
|
|
|
|
node2 = node2->GetNextSibling();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node2->GetNextSibling()) {
|
|
|
|
// node2 has a nextSibling, but node1 doesn't
|
|
|
|
return false;
|
|
|
|
}
|
2016-03-25 13:23:53 +03:00
|
|
|
|
2012-10-09 16:31:24 +04:00
|
|
|
node1 = node1->GetParentNode();
|
|
|
|
node2 = node2->GetParentNode();
|
2012-06-23 10:57:01 +04:00
|
|
|
NS_ASSERTION(node1 && node2, "no parent while walking subtree");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while(node2);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-10-09 16:31:24 +04:00
|
|
|
void
|
2012-06-23 10:57:01 +04:00
|
|
|
nsINode::LookupNamespaceURI(const nsAString& aNamespacePrefix,
|
|
|
|
nsAString& aNamespaceURI)
|
|
|
|
{
|
|
|
|
Element *element = GetNameSpaceElement();
|
|
|
|
if (!element ||
|
|
|
|
NS_FAILED(element->LookupNamespaceURIInternal(aNamespacePrefix,
|
|
|
|
aNamespaceURI))) {
|
|
|
|
SetDOMStringToNull(aNamespaceURI);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-05 20:42:42 +03:00
|
|
|
bool
|
2018-04-05 20:42:42 +03:00
|
|
|
nsINode::ComputeDefaultWantsUntrusted(ErrorResult& aRv)
|
2018-04-05 20:42:42 +03:00
|
|
|
{
|
|
|
|
return !nsContentUtils::IsChromeDoc(OwnerDoc());
|
|
|
|
}
|
|
|
|
|
2014-03-12 05:11:38 +04:00
|
|
|
void
|
|
|
|
nsINode::GetBoxQuads(const BoxQuadOptions& aOptions,
|
2015-10-18 08:24:48 +03:00
|
|
|
nsTArray<RefPtr<DOMQuad> >& aResult,
|
2017-02-01 23:43:59 +03:00
|
|
|
CallerType aCallerType,
|
2014-03-12 05:11:38 +04:00
|
|
|
mozilla::ErrorResult& aRv)
|
|
|
|
{
|
2017-02-01 23:43:59 +03:00
|
|
|
mozilla::GetBoxQuads(this, aOptions, aResult, aCallerType, aRv);
|
2014-03-12 05:11:38 +04:00
|
|
|
}
|
|
|
|
|
2014-03-12 05:11:39 +04:00
|
|
|
already_AddRefed<DOMQuad>
|
|
|
|
nsINode::ConvertQuadFromNode(DOMQuad& aQuad,
|
|
|
|
const GeometryNode& aFrom,
|
|
|
|
const ConvertCoordinateOptions& aOptions,
|
2017-02-01 23:43:59 +03:00
|
|
|
CallerType aCallerType,
|
2014-03-12 05:11:39 +04:00
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
2017-02-01 23:43:59 +03:00
|
|
|
return mozilla::ConvertQuadFromNode(this, aQuad, aFrom, aOptions, aCallerType,
|
|
|
|
aRv);
|
2014-03-12 05:11:39 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<DOMQuad>
|
|
|
|
nsINode::ConvertRectFromNode(DOMRectReadOnly& aRect,
|
|
|
|
const GeometryNode& aFrom,
|
|
|
|
const ConvertCoordinateOptions& aOptions,
|
2017-02-01 23:43:59 +03:00
|
|
|
CallerType aCallerType,
|
2014-03-12 05:11:39 +04:00
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
2017-02-01 23:43:59 +03:00
|
|
|
return mozilla::ConvertRectFromNode(this, aRect, aFrom, aOptions, aCallerType,
|
|
|
|
aRv);
|
2014-03-12 05:11:39 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<DOMPoint>
|
|
|
|
nsINode::ConvertPointFromNode(const DOMPointInit& aPoint,
|
|
|
|
const GeometryNode& aFrom,
|
|
|
|
const ConvertCoordinateOptions& aOptions,
|
2017-02-01 23:43:59 +03:00
|
|
|
CallerType aCallerType,
|
2014-03-12 05:11:39 +04:00
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
2017-02-01 23:43:59 +03:00
|
|
|
return mozilla::ConvertPointFromNode(this, aPoint, aFrom, aOptions,
|
|
|
|
aCallerType, aRv);
|
2014-03-12 05:11:39 +04:00
|
|
|
}
|
|
|
|
|
2018-04-05 20:42:41 +03:00
|
|
|
bool
|
|
|
|
nsINode::DispatchEvent(Event& aEvent, CallerType aCallerType, ErrorResult& aRv)
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
|
|
|
// XXX sXBL/XBL2 issue -- do we really want the owner here? What
|
|
|
|
// if that's the XBL document? Would we want its presshell? Or what?
|
|
|
|
nsCOMPtr<nsIDocument> document = OwnerDoc();
|
|
|
|
|
|
|
|
// Do nothing if the element does not belong to a document
|
|
|
|
if (!document) {
|
2018-04-05 20:42:41 +03:00
|
|
|
return true;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain a presentation shell
|
2018-02-21 01:00:10 +03:00
|
|
|
RefPtr<nsPresContext> context = document->GetPresContext();
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
|
|
nsresult rv =
|
2018-04-05 20:42:41 +03:00
|
|
|
EventDispatcher::DispatchDOMEvent(this, nullptr, &aEvent, context, &status);
|
|
|
|
bool retval = !aEvent.DefaultPrevented(aCallerType);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
aRv.Throw(rv);
|
|
|
|
}
|
|
|
|
return retval;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2014-03-18 08:48:20 +04:00
|
|
|
nsINode::PostHandleEvent(EventChainPostVisitor& /*aVisitor*/)
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2014-03-17 10:56:53 +04:00
|
|
|
EventListenerManager*
|
2013-10-23 03:32:04 +04:00
|
|
|
nsINode::GetOrCreateListenerManager()
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2013-10-23 03:32:04 +04:00
|
|
|
return nsContentUtils::GetListenerManagerForNode(this);
|
|
|
|
}
|
|
|
|
|
2014-03-17 10:56:53 +04:00
|
|
|
EventListenerManager*
|
2013-10-23 03:32:04 +04:00
|
|
|
nsINode::GetExistingListenerManager() const
|
|
|
|
{
|
|
|
|
return nsContentUtils::GetExistingListenerManagerForNode(this);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2016-01-30 20:05:36 +03:00
|
|
|
nsPIDOMWindowOuter*
|
2015-05-12 22:56:39 +03:00
|
|
|
nsINode::GetOwnerGlobalForBindings()
|
2013-05-31 01:46:39 +04:00
|
|
|
{
|
|
|
|
bool dummy;
|
2017-11-04 01:25:38 +03:00
|
|
|
auto* window = static_cast<nsGlobalWindowInner*>(OwnerDoc()->GetScriptHandlingObject(dummy));
|
2016-01-30 20:05:36 +03:00
|
|
|
return window ? nsPIDOMWindowOuter::GetFromCurrentInner(window->AsInner()) : nullptr;
|
2013-05-31 01:46:39 +04:00
|
|
|
}
|
|
|
|
|
2015-05-12 22:56:39 +03:00
|
|
|
nsIGlobalObject*
|
|
|
|
nsINode::GetOwnerGlobal() const
|
|
|
|
{
|
|
|
|
bool dummy;
|
|
|
|
return OwnerDoc()->GetScriptHandlingObject(dummy);
|
|
|
|
}
|
|
|
|
|
2012-06-23 10:58:04 +04:00
|
|
|
bool
|
|
|
|
nsINode::UnoptimizableCCNode() const
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2013-09-24 23:28:32 +04:00
|
|
|
const uintptr_t problematicFlags = (NODE_IS_ANONYMOUS_ROOT |
|
2014-04-06 23:32:38 +04:00
|
|
|
NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE |
|
2012-10-02 12:24:11 +04:00
|
|
|
NODE_IS_NATIVE_ANONYMOUS_ROOT |
|
2018-05-12 00:28:39 +03:00
|
|
|
NODE_MAY_BE_IN_BINDING_MNGR);
|
2012-06-23 10:58:04 +04:00
|
|
|
return HasFlag(problematicFlags) ||
|
2018-01-30 07:10:53 +03:00
|
|
|
NodeType() == ATTRIBUTE_NODE ||
|
2012-06-23 10:57:01 +04:00
|
|
|
// For strange cases like xbl:content/xbl:children
|
2012-06-23 10:58:04 +04:00
|
|
|
(IsElement() &&
|
|
|
|
AsElement()->IsInNamespace(kNameSpaceID_XBL));
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
bool
|
|
|
|
nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb)
|
|
|
|
{
|
2012-10-26 17:32:10 +04:00
|
|
|
if (MOZ_LIKELY(!cb.WantAllTraces())) {
|
2018-05-12 00:28:39 +03:00
|
|
|
nsIDocument* currentDoc = tmp->GetComposedDoc();
|
2012-06-23 10:57:01 +04:00
|
|
|
if (currentDoc &&
|
|
|
|
nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nsCCUncollectableMarker::sGeneration) {
|
|
|
|
// If we're black no need to traverse.
|
2017-01-25 04:38:58 +03:00
|
|
|
if (tmp->HasKnownLiveWrapper() || tmp->InCCBlackTree()) {
|
2012-06-23 10:57:01 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-06-23 10:58:04 +04:00
|
|
|
if (!tmp->UnoptimizableCCNode()) {
|
2012-06-23 10:57:01 +04:00
|
|
|
// If we're in a black document, return early.
|
2017-01-25 04:38:58 +03:00
|
|
|
if ((currentDoc && currentDoc->HasKnownLiveWrapper())) {
|
2012-06-23 10:57:01 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// If we're not in anonymous content and we have a black parent,
|
|
|
|
// return early.
|
|
|
|
nsIContent* parent = tmp->GetParent();
|
2017-01-25 04:38:58 +03:00
|
|
|
if (parent && !parent->UnoptimizableCCNode() &&
|
|
|
|
parent->HasKnownLiveWrapper()) {
|
2018-01-23 16:30:18 +03:00
|
|
|
MOZ_ASSERT(parent->ComputeIndexOf(tmp) >= 0, "Parent doesn't own us?");
|
2012-06-23 10:57:01 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-15 11:32:40 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfo)
|
2018-08-06 22:29:27 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstChild)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextSibling)
|
2012-06-23 10:57:01 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetParent())
|
|
|
|
|
|
|
|
nsSlots *slots = tmp->GetExistingSlots();
|
|
|
|
if (slots) {
|
|
|
|
slots->Traverse(cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tmp->HasProperties()) {
|
|
|
|
nsCOMArray<nsISupports>* objects =
|
|
|
|
static_cast<nsCOMArray<nsISupports>*>(tmp->GetProperty(nsGkAtoms::keepobjectsalive));
|
|
|
|
if (objects) {
|
2012-08-22 19:56:38 +04:00
|
|
|
for (int32_t i = 0; i < objects->Count(); ++i) {
|
2012-06-23 10:57:01 +04:00
|
|
|
cb.NoteXPCOMChild(objects->ObjectAt(i));
|
|
|
|
}
|
|
|
|
}
|
2018-05-26 16:47:48 +03:00
|
|
|
|
2018-06-07 19:43:06 +03:00
|
|
|
#ifdef ACCESSIBILITY
|
2018-05-26 16:47:48 +03:00
|
|
|
AccessibleNode* anode =
|
|
|
|
static_cast<AccessibleNode*>(tmp->GetProperty(nsGkAtoms::accessiblenode));
|
|
|
|
if (anode) {
|
|
|
|
cb.NoteXPCOMChild(anode);
|
|
|
|
}
|
2018-06-07 19:43:06 +03:00
|
|
|
#endif
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-01-30 07:10:53 +03:00
|
|
|
if (tmp->NodeType() != DOCUMENT_NODE &&
|
2012-06-23 10:57:01 +04:00
|
|
|
tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) {
|
|
|
|
nsContentUtils::TraverseListenerManager(tmp, cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
void
|
2013-08-02 11:04:01 +04:00
|
|
|
nsINode::Unlink(nsINode* tmp)
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2013-08-02 11:04:01 +04:00
|
|
|
tmp->ReleaseWrapper(tmp);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
nsSlots *slots = tmp->GetExistingSlots();
|
|
|
|
if (slots) {
|
|
|
|
slots->Unlink();
|
|
|
|
}
|
|
|
|
|
2018-01-30 07:10:53 +03:00
|
|
|
if (tmp->NodeType() != DOCUMENT_NODE &&
|
2012-06-23 10:57:01 +04:00
|
|
|
tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) {
|
|
|
|
nsContentUtils::RemoveListenerManager(tmp);
|
|
|
|
tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tmp->HasProperties()) {
|
|
|
|
tmp->DeleteProperty(nsGkAtoms::keepobjectsalive);
|
2018-05-26 16:47:48 +03:00
|
|
|
tmp->DeleteProperty(nsGkAtoms::accessiblenode);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-13 20:34:55 +03:00
|
|
|
static void
|
|
|
|
AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode, ErrorResult& aError)
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2012-10-09 16:31:24 +04:00
|
|
|
NS_ASSERTION(!aNode->GetParentNode(),
|
2012-06-23 10:57:01 +04:00
|
|
|
"Should have removed from parent already");
|
|
|
|
|
|
|
|
nsIDocument *doc = aParent->OwnerDoc();
|
|
|
|
|
2017-09-13 20:34:55 +03:00
|
|
|
DebugOnly<nsINode*> adoptedNode = doc->AdoptNode(*aNode, aError);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
2017-09-13 20:34:55 +03:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (!aError.Failed()) {
|
|
|
|
MOZ_ASSERT(aParent->OwnerDoc() == doc,
|
2012-06-23 10:57:01 +04:00
|
|
|
"ownerDoc chainged while adopting");
|
2017-09-13 20:34:55 +03:00
|
|
|
MOZ_ASSERT(adoptedNode == aNode, "Uh, adopt node changed nodes?");
|
|
|
|
MOZ_ASSERT(aParent->OwnerDoc() == aNode->OwnerDoc(),
|
2012-06-23 10:57:01 +04:00
|
|
|
"ownerDocument changed again after adopting!");
|
2017-09-13 20:34:55 +03:00
|
|
|
}
|
|
|
|
#endif // DEBUG
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2017-09-13 20:34:55 +03:00
|
|
|
static void
|
|
|
|
CheckForOutdatedParent(nsINode* aParent, nsINode* aNode, ErrorResult& aError)
|
2013-08-16 01:39:28 +04:00
|
|
|
{
|
2013-11-20 19:37:30 +04:00
|
|
|
if (JSObject* existingObjUnrooted = aNode->GetWrapper()) {
|
2016-08-11 15:39:22 +03:00
|
|
|
JS::Rooted<JSObject*> existingObj(RootingCx(), existingObjUnrooted);
|
2014-07-18 20:37:42 +04:00
|
|
|
|
2013-11-20 19:37:30 +04:00
|
|
|
AutoJSContext cx;
|
2013-08-16 01:39:28 +04:00
|
|
|
nsIGlobalObject* global = aParent->OwnerDoc()->GetScopeObject();
|
|
|
|
MOZ_ASSERT(global);
|
|
|
|
|
2018-07-12 13:14:37 +03:00
|
|
|
if (JS::GetNonCCWObjectGlobal(existingObj) !=
|
2013-08-16 01:39:28 +04:00
|
|
|
global->GetGlobalJSObject()) {
|
2018-08-02 09:48:40 +03:00
|
|
|
JSAutoRealm ar(cx, existingObj);
|
2017-09-13 20:34:55 +03:00
|
|
|
ReparentWrapper(cx, existingObj, aError);
|
2013-08-16 01:39:28 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-15 02:17:42 +03:00
|
|
|
static nsresult
|
|
|
|
ReparentWrappersInSubtree(nsIContent* aRoot)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(ShouldUseXBLScope(aRoot));
|
|
|
|
// Start off with no global so we don't fire any error events on failure.
|
|
|
|
AutoJSAPI jsapi;
|
|
|
|
jsapi.Init();
|
|
|
|
|
|
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
|
|
|
|
ErrorResult rv;
|
|
|
|
JS::Rooted<JSObject*> reflector(cx);
|
|
|
|
for (nsIContent* cur = aRoot; cur; cur = cur->GetNextNode(aRoot)) {
|
|
|
|
if ((reflector = cur->GetWrapper())) {
|
2018-08-02 09:48:40 +03:00
|
|
|
JSAutoRealm ar(cx, reflector);
|
2018-06-15 02:17:42 +03:00
|
|
|
ReparentWrapper(cx, reflector, rv);
|
|
|
|
rv.WouldReportJSException();
|
|
|
|
if (rv.Failed()) {
|
|
|
|
// We _could_ consider BlastSubtreeToPieces here, but it's not really
|
|
|
|
// needed. Having some nodes in here accessible to content while others
|
|
|
|
// are not is probably OK. We just need to fail out of the actual
|
|
|
|
// insertion, so they're not in the DOM. Returning a failure here will
|
|
|
|
// do that.
|
|
|
|
return rv.StealNSResult();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
nsresult
|
2018-08-06 22:29:27 +03:00
|
|
|
nsINode::InsertChildBefore(nsIContent* aKid, nsIContent* aChildToInsertBefore,
|
|
|
|
bool aNotify)
|
|
|
|
{
|
|
|
|
if (!IsContainerNode()) {
|
|
|
|
return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
|
|
|
|
}
|
|
|
|
|
2017-10-23 10:04:14 +03:00
|
|
|
MOZ_ASSERT(!aKid->GetParentNode(), "Inserting node that already has parent");
|
2018-04-15 16:12:02 +03:00
|
|
|
MOZ_ASSERT(!IsAttr());
|
2017-08-18 06:56:38 +03:00
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
// The id-handling code, and in the future possibly other code, need to
|
|
|
|
// react to unexpected attribute changes.
|
|
|
|
nsMutationGuard::DidMutate();
|
|
|
|
|
|
|
|
// Do this before checking the child-count since this could cause mutations
|
2014-10-02 22:45:44 +04:00
|
|
|
nsIDocument* doc = GetUncomposedDoc();
|
2018-05-15 16:56:38 +03:00
|
|
|
mozAutoDocUpdate updateBatch(GetComposedDoc(), aNotify);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
2013-08-02 14:24:41 +04:00
|
|
|
if (OwnerDoc() != aKid->OwnerDoc()) {
|
2017-09-13 20:34:55 +03:00
|
|
|
ErrorResult error;
|
|
|
|
AdoptNodeIntoOwnerDoc(this, aKid, error);
|
|
|
|
|
|
|
|
// Need to WouldReportJSException() if our callee can throw a JS
|
|
|
|
// exception (which it can) and we're neither propagating the
|
|
|
|
// error out nor unconditionally suppressing it.
|
|
|
|
error.WouldReportJSException();
|
|
|
|
if (NS_WARN_IF(error.Failed())) {
|
|
|
|
return error.StealNSResult();
|
|
|
|
}
|
2013-08-16 01:39:28 +04:00
|
|
|
} else if (OwnerDoc()->DidDocumentOpen()) {
|
2017-09-13 20:34:55 +03:00
|
|
|
ErrorResult error;
|
|
|
|
CheckForOutdatedParent(this, aKid, error);
|
|
|
|
|
|
|
|
// Need to WouldReportJSException() if our callee can throw a JS
|
|
|
|
// exception (which it can) and we're neither propagating the
|
|
|
|
// error out nor unconditionally suppressing it.
|
|
|
|
error.WouldReportJSException();
|
|
|
|
if (NS_WARN_IF(error.Failed())) {
|
|
|
|
return error.StealNSResult();
|
|
|
|
}
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-08-06 22:29:27 +03:00
|
|
|
if (!aChildToInsertBefore) {
|
|
|
|
AppendChildToChildList(aKid);
|
|
|
|
} else {
|
|
|
|
InsertChildToChildList(aKid, aChildToInsertBefore);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-04-15 12:43:15 +03:00
|
|
|
nsIContent* parent = IsContent() ? AsContent() : nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
|
2018-06-15 02:17:42 +03:00
|
|
|
bool wasInXBLScope = ShouldUseXBLScope(aKid);
|
2018-08-06 22:29:27 +03:00
|
|
|
nsresult rv = aKid->BindToTree(doc, parent,
|
|
|
|
parent ? parent->GetBindingParent() : nullptr);
|
2018-06-15 02:17:42 +03:00
|
|
|
if (NS_SUCCEEDED(rv) && !wasInXBLScope && ShouldUseXBLScope(aKid)) {
|
|
|
|
MOZ_ASSERT(ShouldUseXBLScope(this),
|
|
|
|
"Why does the kid need to use an XBL scope?");
|
|
|
|
rv = ReparentWrappersInSubtree(aKid);
|
|
|
|
}
|
2012-06-23 10:57:01 +04:00
|
|
|
if (NS_FAILED(rv)) {
|
2018-08-06 22:29:27 +03:00
|
|
|
DisconnectChild(aKid);
|
2012-06-23 10:57:01 +04:00
|
|
|
aKid->UnbindFromTree();
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2017-10-23 10:04:14 +03:00
|
|
|
// Invalidate cached array of child nodes
|
|
|
|
InvalidateChildNodes();
|
|
|
|
|
2012-10-09 16:31:24 +04:00
|
|
|
NS_ASSERTION(aKid->GetParentNode() == this,
|
2012-06-23 10:57:01 +04:00
|
|
|
"Did we run script inappropriately?");
|
|
|
|
|
|
|
|
if (aNotify) {
|
|
|
|
// Note that we always want to call ContentInserted when things are added
|
|
|
|
// as kids to documents
|
2018-08-06 22:29:27 +03:00
|
|
|
if (parent && !aChildToInsertBefore) {
|
2017-07-27 16:49:52 +03:00
|
|
|
nsNodeUtils::ContentAppended(parent, aKid);
|
2012-06-23 10:57:01 +04:00
|
|
|
} else {
|
2017-07-27 16:49:52 +03:00
|
|
|
nsNodeUtils::ContentInserted(this, aKid);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (nsContentUtils::HasMutationListeners(aKid,
|
|
|
|
NS_EVENT_BITS_MUTATION_NODEINSERTED, this)) {
|
2015-09-07 17:55:51 +03:00
|
|
|
InternalMutationEvent mutation(true, eLegacyNodeInserted);
|
2018-05-30 05:58:49 +03:00
|
|
|
mutation.mRelatedNode = this;
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
mozAutoSubtreeModified subtree(OwnerDoc(), this);
|
2014-03-17 10:56:54 +04:00
|
|
|
(new AsyncEventDispatcher(aKid, mutation))->RunDOMEventWhenSafe();
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-08-06 22:29:27 +03:00
|
|
|
nsIContent*
|
|
|
|
nsINode::GetPreviousSibling() const
|
|
|
|
{
|
|
|
|
// Do not expose circular linked list
|
|
|
|
if (mPreviousOrLastSibling && !mPreviousOrLastSibling->mNextSibling) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return mPreviousOrLastSibling;
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:30:05 +03:00
|
|
|
// CACHE_POINTER_SHIFT indicates how many steps to downshift the |this| pointer.
|
|
|
|
// It should be small enough to not cause collisions between adjecent objects,
|
|
|
|
// and large enough to make sure that all indexes are used.
|
|
|
|
#define CACHE_POINTER_SHIFT 6
|
|
|
|
#define CACHE_NUM_SLOTS 128
|
|
|
|
#define CACHE_CHILD_LIMIT 10
|
|
|
|
|
|
|
|
#define CACHE_GET_INDEX(_parent) \
|
|
|
|
((NS_PTR_TO_INT32(_parent) >> CACHE_POINTER_SHIFT) & \
|
|
|
|
(CACHE_NUM_SLOTS - 1))
|
|
|
|
|
|
|
|
struct IndexCacheSlot
|
|
|
|
{
|
|
|
|
const nsINode* mParent;
|
|
|
|
const nsINode* mChild;
|
|
|
|
int32_t mChildIndex;
|
|
|
|
};
|
|
|
|
|
|
|
|
static IndexCacheSlot sIndexCache[CACHE_NUM_SLOTS];
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
AddChildAndIndexToCache(const nsINode* aParent, const nsINode* aChild,
|
|
|
|
int32_t aChildIndex)
|
|
|
|
{
|
|
|
|
uint32_t index = CACHE_GET_INDEX(aParent);
|
|
|
|
sIndexCache[index].mParent = aParent;
|
|
|
|
sIndexCache[index].mChild = aChild;
|
|
|
|
sIndexCache[index].mChildIndex = aChildIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
GetChildAndIndexFromCache(const nsINode* aParent,
|
|
|
|
const nsINode** aChild,
|
|
|
|
int32_t* aChildIndex)
|
|
|
|
{
|
|
|
|
uint32_t index = CACHE_GET_INDEX(aParent);
|
|
|
|
if (sIndexCache[index].mParent == aParent) {
|
|
|
|
*aChild = sIndexCache[index].mChild;
|
|
|
|
*aChildIndex = sIndexCache[index].mChildIndex;
|
|
|
|
} else {
|
|
|
|
*aChild = nullptr;
|
|
|
|
*aChildIndex = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
RemoveFromCache(const nsINode* aParent)
|
|
|
|
{
|
|
|
|
uint32_t index = CACHE_GET_INDEX(aParent);
|
|
|
|
if (sIndexCache[index].mParent == aParent) {
|
|
|
|
sIndexCache[index] = { nullptr, nullptr, -1 };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 22:29:27 +03:00
|
|
|
void
|
|
|
|
nsINode::AppendChildToChildList(nsIContent* aKid)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aKid);
|
|
|
|
MOZ_ASSERT(!aKid->mNextSibling);
|
|
|
|
|
2018-09-05 00:30:05 +03:00
|
|
|
RemoveFromCache(this);
|
|
|
|
|
2018-08-06 22:29:27 +03:00
|
|
|
if (mFirstChild) {
|
|
|
|
nsIContent* lastChild = GetLastChild();
|
|
|
|
lastChild->mNextSibling = aKid;
|
|
|
|
aKid->mPreviousOrLastSibling = lastChild;
|
|
|
|
} else {
|
|
|
|
mFirstChild = aKid;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Maintain link to the last child
|
|
|
|
mFirstChild->mPreviousOrLastSibling = aKid;
|
|
|
|
++mChildCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsINode::InsertChildToChildList(nsIContent* aKid, nsIContent* aNextSibling)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aKid);
|
|
|
|
MOZ_ASSERT(aNextSibling);
|
|
|
|
|
2018-09-05 00:30:05 +03:00
|
|
|
RemoveFromCache(this);
|
|
|
|
|
2018-08-06 22:29:27 +03:00
|
|
|
nsIContent* previousSibling = aNextSibling->mPreviousOrLastSibling;
|
|
|
|
aNextSibling->mPreviousOrLastSibling = aKid;
|
|
|
|
aKid->mPreviousOrLastSibling = previousSibling;
|
|
|
|
aKid->mNextSibling = aNextSibling;
|
|
|
|
|
|
|
|
if (aNextSibling == mFirstChild) {
|
|
|
|
MOZ_ASSERT(!previousSibling->mNextSibling);
|
|
|
|
mFirstChild = aKid;
|
|
|
|
} else {
|
|
|
|
previousSibling->mNextSibling = aKid;
|
|
|
|
}
|
|
|
|
|
|
|
|
++mChildCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsINode::DisconnectChild(nsIContent* aKid)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aKid);
|
|
|
|
MOZ_ASSERT(GetChildCount() > 0);
|
|
|
|
|
2018-09-05 00:30:05 +03:00
|
|
|
RemoveFromCache(this);
|
|
|
|
|
2018-08-06 22:29:27 +03:00
|
|
|
nsIContent* previousSibling = aKid->GetPreviousSibling();
|
|
|
|
nsCOMPtr<nsIContent> ref = aKid;
|
|
|
|
|
|
|
|
if (aKid->mNextSibling) {
|
|
|
|
aKid->mNextSibling->mPreviousOrLastSibling = aKid->mPreviousOrLastSibling;
|
|
|
|
} else {
|
|
|
|
// aKid is the last child in the list
|
|
|
|
mFirstChild->mPreviousOrLastSibling = aKid->mPreviousOrLastSibling;
|
|
|
|
}
|
|
|
|
aKid->mPreviousOrLastSibling = nullptr;
|
|
|
|
|
|
|
|
if (previousSibling) {
|
|
|
|
previousSibling->mNextSibling = aKid->mNextSibling.forget();
|
|
|
|
} else {
|
|
|
|
// aKid is the first child in the list
|
|
|
|
mFirstChild = aKid->mNextSibling.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
--mChildCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent*
|
|
|
|
nsINode::GetChildAt_Deprecated(uint32_t aIndex) const
|
|
|
|
{
|
|
|
|
if (aIndex >= GetChildCount()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent* child = mFirstChild;
|
|
|
|
while (aIndex--) {
|
|
|
|
child = child->GetNextSibling();
|
|
|
|
}
|
|
|
|
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t
|
|
|
|
nsINode::ComputeIndexOf(const nsINode* aChild) const
|
|
|
|
{
|
|
|
|
if (!aChild) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aChild->GetParentNode() != this) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aChild == GetLastChild()) {
|
|
|
|
return GetChildCount() - 1;
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:30:05 +03:00
|
|
|
if (mChildCount >= CACHE_CHILD_LIMIT) {
|
|
|
|
const nsINode* child;
|
|
|
|
int32_t childIndex;
|
|
|
|
GetChildAndIndexFromCache(this, &child, &childIndex);
|
|
|
|
if (child) {
|
|
|
|
if (child == aChild) {
|
|
|
|
return childIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t nextIndex = childIndex;
|
|
|
|
int32_t prevIndex = childIndex;
|
|
|
|
nsINode* prev = child->GetPreviousSibling();
|
|
|
|
nsINode* next = child->GetNextSibling();
|
|
|
|
do {
|
|
|
|
if (next) {
|
|
|
|
++nextIndex;
|
|
|
|
if (next == aChild) {
|
|
|
|
AddChildAndIndexToCache(this, aChild, nextIndex);
|
|
|
|
return nextIndex;
|
|
|
|
}
|
|
|
|
next = next->GetNextSibling();
|
|
|
|
}
|
|
|
|
if (prev) {
|
|
|
|
--prevIndex;
|
|
|
|
if (prev == aChild) {
|
|
|
|
AddChildAndIndexToCache(this, aChild, prevIndex);
|
|
|
|
return prevIndex;
|
|
|
|
}
|
|
|
|
prev = prev->GetPreviousSibling();
|
|
|
|
}
|
|
|
|
} while (prev || next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 22:29:27 +03:00
|
|
|
int32_t index = 0;
|
|
|
|
nsINode* current = mFirstChild;
|
|
|
|
while (current) {
|
|
|
|
MOZ_ASSERT(current->GetParentNode() == this);
|
|
|
|
if (current == aChild) {
|
2018-09-05 00:30:05 +03:00
|
|
|
if (mChildCount >= CACHE_CHILD_LIMIT) {
|
|
|
|
AddChildAndIndexToCache(this, current, index);
|
|
|
|
}
|
2018-08-06 22:29:27 +03:00
|
|
|
return index;
|
|
|
|
}
|
|
|
|
current = current->GetNextSibling();
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-04-12 05:48:14 +03:00
|
|
|
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.
|
|
|
|
*/
|
2018-06-02 08:26:00 +03:00
|
|
|
MOZ_CAN_RUN_SCRIPT static already_AddRefed<nsINode>
|
2016-04-12 05:48:14 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-09-27 22:36:41 +03:00
|
|
|
nsCOMPtr<nsINode> viablePreviousSibling =
|
|
|
|
FindViablePreviousSibling(*this, aNodes);
|
2016-04-12 05:48:14 +03:00
|
|
|
|
2018-06-02 08:26:00 +03:00
|
|
|
nsCOMPtr<nsIDocument> doc = OwnerDoc();
|
|
|
|
nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
|
2016-04-12 05:48:14 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-09-27 22:36:41 +03:00
|
|
|
nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes);
|
2016-04-12 05:48:14 +03:00
|
|
|
|
2018-06-02 08:26:00 +03:00
|
|
|
nsCOMPtr<nsIDocument> doc = OwnerDoc();
|
|
|
|
nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
|
2016-04-12 05:48:14 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-09-27 22:36:41 +03:00
|
|
|
nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes);
|
2016-04-12 05:48:14 +03:00
|
|
|
|
2018-06-02 08:26:00 +03:00
|
|
|
nsCOMPtr<nsIDocument> doc = OwnerDoc();
|
|
|
|
nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
|
2016-04-12 05:48:14 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parent == GetParentNode()) {
|
|
|
|
parent->ReplaceChild(*node, *this, aRv);
|
|
|
|
} else {
|
|
|
|
parent->InsertBefore(*node, viableNextSibling, aRv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-13 11:08:47 +04:00
|
|
|
void
|
|
|
|
nsINode::Remove()
|
|
|
|
{
|
2013-04-24 22:09:25 +04:00
|
|
|
nsCOMPtr<nsINode> parent = GetParentNode();
|
2013-04-13 11:08:47 +04:00
|
|
|
if (!parent) {
|
|
|
|
return;
|
|
|
|
}
|
2017-03-08 21:05:34 +03:00
|
|
|
|
2018-02-01 22:21:14 +03:00
|
|
|
parent->RemoveChild(*this, IgnoreErrors());
|
2013-04-13 11:08:47 +04:00
|
|
|
}
|
|
|
|
|
2013-07-22 16:15:43 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-07-26 23:40:13 +03:00
|
|
|
static
|
|
|
|
bool MatchAttribute(Element* aElement,
|
|
|
|
int32_t aNamespaceID,
|
|
|
|
nsAtom* aAttrName,
|
|
|
|
void* aData)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aElement, "Must have content node to work with!");
|
|
|
|
nsString* attrValue = static_cast<nsString*>(aData);
|
|
|
|
if (aNamespaceID != kNameSpaceID_Unknown &&
|
|
|
|
aNamespaceID != kNameSpaceID_Wildcard) {
|
|
|
|
return attrValue->EqualsLiteral("*") ?
|
|
|
|
aElement->HasAttr(aNamespaceID, aAttrName) :
|
|
|
|
aElement->AttrValueIs(aNamespaceID, aAttrName, *attrValue,
|
|
|
|
eCaseMatters);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Qualified name match. This takes more work.
|
|
|
|
uint32_t count = aElement->GetAttrCount();
|
|
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
|
|
const nsAttrName* name = aElement->GetAttrNameAt(i);
|
|
|
|
bool nameMatch;
|
|
|
|
if (name->IsAtom()) {
|
|
|
|
nameMatch = name->Atom() == aAttrName;
|
|
|
|
} else if (aNamespaceID == kNameSpaceID_Wildcard) {
|
|
|
|
nameMatch = name->NodeInfo()->Equals(aAttrName);
|
|
|
|
} else {
|
|
|
|
nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nameMatch) {
|
|
|
|
return attrValue->EqualsLiteral("*") ||
|
|
|
|
aElement->AttrValueIs(name->NamespaceID(), name->LocalName(),
|
|
|
|
*attrValue, eCaseMatters);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<nsIHTMLCollection>
|
|
|
|
nsINode::GetElementsByAttribute(const nsAString& aAttribute,
|
|
|
|
const nsAString& aValue)
|
|
|
|
{
|
|
|
|
RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
|
|
|
|
nsAutoPtr<nsString> attrValue(new nsString(aValue));
|
|
|
|
RefPtr<nsContentList> list = new nsContentList(this,
|
|
|
|
MatchAttribute,
|
|
|
|
nsContentUtils::DestroyMatchString,
|
|
|
|
attrValue.forget(),
|
|
|
|
true,
|
|
|
|
attrAtom,
|
|
|
|
kNameSpaceID_Unknown);
|
|
|
|
|
|
|
|
return list.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<nsIHTMLCollection>
|
|
|
|
nsINode::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
|
|
|
|
const nsAString& aAttribute,
|
|
|
|
const nsAString& aValue,
|
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
|
|
|
|
nsAutoPtr<nsString> attrValue(new nsString(aValue));
|
|
|
|
|
|
|
|
int32_t nameSpaceId = kNameSpaceID_Wildcard;
|
|
|
|
if (!aNamespaceURI.EqualsLiteral("*")) {
|
|
|
|
nsresult rv =
|
|
|
|
nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
|
|
|
|
nameSpaceId);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
aRv.Throw(rv);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<nsContentList> list = new nsContentList(this,
|
|
|
|
MatchAttribute,
|
|
|
|
nsContentUtils::DestroyMatchString,
|
|
|
|
attrValue.forget(),
|
|
|
|
true,
|
|
|
|
attrAtom,
|
|
|
|
nameSpaceId);
|
|
|
|
return list.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-12 05:48:14 +03:00
|
|
|
void
|
|
|
|
nsINode::Prepend(const Sequence<OwningNodeOrString>& aNodes,
|
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
2018-06-02 08:26:00 +03:00
|
|
|
nsCOMPtr<nsIDocument> doc = OwnerDoc();
|
|
|
|
nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
|
2016-04-12 05:48:14 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-08-06 22:29:27 +03:00
|
|
|
nsCOMPtr<nsIContent> refNode = mFirstChild;;
|
2016-10-13 00:34:25 +03:00
|
|
|
InsertBefore(*node, refNode, aRv);
|
2016-04-12 05:48:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsINode::Append(const Sequence<OwningNodeOrString>& aNodes,
|
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
2018-06-02 08:26:00 +03:00
|
|
|
nsCOMPtr<nsIDocument> doc = OwnerDoc();
|
|
|
|
nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
|
2016-04-12 05:48:14 +03:00
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AppendChild(*node, aRv);
|
|
|
|
}
|
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
void
|
2018-08-06 22:29:27 +03:00
|
|
|
nsINode::RemoveChildNode(nsIContent* aKid, bool aNotify)
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2017-05-03 02:08:02 +03:00
|
|
|
// NOTE: This function must not trigger any calls to
|
|
|
|
// nsIDocument::GetRootElement() calls until *after* it has removed aKid from
|
|
|
|
// aChildArray. Any calls before then could potentially restore a stale
|
2018-01-03 16:01:03 +03:00
|
|
|
// value for our cached root element, per note in
|
2018-01-15 19:18:38 +03:00
|
|
|
// nsDocument::RemoveChildNode().
|
2018-08-06 22:29:27 +03:00
|
|
|
MOZ_ASSERT(aKid && aKid->GetParentNode() == this, "Bogus aKid");
|
2018-04-15 16:12:02 +03:00
|
|
|
MOZ_ASSERT(!IsAttr());
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
nsMutationGuard::DidMutate();
|
2018-05-15 16:56:38 +03:00
|
|
|
mozAutoDocUpdate updateBatch(GetComposedDoc(), aNotify);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
nsIContent* previousSibling = aKid->GetPreviousSibling();
|
|
|
|
|
2018-08-06 22:29:27 +03:00
|
|
|
// Since aKid is use also after DisconnectChild, ensure it stays alive.
|
|
|
|
nsCOMPtr<nsIContent> kungfuDeathGrip = aKid;
|
|
|
|
DisconnectChild(aKid);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
2017-10-23 10:04:14 +03:00
|
|
|
// Invalidate cached array of child nodes
|
|
|
|
InvalidateChildNodes();
|
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
if (aNotify) {
|
2017-07-27 16:49:52 +03:00
|
|
|
nsNodeUtils::ContentRemoved(this, aKid, previousSibling);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
aKid->UnbindFromTree();
|
|
|
|
}
|
|
|
|
|
|
|
|
// When replacing, aRefChild is the content being replaced; when
|
|
|
|
// inserting it's the content before which we're inserting. In the
|
|
|
|
// latter case it may be null.
|
2018-10-23 01:32:00 +03:00
|
|
|
//
|
|
|
|
// If aRv is a failure after this call, the insertion should not happen.
|
2018-10-23 01:32:00 +03:00
|
|
|
//
|
|
|
|
// This implements the parts of
|
|
|
|
// https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity and
|
|
|
|
// the checks in https://dom.spec.whatwg.org/#concept-node-replace that
|
|
|
|
// depend on the child nodes or come after steps that depend on the child nodes
|
|
|
|
// (steps 2-6 in both cases).
|
2012-06-23 10:57:01 +04:00
|
|
|
static
|
2018-10-23 01:32:00 +03:00
|
|
|
void EnsureAllowedAsChild(nsINode* aNewChild, nsINode* aParent,
|
2018-10-23 01:32:00 +03:00
|
|
|
bool aIsReplace, nsINode* aRefChild,
|
|
|
|
ErrorResult& aRv)
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(aNewChild, "Must have new child");
|
|
|
|
MOZ_ASSERT_IF(aIsReplace, aRefChild);
|
|
|
|
MOZ_ASSERT(aParent);
|
2018-04-15 12:43:15 +03:00
|
|
|
MOZ_ASSERT(aParent->IsDocument() ||
|
2018-04-15 13:14:22 +03:00
|
|
|
aParent->IsDocumentFragment() ||
|
2012-06-23 10:57:01 +04:00
|
|
|
aParent->IsElement(),
|
|
|
|
"Nodes that are not documents, document fragments or elements "
|
|
|
|
"can't be parents!");
|
|
|
|
|
2018-10-23 01:32:00 +03:00
|
|
|
// Step 2.
|
2012-06-23 10:57:01 +04:00
|
|
|
// A common case is that aNewChild has no kids, in which case
|
|
|
|
// aParent can't be a descendant of aNewChild unless they're
|
|
|
|
// actually equal to each other. Fast-path that case, since aParent
|
|
|
|
// could be pretty deep in the DOM tree.
|
|
|
|
if (aNewChild == aParent ||
|
2013-03-26 11:15:23 +04:00
|
|
|
((aNewChild->GetFirstChild() ||
|
2013-12-02 14:26:11 +04:00
|
|
|
// HTML template elements and ShadowRoot hosts need
|
|
|
|
// to be checked to ensure that they are not inserted into
|
|
|
|
// the hosted content.
|
2015-03-03 14:09:00 +03:00
|
|
|
aNewChild->NodeInfo()->NameAtom() == nsGkAtoms::_template ||
|
2018-10-23 01:32:00 +03:00
|
|
|
(aNewChild->IsElement() && aNewChild->AsElement()->GetShadowRoot())) &&
|
2013-03-26 11:15:23 +04:00
|
|
|
nsContentUtils::ContentIsHostIncludingDescendantOf(aParent,
|
|
|
|
aNewChild))) {
|
2018-10-23 01:32:00 +03:00
|
|
|
aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-10-23 01:32:00 +03:00
|
|
|
// Step 3.
|
|
|
|
if (aRefChild && aRefChild->GetParentNode() != aParent) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 4.
|
|
|
|
if (!aNewChild->IsContent()) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Steps 5 and 6 combined.
|
2012-06-23 10:57:01 +04:00
|
|
|
// The allowed child nodes differ for documents and elements
|
|
|
|
switch (aNewChild->NodeType()) {
|
2018-01-30 07:10:53 +03:00
|
|
|
case nsINode::COMMENT_NODE :
|
|
|
|
case nsINode::PROCESSING_INSTRUCTION_NODE :
|
2012-06-23 10:57:01 +04:00
|
|
|
// OK in both cases
|
2018-10-23 01:32:00 +03:00
|
|
|
return;
|
2018-01-30 07:10:53 +03:00
|
|
|
case nsINode::TEXT_NODE :
|
|
|
|
case nsINode::CDATA_SECTION_NODE :
|
|
|
|
case nsINode::ENTITY_REFERENCE_NODE :
|
2012-06-23 10:57:01 +04:00
|
|
|
// Allowed under Elements and DocumentFragments
|
2018-10-23 01:32:00 +03:00
|
|
|
if (aParent->NodeType() == nsINode::DOCUMENT_NODE) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
}
|
|
|
|
return;
|
2018-01-30 07:10:53 +03:00
|
|
|
case nsINode::ELEMENT_NODE :
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2018-04-15 12:43:15 +03:00
|
|
|
if (!aParent->IsDocument()) {
|
2012-06-23 10:57:01 +04:00
|
|
|
// Always ok to have elements under other elements or document fragments
|
2018-10-23 01:32:00 +03:00
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-04-15 12:43:15 +03:00
|
|
|
nsIDocument* parentDocument = aParent->AsDocument();
|
2012-06-23 10:57:01 +04:00
|
|
|
Element* rootElement = parentDocument->GetRootElement();
|
|
|
|
if (rootElement) {
|
|
|
|
// Already have a documentElement, so this is only OK if we're
|
|
|
|
// replacing it.
|
2018-10-23 01:32:00 +03:00
|
|
|
if (!aIsReplace || rootElement != aRefChild) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
}
|
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// We don't have a documentElement yet. Our one remaining constraint is
|
|
|
|
// that the documentElement must come after the doctype.
|
|
|
|
if (!aRefChild) {
|
|
|
|
// Appending is just fine.
|
2018-10-23 01:32:00 +03:00
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2012-12-22 12:27:27 +04:00
|
|
|
nsIContent* docTypeContent = parentDocument->GetDoctype();
|
2012-06-23 10:57:01 +04:00
|
|
|
if (!docTypeContent) {
|
|
|
|
// It's all good.
|
2018-10-23 01:32:00 +03:00
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-01-23 16:30:18 +03:00
|
|
|
int32_t doctypeIndex = aParent->ComputeIndexOf(docTypeContent);
|
|
|
|
int32_t insertIndex = aParent->ComputeIndexOf(aRefChild);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
// Now we're OK in the following two cases only:
|
|
|
|
// 1) We're replacing something that's not before the doctype
|
2016-03-25 13:23:53 +03:00
|
|
|
// 2) We're inserting before something that comes after the doctype
|
2018-10-23 01:32:00 +03:00
|
|
|
bool ok = aIsReplace ? (insertIndex >= doctypeIndex) :
|
2012-06-23 10:57:01 +04:00
|
|
|
insertIndex > doctypeIndex;
|
2018-10-23 01:32:00 +03:00
|
|
|
if (!ok) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
}
|
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
2018-01-30 07:10:53 +03:00
|
|
|
case nsINode::DOCUMENT_TYPE_NODE :
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2018-04-15 12:43:15 +03:00
|
|
|
if (!aParent->IsDocument()) {
|
2012-06-23 10:57:01 +04:00
|
|
|
// doctypes only allowed under documents
|
2018-10-23 01:32:00 +03:00
|
|
|
aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-04-15 12:43:15 +03:00
|
|
|
nsIDocument* parentDocument = aParent->AsDocument();
|
2012-12-22 12:27:27 +04:00
|
|
|
nsIContent* docTypeContent = parentDocument->GetDoctype();
|
2012-06-23 10:57:01 +04:00
|
|
|
if (docTypeContent) {
|
|
|
|
// Already have a doctype, so this is only OK if we're replacing it
|
2018-10-23 01:32:00 +03:00
|
|
|
if (!aIsReplace || docTypeContent != aRefChild) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
}
|
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// We don't have a doctype yet. Our one remaining constraint is
|
|
|
|
// that the doctype must come before the documentElement.
|
|
|
|
Element* rootElement = parentDocument->GetRootElement();
|
|
|
|
if (!rootElement) {
|
|
|
|
// It's all good
|
2018-10-23 01:32:00 +03:00
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!aRefChild) {
|
|
|
|
// Trying to append a doctype, but have a documentElement
|
2018-10-23 01:32:00 +03:00
|
|
|
aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-01-23 16:30:18 +03:00
|
|
|
int32_t rootIndex = aParent->ComputeIndexOf(rootElement);
|
|
|
|
int32_t insertIndex = aParent->ComputeIndexOf(aRefChild);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
// Now we're OK if and only if insertIndex <= rootIndex. Indeed, either
|
|
|
|
// we end up replacing aRefChild or we end up before it. Either one is
|
|
|
|
// ok as long as aRefChild is not after rootElement.
|
2018-10-23 01:32:00 +03:00
|
|
|
if (insertIndex > rootIndex) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
}
|
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
2018-01-30 07:10:53 +03:00
|
|
|
case nsINode::DOCUMENT_FRAGMENT_NODE :
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
|
|
|
// Note that for now we only allow nodes inside document fragments if
|
|
|
|
// they're allowed inside elements. If we ever change this to allow
|
|
|
|
// doctype nodes in document fragments, we'll need to update this code.
|
|
|
|
// Also, there's a version of this code in ReplaceOrInsertBefore. If you
|
|
|
|
// change this code, change that too.
|
2018-04-15 12:43:15 +03:00
|
|
|
if (!aParent->IsDocument()) {
|
2012-06-23 10:57:01 +04:00
|
|
|
// All good here
|
2018-10-23 01:32:00 +03:00
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool sawElement = false;
|
|
|
|
for (nsIContent* child = aNewChild->GetFirstChild();
|
|
|
|
child;
|
|
|
|
child = child->GetNextSibling()) {
|
|
|
|
if (child->IsElement()) {
|
|
|
|
if (sawElement) {
|
|
|
|
// Can't put two elements into a document
|
2018-10-23 01:32:00 +03:00
|
|
|
aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
sawElement = true;
|
|
|
|
}
|
2018-10-23 01:32:00 +03:00
|
|
|
// If we can put this content at the right place, we might be ok;
|
2012-06-23 10:57:01 +04:00
|
|
|
// if not, we bail out.
|
2018-10-23 01:32:00 +03:00
|
|
|
EnsureAllowedAsChild(child, aParent, aIsReplace, aRefChild, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Everything in the fragment checked out ok, so we can stick it in here
|
2018-10-23 01:32:00 +03:00
|
|
|
return;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* aNewChild is of invalid type.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-10-23 01:32:00 +03:00
|
|
|
aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-10-23 01:32:00 +03:00
|
|
|
// Implements https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
|
2015-10-07 16:07:39 +03:00
|
|
|
void
|
|
|
|
nsINode::EnsurePreInsertionValidity(nsINode& aNewChild, nsINode* aRefChild,
|
|
|
|
ErrorResult& aError)
|
|
|
|
{
|
2018-10-23 01:32:00 +03:00
|
|
|
EnsurePreInsertionValidity1(aError);
|
2015-10-07 16:07:39 +03:00
|
|
|
if (aError.Failed()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
EnsurePreInsertionValidity2(false, aNewChild, aRefChild, aError);
|
|
|
|
}
|
|
|
|
|
2018-10-23 01:32:00 +03:00
|
|
|
// Implements the parts of
|
|
|
|
// https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity and
|
|
|
|
// the checks in https://dom.spec.whatwg.org/#concept-node-replace that can be
|
|
|
|
// evaluated before ever looking at the child nodes (step 1 in both cases).
|
2015-10-07 16:07:39 +03:00
|
|
|
void
|
2018-10-23 01:32:00 +03:00
|
|
|
nsINode::EnsurePreInsertionValidity1(ErrorResult& aError)
|
2015-10-07 16:07:39 +03:00
|
|
|
{
|
2018-10-23 01:32:00 +03:00
|
|
|
if (!IsDocument() && !IsDocumentFragment() && !IsElement()) {
|
2015-10-07 16:07:39 +03:00
|
|
|
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsINode::EnsurePreInsertionValidity2(bool aReplace, nsINode& aNewChild,
|
|
|
|
nsINode* aRefChild, ErrorResult& aError)
|
|
|
|
{
|
2018-10-23 01:32:00 +03:00
|
|
|
if (aNewChild.IsContent() &&
|
|
|
|
aNewChild.AsContent()->IsRootOfAnonymousSubtree()) {
|
2015-10-07 16:07:39 +03:00
|
|
|
// This is anonymous content. Don't allow its insertion
|
|
|
|
// anywhere, since it might have UnbindFromTree calls coming
|
|
|
|
// its way.
|
|
|
|
aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure that the inserted node is allowed as a child of its new parent.
|
2018-10-23 01:32:00 +03:00
|
|
|
EnsureAllowedAsChild(&aNewChild, this, aReplace, aRefChild, aError);
|
2015-10-07 16:07:39 +03:00
|
|
|
}
|
|
|
|
|
2012-10-09 16:31:24 +04:00
|
|
|
nsINode*
|
2012-06-23 10:57:01 +04:00
|
|
|
nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
|
2012-10-09 16:31:24 +04:00
|
|
|
nsINode* aRefChild, ErrorResult& aError)
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
|
|
|
// XXXbz I wish I could assert that nsContentUtils::IsSafeToRunScript() so we
|
|
|
|
// could rely on scriptblockers going out of scope to actually run XBL
|
|
|
|
// teardown, but various crud adds nodes under scriptblockers (e.g. native
|
|
|
|
// anonymous content). The only good news is those insertions can't trigger
|
|
|
|
// the bad XBL cases.
|
2012-10-09 16:31:24 +04:00
|
|
|
MOZ_ASSERT_IF(aReplace, aRefChild);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
2018-10-23 01:32:01 +03:00
|
|
|
// Before firing DOMNodeRemoved events, make sure this is actually an insert
|
|
|
|
// we plan to do.
|
2018-10-23 01:32:00 +03:00
|
|
|
EnsurePreInsertionValidity1(aError);
|
2015-10-07 16:07:39 +03:00
|
|
|
if (aError.Failed()) {
|
2012-10-09 16:31:24 +04:00
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-10-23 01:32:01 +03:00
|
|
|
EnsurePreInsertionValidity2(aReplace, *aNewChild, aRefChild, aError);
|
|
|
|
if (aError.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
uint16_t nodeType = aNewChild->NodeType();
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
// Before we do anything else, fire all DOMNodeRemoved mutation events
|
|
|
|
// We do this up front as to avoid having to deal with script running
|
|
|
|
// at random places further down.
|
|
|
|
// Scope firing mutation events so that we don't carry any state that
|
|
|
|
// might be stale
|
|
|
|
{
|
2018-10-23 01:32:01 +03:00
|
|
|
nsMutationGuard guard;
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
// If we're replacing, fire for node-to-be-replaced.
|
|
|
|
// If aRefChild == aNewChild then we'll fire for it in check below
|
|
|
|
if (aReplace && aRefChild != aNewChild) {
|
2018-05-13 00:46:45 +03:00
|
|
|
nsContentUtils::MaybeFireNodeRemoved(aRefChild, this);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the new node already has a parent, fire for removing from old
|
|
|
|
// parent
|
2012-10-09 16:31:24 +04:00
|
|
|
nsINode* oldParent = aNewChild->GetParentNode();
|
2012-06-23 10:57:01 +04:00
|
|
|
if (oldParent) {
|
2018-05-13 00:46:45 +03:00
|
|
|
nsContentUtils::MaybeFireNodeRemoved(aNewChild, oldParent);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// If we're inserting a fragment, fire for all the children of the
|
|
|
|
// fragment
|
2018-01-30 07:10:53 +03:00
|
|
|
if (nodeType == DOCUMENT_FRAGMENT_NODE) {
|
2012-11-15 02:10:08 +04:00
|
|
|
static_cast<FragmentOrElement*>(aNewChild)->FireNodeRemovedForChildren();
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-10-23 01:32:01 +03:00
|
|
|
if (guard.Mutated(0)) {
|
|
|
|
// Re-check the parts of our pre-insertion validity that might depend on
|
|
|
|
// the tree shape.
|
|
|
|
EnsurePreInsertionValidity2(aReplace, *aNewChild, aRefChild, aError);
|
|
|
|
if (aError.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Record the node to insert before, if any
|
2018-06-19 12:21:18 +03:00
|
|
|
nsIContent* nodeToInsertBefore;
|
2012-06-23 10:57:01 +04:00
|
|
|
if (aReplace) {
|
|
|
|
nodeToInsertBefore = aRefChild->GetNextSibling();
|
|
|
|
} else {
|
2018-06-19 12:21:18 +03:00
|
|
|
// Since aRefChild is our child, it must be an nsIContent object.
|
|
|
|
nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
if (nodeToInsertBefore == aNewChild) {
|
|
|
|
// We're going to remove aNewChild from its parent, so use its next sibling
|
|
|
|
// as the node to insert before.
|
|
|
|
nodeToInsertBefore = nodeToInsertBefore->GetNextSibling();
|
|
|
|
}
|
|
|
|
|
2016-02-02 18:36:30 +03:00
|
|
|
Maybe<AutoTArray<nsCOMPtr<nsIContent>, 50> > fragChildren;
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
// Remove the new child from the old parent if one exists
|
2015-10-07 16:07:39 +03:00
|
|
|
nsIContent* newContent = aNewChild->AsContent();
|
2012-10-09 16:31:24 +04:00
|
|
|
nsCOMPtr<nsINode> oldParent = newContent->GetParentNode();
|
2012-06-23 10:57:01 +04:00
|
|
|
if (oldParent) {
|
|
|
|
// Hold a strong ref to nodeToInsertBefore across the removal of newContent
|
|
|
|
nsCOMPtr<nsINode> kungFuDeathGrip = nodeToInsertBefore;
|
|
|
|
|
|
|
|
// Removing a child can run script, via XBL destructors.
|
|
|
|
nsMutationGuard guard;
|
|
|
|
|
|
|
|
// Scope for the mutation batch and scriptblocker, so they go away
|
|
|
|
// while kungFuDeathGrip is still alive.
|
|
|
|
{
|
2018-05-15 16:56:38 +03:00
|
|
|
mozAutoDocUpdate batch(newContent->GetComposedDoc(), true);
|
2012-06-23 10:57:01 +04:00
|
|
|
nsAutoMutationBatch mb(oldParent, true, true);
|
2018-06-19 12:21:18 +03:00
|
|
|
// ScriptBlocker ensures previous and next stay alive.
|
|
|
|
nsIContent* previous = aNewChild->GetPreviousSibling();
|
|
|
|
nsIContent* next = aNewChild->GetNextSibling();
|
|
|
|
oldParent->RemoveChildNode(aNewChild->AsContent(), true);
|
2012-06-23 10:57:01 +04:00
|
|
|
if (nsAutoMutationBatch::GetCurrentBatch() == &mb) {
|
|
|
|
mb.RemovalDone();
|
2018-06-19 12:21:18 +03:00
|
|
|
mb.SetPrevSibling(previous);
|
|
|
|
mb.SetNextSibling(next);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We expect one mutation (the removal) to have happened.
|
|
|
|
if (guard.Mutated(1)) {
|
|
|
|
// XBL destructors, yuck.
|
2016-03-25 13:23:53 +03:00
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
// Verify that newContent has no parent.
|
2013-08-21 20:08:23 +04:00
|
|
|
if (newContent->GetParentNode()) {
|
2012-10-09 16:31:24 +04:00
|
|
|
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// And verify that newContent is still allowed as our child.
|
|
|
|
if (aNewChild == aRefChild) {
|
|
|
|
// We've already removed aRefChild. So even if we were doing a replace,
|
|
|
|
// now we're doing a simple insert before nodeToInsertBefore.
|
2018-10-23 01:32:00 +03:00
|
|
|
EnsureAllowedAsChild(newContent, this, false, nodeToInsertBefore, aError);
|
|
|
|
if (aError.Failed()) {
|
2012-10-09 16:31:24 +04:00
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
} else {
|
2018-10-23 01:32:00 +03:00
|
|
|
EnsureAllowedAsChild(newContent, this, aReplace, aRefChild, aError);
|
|
|
|
if (aError.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
// And recompute nodeToInsertBefore, just in case.
|
|
|
|
if (aReplace) {
|
|
|
|
nodeToInsertBefore = aRefChild->GetNextSibling();
|
|
|
|
} else {
|
2018-06-19 12:21:18 +03:00
|
|
|
nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-30 07:10:53 +03:00
|
|
|
} else if (nodeType == DOCUMENT_FRAGMENT_NODE) {
|
2012-06-23 10:57:01 +04:00
|
|
|
// Make sure to remove all the fragment's kids. We need to do this before
|
|
|
|
// we start inserting anything, so we will run out XBL destructors and
|
|
|
|
// binding teardown (GOD, I HATE THESE THINGS) before we insert anything
|
|
|
|
// into the DOM.
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t count = newContent->GetChildCount();
|
2012-06-23 10:57:01 +04:00
|
|
|
|
2014-08-14 02:39:41 +04:00
|
|
|
fragChildren.emplace();
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
// Copy the children into a separate array to avoid having to deal with
|
|
|
|
// mutations to the fragment later on here.
|
2014-08-14 02:39:41 +04:00
|
|
|
fragChildren->SetCapacity(count);
|
2012-06-23 10:57:01 +04:00
|
|
|
for (nsIContent* child = newContent->GetFirstChild();
|
|
|
|
child;
|
|
|
|
child = child->GetNextSibling()) {
|
2014-10-02 22:45:44 +04:00
|
|
|
NS_ASSERTION(child->GetComposedDoc() == nullptr,
|
2012-06-23 10:57:01 +04:00
|
|
|
"How did we get a child with a current doc?");
|
2014-08-14 02:39:41 +04:00
|
|
|
fragChildren->AppendElement(child);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Hold a strong ref to nodeToInsertBefore across the removals
|
|
|
|
nsCOMPtr<nsINode> kungFuDeathGrip = nodeToInsertBefore;
|
|
|
|
|
|
|
|
nsMutationGuard guard;
|
|
|
|
|
|
|
|
// Scope for the mutation batch and scriptblocker, so they go away
|
|
|
|
// while kungFuDeathGrip is still alive.
|
|
|
|
{
|
2018-05-15 16:56:38 +03:00
|
|
|
mozAutoDocUpdate batch(newContent->GetComposedDoc(), true);
|
2012-06-23 10:57:01 +04:00
|
|
|
nsAutoMutationBatch mb(newContent, false, true);
|
|
|
|
|
2018-06-19 12:21:18 +03:00
|
|
|
while (newContent->HasChildren()) {
|
|
|
|
newContent->RemoveChildNode(newContent->GetLastChild(), true);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We expect |count| removals
|
|
|
|
if (guard.Mutated(count)) {
|
|
|
|
// XBL destructors, yuck.
|
2016-03-25 13:23:53 +03:00
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
// Verify that nodeToInsertBefore, if non-null, is still our child. If
|
|
|
|
// it's not, there's no way we can do this insert sanely; just bail out.
|
|
|
|
if (nodeToInsertBefore && nodeToInsertBefore->GetParent() != this) {
|
2012-10-09 16:31:24 +04:00
|
|
|
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that all the things in fragChildren have no parent.
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t i = 0; i < count; ++i) {
|
2014-08-14 02:39:41 +04:00
|
|
|
if (fragChildren->ElementAt(i)->GetParentNode()) {
|
2012-10-09 16:31:24 +04:00
|
|
|
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note that unlike the single-element case above, none of our kids can
|
|
|
|
// be aRefChild, so we can always pass through aReplace in the
|
2018-10-23 01:32:00 +03:00
|
|
|
// EnsureAllowedAsChild checks below and don't have to worry about whether
|
2012-06-23 10:57:01 +04:00
|
|
|
// recomputing nodeToInsertBefore is OK.
|
|
|
|
|
|
|
|
// Verify that our aRefChild is still sensible
|
|
|
|
if (aRefChild && aRefChild->GetParent() != this) {
|
2012-10-09 16:31:24 +04:00
|
|
|
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Recompute nodeToInsertBefore, just in case.
|
|
|
|
if (aReplace) {
|
|
|
|
nodeToInsertBefore = aRefChild->GetNextSibling();
|
|
|
|
} else {
|
2018-06-19 12:21:18 +03:00
|
|
|
// If aRefChild has 'this' as a parent, it must be an nsIContent.
|
|
|
|
nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr;
|
2016-03-25 13:23:53 +03:00
|
|
|
}
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
// And verify that newContent is still allowed as our child. Sadly, we
|
2018-10-23 01:32:00 +03:00
|
|
|
// need to reimplement the relevant part of EnsureAllowedAsChild() because
|
2012-06-23 10:57:01 +04:00
|
|
|
// now our nodes are in an array and all. If you change this code,
|
|
|
|
// change the code there.
|
2018-04-15 12:43:15 +03:00
|
|
|
if (IsDocument()) {
|
2012-06-23 10:57:01 +04:00
|
|
|
bool sawElement = false;
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t i = 0; i < count; ++i) {
|
2014-08-14 02:39:41 +04:00
|
|
|
nsIContent* child = fragChildren->ElementAt(i);
|
2012-06-23 10:57:01 +04:00
|
|
|
if (child->IsElement()) {
|
|
|
|
if (sawElement) {
|
|
|
|
// No good
|
2012-10-09 16:31:24 +04:00
|
|
|
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
sawElement = true;
|
|
|
|
}
|
2018-10-23 01:32:00 +03:00
|
|
|
EnsureAllowedAsChild(child, this, aReplace, aRefChild, aError);
|
|
|
|
if (aError.Failed()) {
|
2012-10-09 16:31:24 +04:00
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-15 16:56:38 +03:00
|
|
|
mozAutoDocUpdate batch(GetComposedDoc(), true);
|
2012-06-23 10:57:01 +04:00
|
|
|
nsAutoMutationBatch mb;
|
|
|
|
|
|
|
|
// If we're replacing and we haven't removed aRefChild yet, do so now
|
|
|
|
if (aReplace && aRefChild != aNewChild) {
|
|
|
|
mb.Init(this, true, true);
|
|
|
|
|
|
|
|
// Since aRefChild is never null in the aReplace case, we know that at
|
|
|
|
// this point nodeToInsertBefore is the next sibling of aRefChild.
|
|
|
|
NS_ASSERTION(aRefChild->GetNextSibling() == nodeToInsertBefore,
|
|
|
|
"Unexpected nodeToInsertBefore");
|
|
|
|
|
2018-06-19 12:21:18 +03:00
|
|
|
nsIContent* toBeRemoved = nodeToInsertBefore ?
|
|
|
|
nodeToInsertBefore->GetPreviousSibling() : GetLastChild();
|
|
|
|
MOZ_ASSERT(toBeRemoved);
|
|
|
|
|
|
|
|
RemoveChildNode(toBeRemoved, true);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Move new child over to our document if needed. Do this after removing
|
|
|
|
// it from its parent so that AdoptNode doesn't fire DOMNodeRemoved
|
|
|
|
// DocumentType nodes are the only nodes that can have a null
|
|
|
|
// ownerDocument according to the DOM spec, and we need to allow
|
|
|
|
// inserting them w/o calling AdoptNode().
|
2015-10-07 16:07:39 +03:00
|
|
|
nsIDocument* doc = OwnerDoc();
|
2013-08-16 01:39:28 +04:00
|
|
|
if (doc != newContent->OwnerDoc()) {
|
2017-09-13 20:34:55 +03:00
|
|
|
AdoptNodeIntoOwnerDoc(this, aNewChild, aError);
|
2012-10-09 16:31:24 +04:00
|
|
|
if (aError.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2013-08-16 01:39:28 +04:00
|
|
|
} else if (doc->DidDocumentOpen()) {
|
2017-09-13 20:34:55 +03:00
|
|
|
CheckForOutdatedParent(this, aNewChild, aError);
|
2013-08-16 01:39:28 +04:00
|
|
|
if (aError.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if we're inserting a document fragment. If we are, we need
|
|
|
|
* to actually add its children individually (i.e. we don't add the
|
|
|
|
* actual document fragment).
|
|
|
|
*/
|
2012-10-09 16:31:24 +04:00
|
|
|
nsINode* result = aReplace ? aRefChild : aNewChild;
|
2018-01-30 07:10:53 +03:00
|
|
|
if (nodeType == DOCUMENT_FRAGMENT_NODE) {
|
2012-06-23 10:57:01 +04:00
|
|
|
if (!aReplace) {
|
|
|
|
mb.Init(this, true, true);
|
|
|
|
}
|
|
|
|
nsAutoMutationBatch* mutationBatch = nsAutoMutationBatch::GetCurrentBatch();
|
|
|
|
if (mutationBatch) {
|
|
|
|
mutationBatch->RemovalDone();
|
2018-06-19 12:21:18 +03:00
|
|
|
mutationBatch->SetPrevSibling(nodeToInsertBefore ?
|
|
|
|
nodeToInsertBefore->GetPreviousSibling() : GetLastChild());
|
|
|
|
mutationBatch->SetNextSibling(nodeToInsertBefore);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2014-08-14 02:39:41 +04:00
|
|
|
uint32_t count = fragChildren->Length();
|
2012-10-12 03:01:40 +04:00
|
|
|
if (!count) {
|
2012-10-09 16:31:24 +04:00
|
|
|
return result;
|
2012-10-12 03:01:40 +04:00
|
|
|
}
|
|
|
|
|
2018-06-19 12:21:18 +03:00
|
|
|
bool appending = !IsDocument() && !nodeToInsertBefore;
|
2014-08-14 02:39:41 +04:00
|
|
|
nsIContent* firstInsertedContent = fragChildren->ElementAt(0);
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
// Iterate through the fragment's children, and insert them in the new
|
|
|
|
// parent
|
2018-06-19 12:21:18 +03:00
|
|
|
for (uint32_t i = 0; i < count; ++i) {
|
2012-06-23 10:57:01 +04:00
|
|
|
// XXXbz how come no reparenting here? That seems odd...
|
|
|
|
// Insert the child.
|
2018-06-19 12:21:18 +03:00
|
|
|
aError = InsertChildBefore(fragChildren->ElementAt(i), nodeToInsertBefore,
|
|
|
|
!appending);
|
2012-10-09 16:31:24 +04:00
|
|
|
if (aError.Failed()) {
|
2012-06-23 10:57:01 +04:00
|
|
|
// Make sure to notify on any children that we did succeed to insert
|
|
|
|
if (appending && i != 0) {
|
|
|
|
nsNodeUtils::ContentAppended(static_cast<nsIContent*>(this),
|
2017-07-27 16:49:52 +03:00
|
|
|
firstInsertedContent);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
2012-10-09 16:31:24 +04:00
|
|
|
return nullptr;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mutationBatch && !appending) {
|
|
|
|
mutationBatch->NodesAdded();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Notify and fire mutation events when appending
|
|
|
|
if (appending) {
|
|
|
|
nsNodeUtils::ContentAppended(static_cast<nsIContent*>(this),
|
2017-07-27 16:49:52 +03:00
|
|
|
firstInsertedContent);
|
2012-06-23 10:57:01 +04:00
|
|
|
if (mutationBatch) {
|
|
|
|
mutationBatch->NodesAdded();
|
|
|
|
}
|
|
|
|
// Optimize for the case when there are no listeners
|
|
|
|
if (nsContentUtils::
|
|
|
|
HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
|
2014-08-14 02:39:41 +04:00
|
|
|
Element::FireNodeInserted(doc, this, *fragChildren);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Not inserting a fragment but rather a single node.
|
|
|
|
|
|
|
|
// FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=544654
|
|
|
|
// We need to reparent here for nodes for which the parent of their
|
|
|
|
// wrapper is not the wrapper for their ownerDocument (XUL elements,
|
|
|
|
// form controls, ...). Also applies in the fragment code above.
|
|
|
|
if (nsAutoMutationBatch::GetCurrentBatch() == &mb) {
|
|
|
|
mb.RemovalDone();
|
2018-06-19 12:21:18 +03:00
|
|
|
mb.SetPrevSibling(nodeToInsertBefore ?
|
|
|
|
nodeToInsertBefore->GetPreviousSibling() : GetLastChild());
|
|
|
|
mb.SetNextSibling(nodeToInsertBefore);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
2018-06-19 12:21:18 +03:00
|
|
|
aError = InsertChildBefore(newContent, nodeToInsertBefore, true);
|
2012-10-09 16:31:24 +04:00
|
|
|
if (aError.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2012-10-09 16:31:24 +04:00
|
|
|
return result;
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsINode::BindObject(nsISupports* aObject)
|
|
|
|
{
|
|
|
|
nsCOMArray<nsISupports>* objects =
|
|
|
|
static_cast<nsCOMArray<nsISupports>*>(GetProperty(nsGkAtoms::keepobjectsalive));
|
|
|
|
if (!objects) {
|
|
|
|
objects = new nsCOMArray<nsISupports>();
|
2014-03-25 17:25:47 +04:00
|
|
|
SetProperty(nsGkAtoms::keepobjectsalive, objects,
|
|
|
|
nsINode::DeleteProperty< nsCOMArray<nsISupports> >, true);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
objects->AppendObject(aObject);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsINode::UnbindObject(nsISupports* aObject)
|
|
|
|
{
|
|
|
|
nsCOMArray<nsISupports>* objects =
|
|
|
|
static_cast<nsCOMArray<nsISupports>*>(GetProperty(nsGkAtoms::keepobjectsalive));
|
|
|
|
if (objects) {
|
|
|
|
objects->RemoveObject(aObject);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-23 21:15:15 +03:00
|
|
|
already_AddRefed<AccessibleNode>
|
|
|
|
nsINode::GetAccessibleNode()
|
|
|
|
{
|
|
|
|
#ifdef ACCESSIBILITY
|
2018-05-26 16:47:48 +03:00
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
RefPtr<AccessibleNode> anode =
|
|
|
|
static_cast<AccessibleNode*>(GetProperty(nsGkAtoms::accessiblenode, &rv));
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
anode = new AccessibleNode(this);
|
|
|
|
RefPtr<AccessibleNode> temp = anode;
|
|
|
|
rv = SetProperty(nsGkAtoms::accessiblenode, temp.forget().take(),
|
|
|
|
nsPropertyTable::SupportsDtorFunc, true);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
NS_WARNING("SetProperty failed");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
2016-08-23 21:15:15 +03:00
|
|
|
return anode.forget();
|
2017-10-26 12:40:12 +03:00
|
|
|
#else
|
2016-08-23 21:15:15 +03:00
|
|
|
return nullptr;
|
2017-10-26 12:40:12 +03:00
|
|
|
#endif
|
2016-08-23 21:15:15 +03:00
|
|
|
}
|
|
|
|
|
Bug 1387956 - Overhaul ComputedValues measurement, and add style structs measurement. r=bholley.
This patch moves measurement of ComputedValues objects from Rust to C++.
Measurement now happens (a) via DOM elements and (b) remaining elements via
the frame tree. Likewise for the style structs hanging off ComputedValues
objects.
Here is an example of the output.
> ├──27,600,448 B (26.49%) -- active/window(https://en.wikipedia.org/wiki/Barack_Obama)
> │ ├──12,772,544 B (12.26%) -- layout
> │ │ ├───4,483,744 B (04.30%) -- frames
> │ │ │ ├──1,653,552 B (01.59%) ── nsInlineFrame
> │ │ │ ├──1,415,760 B (01.36%) ── nsTextFrame
> │ │ │ ├────431,376 B (00.41%) ── nsBlockFrame
> │ │ │ ├────340,560 B (00.33%) ── nsHTMLScrollFrame
> │ │ │ ├────302,544 B (00.29%) ── nsContinuingTextFrame
> │ │ │ ├────156,408 B (00.15%) ── nsBulletFrame
> │ │ │ ├─────73,024 B (00.07%) ── nsPlaceholderFrame
> │ │ │ ├─────27,656 B (00.03%) ── sundries
> │ │ │ ├─────23,520 B (00.02%) ── nsTableCellFrame
> │ │ │ ├─────16,704 B (00.02%) ── nsImageFrame
> │ │ │ ├─────15,488 B (00.01%) ── nsTableRowFrame
> │ │ │ ├─────13,776 B (00.01%) ── nsTableColFrame
> │ │ │ └─────13,376 B (00.01%) ── nsTableFrame
> │ │ ├───3,412,192 B (03.28%) -- servo-style-structs
> │ │ │ ├──1,288,224 B (01.24%) ── Display
> │ │ │ ├────742,400 B (00.71%) ── Position
> │ │ │ ├────308,736 B (00.30%) ── Font
> │ │ │ ├────226,512 B (00.22%) ── Background
> │ │ │ ├────218,304 B (00.21%) ── TextReset
> │ │ │ ├────214,896 B (00.21%) ── Text
> │ │ │ ├────130,560 B (00.13%) ── Border
> │ │ │ ├─────81,408 B (00.08%) ── UIReset
> │ │ │ ├─────61,440 B (00.06%) ── Padding
> │ │ │ ├─────38,176 B (00.04%) ── UserInterface
> │ │ │ ├─────29,232 B (00.03%) ── Margin
> │ │ │ ├─────21,824 B (00.02%) ── sundries
> │ │ │ ├─────20,080 B (00.02%) ── Color
> │ │ │ ├─────20,080 B (00.02%) ── Column
> │ │ │ └─────10,320 B (00.01%) ── Effects
> │ │ ├───2,227,680 B (02.14%) -- computed-values
> │ │ │ ├──1,182,928 B (01.14%) ── non-dom
> │ │ │ └──1,044,752 B (01.00%) ── dom
> │ │ ├───1,500,016 B (01.44%) ── text-runs
> │ │ ├─────492,640 B (00.47%) ── line-boxes
> │ │ ├─────326,688 B (00.31%) ── frame-properties
> │ │ ├─────301,760 B (00.29%) ── pres-shell
> │ │ ├──────27,648 B (00.03%) ── pres-contexts
> │ │ └─────────176 B (00.00%) ── style-sets
The 'servo-style-structs' and 'computed-values' sub-trees are new. (Prior to
this patch, ComputedValues under DOM elements were tallied under the the
'dom/element-nodes' sub-tree, and ComputedValues not under DOM element were
ignored.) 'servo-style-structs/sundries' aggregates all the style structs that
are smaller than 8 KiB.
Other notable things done by the patch are as follows.
- It significantly changes the signatures of the methods measuring nsINode and
its subclasses, in order to handle the tallying of style structs separately
from element-nodes. Likewise for nsIFrame.
- It renames the 'layout/style-structs' sub-tree as
'layout/gecko-style-structs', to clearly distinguish it from the new
'layout/servo-style-structs' sub-tree.
- It adds some FFI functions to access various Rust-side data structures from
C++ code.
- There is a nasty hack used twice to measure Arcs, by stepping backwards from
an interior pointer to a base pointer. It works, but I want to replace it
with something better eventually. The "XXX WARNING" comments have details.
- It makes DMD print a line to the console if it sees a pointer it doesn't
recognise. This is useful for detecting when we are measuring an interior
pointer instead of a base pointer, which is bad but easy to do when Arcs are
involved.
- It removes the Rust code for measuring CVs, because it's now all done on the
C++ side.
MozReview-Commit-ID: BKebACLKtCi
--HG--
extra : rebase_source : 4d9a8c6b198a0ff025b811759a6bfa9f33a260ba
2017-08-11 09:37:33 +03:00
|
|
|
void
|
2017-08-25 07:47:54 +03:00
|
|
|
nsINode::AddSizeOfExcludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const
|
2012-06-23 10:57:01 +04:00
|
|
|
{
|
2014-03-17 10:56:53 +04:00
|
|
|
EventListenerManager* elm = GetExistingListenerManager();
|
2012-06-23 10:57:01 +04:00
|
|
|
if (elm) {
|
2017-08-25 07:47:54 +03:00
|
|
|
*aNodeSize += elm->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Measurement of the following members may be added later if DMD finds it is
|
|
|
|
// worthwhile:
|
2013-03-01 06:53:49 +04:00
|
|
|
// - mNodeInfo
|
2012-06-23 10:57:01 +04:00
|
|
|
// - mSlots
|
|
|
|
//
|
|
|
|
// The following members are not measured:
|
2018-11-27 13:08:58 +03:00
|
|
|
// - mParent, mNextSibling, mPreviousOrLastSibling, mFirstChild: because
|
|
|
|
// they're non-owning, from "exclusive ownership" point of view.
|
2012-06-23 10:57:01 +04:00
|
|
|
}
|
|
|
|
|
2018-03-28 02:44:49 +03:00
|
|
|
void
|
|
|
|
nsINode::AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const
|
|
|
|
{
|
|
|
|
*aNodeSize += aSizes.mState.mMallocSizeOf(this);
|
|
|
|
AddSizeOfExcludingThis(aSizes, aNodeSize);
|
|
|
|
}
|
|
|
|
|
2012-06-23 10:57:01 +04:00
|
|
|
bool
|
|
|
|
nsINode::Contains(const nsINode* aOther) const
|
|
|
|
{
|
|
|
|
if (aOther == this) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!aOther ||
|
|
|
|
OwnerDoc() != aOther->OwnerDoc() ||
|
2016-03-31 13:58:25 +03:00
|
|
|
IsInUncomposedDoc() != aOther->IsInUncomposedDoc() ||
|
2017-10-25 18:19:11 +03:00
|
|
|
!aOther->IsContent() ||
|
2012-06-23 10:57:01 +04:00
|
|
|
!GetFirstChild()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const nsIContent* other = static_cast<const nsIContent*>(aOther);
|
|
|
|
if (this == OwnerDoc()) {
|
|
|
|
// document.contains(aOther) returns true if aOther is in the document,
|
|
|
|
// but is not in any anonymous subtree.
|
2016-03-31 13:58:25 +03:00
|
|
|
// IsInUncomposedDoc() check is done already before this.
|
2012-06-23 10:57:01 +04:00
|
|
|
return !other->IsInAnonymousSubtree();
|
|
|
|
}
|
|
|
|
|
2018-04-15 13:14:22 +03:00
|
|
|
if (!IsElement() && !IsDocumentFragment()) {
|
2012-06-23 10:57:01 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-04-15 13:14:22 +03:00
|
|
|
if (AsContent()->GetBindingParent() != other->GetBindingParent()) {
|
2012-06-23 10:57:01 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsContentUtils::ContentIsDescendantOf(other, this);
|
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t
|
2012-06-23 10:57:01 +04:00
|
|
|
nsINode::Length() const
|
|
|
|
{
|
|
|
|
switch (NodeType()) {
|
2018-01-30 07:10:53 +03:00
|
|
|
case DOCUMENT_TYPE_NODE:
|
2012-06-23 10:57:01 +04:00
|
|
|
return 0;
|
|
|
|
|
2018-01-30 07:10:53 +03:00
|
|
|
case TEXT_NODE:
|
|
|
|
case CDATA_SECTION_NODE:
|
|
|
|
case PROCESSING_INSTRUCTION_NODE:
|
|
|
|
case COMMENT_NODE:
|
2017-10-25 18:19:11 +03:00
|
|
|
MOZ_ASSERT(IsContent());
|
|
|
|
return AsContent()->TextLength();
|
2012-06-23 10:57:01 +04:00
|
|
|
|
|
|
|
default:
|
|
|
|
return GetChildCount();
|
|
|
|
}
|
|
|
|
}
|
2012-08-30 21:10:13 +04:00
|
|
|
|
2017-10-02 19:14:15 +03:00
|
|
|
const RawServoSelectorList*
|
2018-03-29 16:34:05 +03:00
|
|
|
nsINode::ParseSelectorList(const nsAString& aSelectorString,
|
|
|
|
ErrorResult& aRv)
|
2017-10-02 19:14:15 +03:00
|
|
|
{
|
|
|
|
nsIDocument* doc = OwnerDoc();
|
|
|
|
|
2018-03-28 18:34:34 +03:00
|
|
|
nsIDocument::SelectorCache& cache = doc->GetSelectorCache();
|
2017-10-02 19:14:15 +03:00
|
|
|
nsIDocument::SelectorCache::SelectorList* list =
|
|
|
|
cache.GetList(aSelectorString);
|
|
|
|
if (list) {
|
|
|
|
if (!*list) {
|
|
|
|
// Invalid selector.
|
|
|
|
aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR,
|
|
|
|
NS_LITERAL_CSTRING("'") + NS_ConvertUTF16toUTF8(aSelectorString) +
|
|
|
|
NS_LITERAL_CSTRING("' is not a valid selector")
|
|
|
|
);
|
2017-10-06 09:16:13 +03:00
|
|
|
return nullptr;
|
2017-10-02 19:14:15 +03:00
|
|
|
}
|
|
|
|
|
2018-03-29 16:34:05 +03:00
|
|
|
return list->get();
|
2017-10-02 19:14:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_ConvertUTF16toUTF8 selectorString(aSelectorString);
|
|
|
|
|
2018-03-29 16:34:05 +03:00
|
|
|
auto selectorList =
|
|
|
|
UniquePtr<RawServoSelectorList>(Servo_SelectorList_Parse(&selectorString));
|
2017-10-02 19:14:15 +03:00
|
|
|
if (!selectorList) {
|
|
|
|
aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR,
|
|
|
|
NS_LITERAL_CSTRING("'") + selectorString +
|
|
|
|
NS_LITERAL_CSTRING("' is not a valid selector")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-03-29 16:34:05 +03:00
|
|
|
auto* ret = selectorList.get();
|
2018-05-30 22:15:35 +03:00
|
|
|
cache.CacheList(aSelectorString, std::move(selectorList));
|
2018-03-29 16:34:05 +03:00
|
|
|
return ret;
|
2017-10-02 19:14:15 +03:00
|
|
|
}
|
|
|
|
|
2018-05-26 17:45:11 +03:00
|
|
|
// Given an id, find first element with that id under aRoot.
|
|
|
|
// If none found, return nullptr. aRoot must be in the document.
|
|
|
|
inline static Element*
|
2018-11-19 23:58:56 +03:00
|
|
|
FindMatchingElementWithId(const nsAString& aId,
|
|
|
|
const Element& aRoot,
|
|
|
|
const DocumentOrShadowRoot& aContainingDocOrShadowRoot)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aRoot.SubtreeRoot() == &aContainingDocOrShadowRoot.AsNode());
|
|
|
|
MOZ_ASSERT(aRoot.IsInUncomposedDoc() || aRoot.IsInShadowTree(),
|
|
|
|
"Don't call me if the root is not in the document or in a shadow tree");
|
|
|
|
|
|
|
|
const nsTArray<Element*>* elements =
|
|
|
|
aContainingDocOrShadowRoot.GetAllElementsForId(aId);
|
2013-11-01 23:39:24 +04:00
|
|
|
if (!elements) {
|
|
|
|
// Nothing to do; we're done
|
2018-05-26 17:45:11 +03:00
|
|
|
return nullptr;
|
2013-11-01 23:39:24 +04:00
|
|
|
}
|
|
|
|
|
2018-11-19 23:58:56 +03:00
|
|
|
// XXXbz: Should we fall back to the tree walk if |elements| is long,
|
|
|
|
// for some value of "long"?
|
|
|
|
for (Element* element : *elements) {
|
|
|
|
if (MOZ_UNLIKELY(element == &aRoot)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nsContentUtils::ContentIsDescendantOf(element, &aRoot)) {
|
|
|
|
continue;
|
2013-11-01 23:39:24 +04:00
|
|
|
}
|
2018-11-19 23:58:56 +03:00
|
|
|
|
|
|
|
// We have an element with the right id and it's a strict descendant
|
|
|
|
// of aRoot.
|
|
|
|
return element;
|
2013-11-01 23:39:24 +04:00
|
|
|
}
|
2018-11-19 23:58:56 +03:00
|
|
|
|
2018-05-26 17:45:11 +03:00
|
|
|
return nullptr;
|
2013-11-01 23:39:24 +04:00
|
|
|
}
|
|
|
|
|
2012-11-15 02:10:08 +04:00
|
|
|
Element*
|
2012-10-16 15:51:00 +04:00
|
|
|
nsINode::QuerySelector(const nsAString& aSelector, ErrorResult& aResult)
|
2012-08-30 21:10:13 +04:00
|
|
|
{
|
2018-06-14 20:16:49 +03:00
|
|
|
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
|
|
|
|
"nsINode::QuerySelector", DOM, aSelector);
|
|
|
|
|
2018-03-29 16:34:05 +03:00
|
|
|
const RawServoSelectorList* list = ParseSelectorList(aSelector, aResult);
|
|
|
|
if (!list) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
const bool useInvalidation = false;
|
|
|
|
return const_cast<Element*>(
|
|
|
|
Servo_SelectorList_QueryFirst(this, list, useInvalidation));
|
2012-08-30 21:10:13 +04:00
|
|
|
}
|
|
|
|
|
2012-10-16 15:51:00 +04:00
|
|
|
already_AddRefed<nsINodeList>
|
|
|
|
nsINode::QuerySelectorAll(const nsAString& aSelector, ErrorResult& aResult)
|
2012-08-30 21:10:13 +04:00
|
|
|
{
|
2018-06-14 20:16:49 +03:00
|
|
|
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
|
|
|
|
"nsINode::QuerySelectorAll", DOM, aSelector);
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<nsSimpleContentList> contentList = new nsSimpleContentList(this);
|
2018-03-29 16:34:05 +03:00
|
|
|
const RawServoSelectorList* list = ParseSelectorList(aSelector, aResult);
|
|
|
|
if (!list) {
|
|
|
|
return contentList.forget();
|
|
|
|
}
|
2012-08-30 21:10:13 +04:00
|
|
|
|
2018-03-29 16:34:05 +03:00
|
|
|
const bool useInvalidation = false;
|
|
|
|
Servo_SelectorList_QueryAll(this, list, contentList.get(), useInvalidation);
|
2012-10-16 15:51:00 +04:00
|
|
|
return contentList.forget();
|
2012-08-30 21:10:13 +04:00
|
|
|
}
|
|
|
|
|
2013-11-01 23:39:24 +04:00
|
|
|
Element*
|
|
|
|
nsINode::GetElementById(const nsAString& aId)
|
|
|
|
{
|
2018-11-19 23:58:56 +03:00
|
|
|
MOZ_ASSERT(!IsShadowRoot(), "Should use the faster version");
|
2018-04-15 13:14:22 +03:00
|
|
|
MOZ_ASSERT(IsElement() || IsDocumentFragment(),
|
2013-11-01 23:39:24 +04:00
|
|
|
"Bogus this object for GetElementById call");
|
2016-03-31 13:58:25 +03:00
|
|
|
if (IsInUncomposedDoc()) {
|
2018-11-19 23:58:56 +03:00
|
|
|
MOZ_ASSERT(IsElement(), "Huh? A fragment in a document?");
|
|
|
|
return FindMatchingElementWithId(aId, *AsElement(), *OwnerDoc());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ShadowRoot* containingShadow = AsContent()->GetContainingShadow()) {
|
|
|
|
MOZ_ASSERT(IsElement(), "Huh? A fragment in a ShadowRoot?");
|
|
|
|
return FindMatchingElementWithId(aId, *AsElement(), *containingShadow);
|
2013-11-01 23:39:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (nsIContent* kid = GetFirstChild(); kid; kid = kid->GetNextNode(this)) {
|
|
|
|
if (!kid->IsElement()) {
|
|
|
|
continue;
|
|
|
|
}
|
2017-10-03 01:05:19 +03:00
|
|
|
nsAtom* id = kid->AsElement()->GetID();
|
2013-11-01 23:39:24 +04:00
|
|
|
if (id && id->Equals(aId)) {
|
|
|
|
return kid->AsElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2012-10-09 16:31:24 +04:00
|
|
|
JSObject*
|
Bug 1117172 part 3. Change the wrappercached WrapObject methods to allow passing in aGivenProto. r=peterv
The only manual changes here are to BindingUtils.h, BindingUtils.cpp,
Codegen.py, Element.cpp, IDBFileRequest.cpp, IDBObjectStore.cpp,
dom/workers/Navigator.cpp, WorkerPrivate.cpp, DeviceStorageRequestChild.cpp,
Notification.cpp, nsGlobalWindow.cpp, MessagePort.cpp, nsJSEnvironment.cpp,
Sandbox.cpp, XPCConvert.cpp, ExportHelpers.cpp, and DataStoreService.cpp. The
rest of this diff was generated by running the following commands:
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObject\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(Binding(?:_workers)?::Wrap\((?:aCx|cx|aContext|aCtx|js), [^,)]+)\)/\1, aGivenProto)/g'
2015-03-19 17:13:33 +03:00
|
|
|
nsINode::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
2012-10-09 16:31:24 +04:00
|
|
|
{
|
|
|
|
// Make sure one of these is true
|
|
|
|
// (1) our owner document has a script handling object,
|
|
|
|
// (2) Our owner document has had a script handling object, or has been marked
|
|
|
|
// to have had one,
|
|
|
|
// (3) we are running a privileged script.
|
|
|
|
// Event handling is possible only if (1). If (2) event handling is
|
|
|
|
// prevented.
|
|
|
|
// If the document has never had a script handling object, untrusted
|
|
|
|
// scripts (3) shouldn't touch it!
|
|
|
|
bool hasHadScriptHandlingObject = false;
|
|
|
|
if (!OwnerDoc()->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
|
|
|
|
!hasHadScriptHandlingObject &&
|
2017-02-01 23:43:36 +03:00
|
|
|
!nsContentUtils::IsSystemCaller(aCx)) {
|
2013-09-09 07:29:21 +04:00
|
|
|
Throw(aCx, NS_ERROR_UNEXPECTED);
|
2012-10-09 16:31:24 +04:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
Bug 1117172 part 3. Change the wrappercached WrapObject methods to allow passing in aGivenProto. r=peterv
The only manual changes here are to BindingUtils.h, BindingUtils.cpp,
Codegen.py, Element.cpp, IDBFileRequest.cpp, IDBObjectStore.cpp,
dom/workers/Navigator.cpp, WorkerPrivate.cpp, DeviceStorageRequestChild.cpp,
Notification.cpp, nsGlobalWindow.cpp, MessagePort.cpp, nsJSEnvironment.cpp,
Sandbox.cpp, XPCConvert.cpp, ExportHelpers.cpp, and DataStoreService.cpp. The
rest of this diff was generated by running the following commands:
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObject\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(Binding(?:_workers)?::Wrap\((?:aCx|cx|aContext|aCtx|js), [^,)]+)\)/\1, aGivenProto)/g'
2015-03-19 17:13:33 +03:00
|
|
|
JS::Rooted<JSObject*> obj(aCx, WrapNode(aCx, aGivenProto));
|
2016-01-11 21:32:12 +03:00
|
|
|
MOZ_ASSERT_IF(obj && ChromeOnlyAccess(),
|
|
|
|
xpc::IsInContentXBLScope(obj) ||
|
2017-06-28 22:31:53 +03:00
|
|
|
!xpc::UseContentXBLScope(JS::GetObjectRealmOrNull(obj)));
|
2012-09-13 00:29:30 +04:00
|
|
|
return obj;
|
2012-10-09 16:31:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<nsINode>
|
|
|
|
nsINode::CloneNode(bool aDeep, ErrorResult& aError)
|
|
|
|
{
|
2017-09-13 20:34:55 +03:00
|
|
|
return nsNodeUtils::CloneNodeImpl(this, aDeep, aError);
|
2012-10-09 16:31:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsDOMAttributeMap*
|
|
|
|
nsINode::GetAttributes()
|
|
|
|
{
|
|
|
|
if (!IsElement()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2013-03-10 11:58:47 +04:00
|
|
|
return AsElement()->Attributes();
|
2012-10-09 16:31:24 +04:00
|
|
|
}
|
|
|
|
|
2014-06-06 09:22:55 +04:00
|
|
|
Element*
|
|
|
|
nsINode::GetParentElementCrossingShadowRoot() const
|
|
|
|
{
|
|
|
|
if (!mParent) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mParent->IsElement()) {
|
|
|
|
return mParent->AsElement();
|
|
|
|
}
|
|
|
|
|
2017-12-31 22:57:32 +03:00
|
|
|
if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(mParent)) {
|
|
|
|
MOZ_ASSERT(shadowRoot->GetHost(), "ShowRoots should always have a host");
|
|
|
|
return shadowRoot->GetHost();
|
2014-06-06 09:22:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
2014-07-03 17:23:14 +04:00
|
|
|
|
|
|
|
bool
|
|
|
|
nsINode::HasBoxQuadsSupport(JSContext* aCx, JSObject* /* unused */)
|
|
|
|
{
|
|
|
|
return xpc::AccessCheck::isChrome(js::GetContextCompartment(aCx)) ||
|
2017-04-07 22:48:24 +03:00
|
|
|
nsContentUtils::GetBoxQuadsEnabled();
|
2014-07-03 17:23:14 +04:00
|
|
|
}
|
|
|
|
|
2014-10-31 00:38:48 +03:00
|
|
|
nsINode*
|
|
|
|
nsINode::GetScopeChainParent() const
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-03-14 08:34:40 +03:00
|
|
|
|
|
|
|
void
|
|
|
|
nsINode::AddAnimationObserver(nsIAnimationObserver* aAnimationObserver)
|
|
|
|
{
|
|
|
|
AddMutationObserver(aAnimationObserver);
|
|
|
|
OwnerDoc()->SetMayHaveAnimationObservers();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsINode::AddAnimationObserverUnlessExists(
|
|
|
|
nsIAnimationObserver* aAnimationObserver)
|
|
|
|
{
|
|
|
|
AddMutationObserverUnlessExists(aAnimationObserver);
|
|
|
|
OwnerDoc()->SetMayHaveAnimationObservers();
|
|
|
|
}
|
2015-09-17 13:16:20 +03:00
|
|
|
|
2017-06-15 04:49:17 +03:00
|
|
|
void
|
|
|
|
nsINode::GenerateXPath(nsAString& aResult)
|
|
|
|
{
|
|
|
|
XPathGenerator::Generate(this, aResult);
|
|
|
|
}
|
|
|
|
|
2015-09-17 13:16:20 +03:00
|
|
|
bool
|
2016-05-06 13:39:10 +03:00
|
|
|
nsINode::IsApzAware() const
|
2015-09-17 13:16:20 +03:00
|
|
|
{
|
2016-05-06 13:39:10 +03:00
|
|
|
return IsNodeApzAware();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
nsINode::IsNodeApzAwareInternal() const
|
|
|
|
{
|
|
|
|
return EventTarget::IsApzAware();
|
2015-09-17 13:16:20 +03:00
|
|
|
}
|
2016-07-08 10:02:56 +03:00
|
|
|
|
2017-11-27 11:10:27 +03:00
|
|
|
DocGroup*
|
|
|
|
nsINode::GetDocGroup() const
|
|
|
|
{
|
|
|
|
return OwnerDoc()->GetDocGroup();
|
|
|
|
}
|
2018-02-22 01:07:53 +03:00
|
|
|
|
|
|
|
class LocalizationHandler : public PromiseNativeHandler
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
LocalizationHandler() = default;
|
|
|
|
|
|
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
|
|
NS_DECL_CYCLE_COLLECTION_CLASS(LocalizationHandler)
|
|
|
|
|
|
|
|
nsTArray<nsCOMPtr<Element>>& Elements() { return mElements; }
|
|
|
|
|
|
|
|
void SetReturnValuePromise(Promise* aReturnValuePromise)
|
|
|
|
{
|
|
|
|
mReturnValuePromise = aReturnValuePromise;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
|
|
|
|
{
|
|
|
|
nsTArray<L10nValue> l10nData;
|
|
|
|
if (aValue.isObject()) {
|
|
|
|
JS::ForOfIterator iter(aCx);
|
|
|
|
if (!iter.init(aValue, JS::ForOfIterator::AllowNonIterable)) {
|
|
|
|
mReturnValuePromise->MaybeRejectWithUndefined();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!iter.valueIsIterable()) {
|
|
|
|
mReturnValuePromise->MaybeRejectWithUndefined();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS::Rooted<JS::Value> temp(aCx);
|
|
|
|
while (true) {
|
|
|
|
bool done;
|
|
|
|
if (!iter.next(&temp, &done)) {
|
|
|
|
mReturnValuePromise->MaybeRejectWithUndefined();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (done) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
L10nValue* slotPtr =
|
|
|
|
l10nData.AppendElement(mozilla::fallible);
|
|
|
|
if (!slotPtr) {
|
|
|
|
mReturnValuePromise->MaybeRejectWithUndefined();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!slotPtr->Init(aCx, temp)) {
|
|
|
|
mReturnValuePromise->MaybeRejectWithUndefined();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mElements.Length() != l10nData.Length()) {
|
|
|
|
mReturnValuePromise->MaybeRejectWithUndefined();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> untranslatedElements(aCx,
|
|
|
|
JS_NewArrayObject(aCx, mElements.Length()));
|
|
|
|
if (!untranslatedElements) {
|
|
|
|
mReturnValuePromise->MaybeRejectWithUndefined();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorResult rv;
|
|
|
|
for (size_t i = 0; i < l10nData.Length(); ++i) {
|
|
|
|
Element* elem = mElements[i];
|
|
|
|
nsString& content = l10nData[i].mValue;
|
|
|
|
if (!content.IsVoid()) {
|
|
|
|
elem->SetTextContent(content, rv);
|
|
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
|
|
mReturnValuePromise->MaybeRejectWithUndefined();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Nullable<Sequence<AttributeNameValue>>& attributes =
|
2018-04-13 08:58:09 +03:00
|
|
|
l10nData[i].mAttributes;
|
2018-02-22 01:07:53 +03:00
|
|
|
if (!attributes.IsNull()) {
|
|
|
|
for (size_t j = 0; j < attributes.Value().Length(); ++j) {
|
|
|
|
// Use SetAttribute here to validate the attribute name!
|
|
|
|
elem->SetAttribute(attributes.Value()[j].mName,
|
|
|
|
attributes.Value()[j].mValue,
|
|
|
|
rv);
|
|
|
|
if (rv.Failed()) {
|
|
|
|
mReturnValuePromise->MaybeRejectWithUndefined();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (content.IsVoid() && attributes.IsNull()) {
|
|
|
|
JS::Rooted<JS::Value> wrappedElem(aCx);
|
|
|
|
if (!ToJSValue(aCx, elem, &wrappedElem)) {
|
|
|
|
mReturnValuePromise->MaybeRejectWithUndefined();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!JS_DefineElement(aCx, untranslatedElements, i, wrappedElem, JSPROP_ENUMERATE)) {
|
|
|
|
mReturnValuePromise->MaybeRejectWithUndefined();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-24 20:41:46 +03:00
|
|
|
|
|
|
|
JS::RootedObject sourceScope(aCx, JS::CurrentGlobalOrNull(aCx));
|
|
|
|
|
|
|
|
AutoEntryScript aes(mReturnValuePromise->GetParentObject(), "Promise resolution");
|
|
|
|
JSContext* cx = aes.cx();
|
|
|
|
JS::Rooted<JS::Value> result(cx, JS::ObjectValue(*untranslatedElements));
|
|
|
|
|
|
|
|
xpc::StackScopedCloneOptions options;
|
|
|
|
options.wrapReflectors = true;
|
|
|
|
StackScopedClone(cx, options, sourceScope, &result);
|
|
|
|
|
|
|
|
mReturnValuePromise->MaybeResolve(result);
|
2018-02-22 01:07:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
|
|
|
|
{
|
|
|
|
mReturnValuePromise->MaybeRejectWithUndefined();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
~LocalizationHandler() = default;
|
|
|
|
|
|
|
|
nsTArray<nsCOMPtr<Element>> mElements;
|
|
|
|
RefPtr<Promise> mReturnValuePromise;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LocalizationHandler)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(LocalizationHandler)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(LocalizationHandler)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(LocalizationHandler)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(LocalizationHandler)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValuePromise)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(LocalizationHandler)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValuePromise)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
|
|
|
|
|
|
|
already_AddRefed<Promise>
|
|
|
|
nsINode::Localize(JSContext* aCx,
|
|
|
|
mozilla::dom::L10nCallback& aCallback,
|
|
|
|
mozilla::ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
Sequence<L10nElement> l10nElements;
|
|
|
|
SequenceRooter<L10nElement> rooter(aCx, &l10nElements);
|
|
|
|
RefPtr<LocalizationHandler> nativeHandler = new LocalizationHandler();
|
|
|
|
nsTArray<nsCOMPtr<Element>>& domElements = nativeHandler->Elements();
|
|
|
|
nsIContent* node = IsContent() ? AsContent() : GetFirstChild();
|
|
|
|
nsAutoString l10nId;
|
|
|
|
nsAutoString l10nArgs;
|
|
|
|
nsAutoString l10nAttrs;
|
|
|
|
nsAutoString type;
|
|
|
|
for (; node; node = node->GetNextNode(this)) {
|
|
|
|
if (!node->IsElement()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Element* domElement = node->AsElement();
|
|
|
|
if (!domElement->GetAttr(kNameSpaceID_None, nsGkAtoms::datal10nid, l10nId)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
domElement->GetAttr(kNameSpaceID_None, nsGkAtoms::datal10nargs, l10nArgs);
|
|
|
|
domElement->GetAttr(kNameSpaceID_None, nsGkAtoms::datal10nattrs, l10nAttrs);
|
|
|
|
L10nElement* element = l10nElements.AppendElement(fallible);
|
|
|
|
if (!element) {
|
|
|
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
return nullptr;
|
|
|
|
}
|
2018-07-28 01:36:14 +03:00
|
|
|
if (!domElements.AppendElement(domElement, fallible)) {
|
|
|
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
return nullptr;
|
|
|
|
}
|
2018-02-22 01:07:53 +03:00
|
|
|
|
|
|
|
domElement->GetNamespaceURI(element->mNamespaceURI);
|
|
|
|
element->mLocalName = domElement->LocalName();
|
|
|
|
domElement->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
|
|
|
|
if (!type.IsEmpty()) {
|
|
|
|
element->mType = type;
|
|
|
|
}
|
|
|
|
element->mL10nId = l10nId;
|
|
|
|
if (!l10nAttrs.IsEmpty()) {
|
|
|
|
element->mL10nAttrs = l10nAttrs;
|
|
|
|
}
|
|
|
|
if (!l10nArgs.IsEmpty()) {
|
|
|
|
JS::Rooted<JS::Value> json(aCx);
|
|
|
|
if (!JS_ParseJSON(aCx, l10nArgs.get(), l10nArgs.Length(), &json) ||
|
|
|
|
!json.isObject()) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
element->mL10nArgs = &json.toObject();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<Promise> callbackResult = aCallback.Call(l10nElements, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<Promise> promise = Promise::Create(OwnerDoc()->GetParentObject(), aRv);
|
|
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nativeHandler->SetReturnValuePromise(promise);
|
|
|
|
callbackResult->AppendNativeHandler(nativeHandler);
|
|
|
|
|
|
|
|
return promise.forget();
|
|
|
|
}
|
2018-04-07 20:41:03 +03:00
|
|
|
|
2018-08-02 14:54:15 +03:00
|
|
|
nsINode*
|
|
|
|
nsINode::GetFlattenedTreeParentNodeNonInline() const
|
|
|
|
{
|
|
|
|
return GetFlattenedTreeParentNode();
|
|
|
|
}
|
|
|
|
|
2018-04-07 20:41:03 +03:00
|
|
|
NS_IMPL_ISUPPORTS(nsNodeWeakReference,
|
|
|
|
nsIWeakReference)
|
|
|
|
|
|
|
|
nsNodeWeakReference::nsNodeWeakReference(nsINode* aNode)
|
|
|
|
: nsIWeakReference(aNode)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
nsNodeWeakReference::~nsNodeWeakReference()
|
|
|
|
{
|
|
|
|
nsINode* node = static_cast<nsINode*>(mObject);
|
|
|
|
|
|
|
|
if (node) {
|
|
|
|
NS_ASSERTION(node->Slots()->mWeakReference == this,
|
|
|
|
"Weak reference has wrong value");
|
|
|
|
node->Slots()->mWeakReference = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsNodeWeakReference::QueryReferentFromScript(const nsIID& aIID, void** aInstancePtr)
|
|
|
|
{
|
|
|
|
return QueryReferent(aIID, aInstancePtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
nsNodeWeakReference::SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
|
|
|
{
|
|
|
|
return aMallocSizeOf(this);
|
|
|
|
}
|