content/base/src/nsINode.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/base/src/nsINode.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2695 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 + * Base class for all DOM nodes.
    1.12 + */
    1.13 +
    1.14 +#include "nsINode.h"
    1.15 +
    1.16 +#include "AccessCheck.h"
    1.17 +#include "jsapi.h"
    1.18 +#include "mozAutoDocUpdate.h"
    1.19 +#include "mozilla/AsyncEventDispatcher.h"
    1.20 +#include "mozilla/CORSMode.h"
    1.21 +#include "mozilla/EventDispatcher.h"
    1.22 +#include "mozilla/EventListenerManager.h"
    1.23 +#include "mozilla/InternalMutationEvent.h"
    1.24 +#include "mozilla/Likely.h"
    1.25 +#include "mozilla/MemoryReporting.h"
    1.26 +#include "mozilla/Telemetry.h"
    1.27 +#include "mozilla/dom/Element.h"
    1.28 +#include "mozilla/dom/Event.h"
    1.29 +#include "mozilla/dom/ShadowRoot.h"
    1.30 +#include "nsAttrValueOrString.h"
    1.31 +#include "nsBindingManager.h"
    1.32 +#include "nsCCUncollectableMarker.h"
    1.33 +#include "nsContentCreatorFunctions.h"
    1.34 +#include "nsContentList.h"
    1.35 +#include "nsContentUtils.h"
    1.36 +#include "nsCycleCollectionParticipant.h"
    1.37 +#include "nsDocument.h"
    1.38 +#include "mozilla/dom/Attr.h"
    1.39 +#include "nsDOMAttributeMap.h"
    1.40 +#include "nsDOMCID.h"
    1.41 +#include "nsDOMCSSAttrDeclaration.h"
    1.42 +#include "nsError.h"
    1.43 +#include "nsDOMMutationObserver.h"
    1.44 +#include "nsDOMString.h"
    1.45 +#include "nsDOMTokenList.h"
    1.46 +#include "nsFocusManager.h"
    1.47 +#include "nsFrameManager.h"
    1.48 +#include "nsFrameSelection.h"
    1.49 +#include "nsGenericHTMLElement.h"
    1.50 +#include "nsGkAtoms.h"
    1.51 +#include "nsIAnonymousContentCreator.h"
    1.52 +#include "nsIAtom.h"
    1.53 +#include "nsIBaseWindow.h"
    1.54 +#include "nsICategoryManager.h"
    1.55 +#include "nsIContentIterator.h"
    1.56 +#include "nsIControllers.h"
    1.57 +#include "nsIDocument.h"
    1.58 +#include "nsIDOMDocument.h"
    1.59 +#include "nsIDOMDocumentType.h"
    1.60 +#include "nsIDOMEvent.h"
    1.61 +#include "nsIDOMEventListener.h"
    1.62 +#include "nsIDOMMutationEvent.h"
    1.63 +#include "nsIDOMNodeList.h"
    1.64 +#include "nsIDOMUserDataHandler.h"
    1.65 +#include "nsIEditor.h"
    1.66 +#include "nsIEditorIMESupport.h"
    1.67 +#include "nsILinkHandler.h"
    1.68 +#include "nsINodeInfo.h"
    1.69 +#include "nsIPresShell.h"
    1.70 +#include "nsIScriptError.h"
    1.71 +#include "nsIScriptGlobalObject.h"
    1.72 +#include "nsIScriptSecurityManager.h"
    1.73 +#include "nsIScrollableFrame.h"
    1.74 +#include "nsIServiceManager.h"
    1.75 +#include "nsIURL.h"
    1.76 +#include "nsView.h"
    1.77 +#include "nsViewManager.h"
    1.78 +#include "nsIWebNavigation.h"
    1.79 +#include "nsIWidget.h"
    1.80 +#include "nsLayoutUtils.h"
    1.81 +#include "nsNameSpaceManager.h"
    1.82 +#include "nsNetUtil.h"
    1.83 +#include "nsNodeInfoManager.h"
    1.84 +#include "nsNodeUtils.h"
    1.85 +#include "nsPIBoxObject.h"
    1.86 +#include "nsPIDOMWindow.h"
    1.87 +#include "nsPresContext.h"
    1.88 +#include "nsRuleProcessorData.h"
    1.89 +#include "nsString.h"
    1.90 +#include "nsStyleConsts.h"
    1.91 +#include "nsSVGFeatures.h"
    1.92 +#include "nsSVGUtils.h"
    1.93 +#include "nsTextNode.h"
    1.94 +#include "nsUnicharUtils.h"
    1.95 +#include "nsXBLBinding.h"
    1.96 +#include "nsXBLPrototypeBinding.h"
    1.97 +#include "prprf.h"
    1.98 +#include "xpcpublic.h"
    1.99 +#include "nsCSSRuleProcessor.h"
   1.100 +#include "nsCSSParser.h"
   1.101 +#include "HTMLLegendElement.h"
   1.102 +#include "nsWrapperCacheInlines.h"
   1.103 +#include "WrapperFactory.h"
   1.104 +#include "DocumentType.h"
   1.105 +#include <algorithm>
   1.106 +#include "nsGlobalWindow.h"
   1.107 +#include "nsDOMMutationObserver.h"
   1.108 +#include "GeometryUtils.h"
   1.109 +
   1.110 +using namespace mozilla;
   1.111 +using namespace mozilla::dom;
   1.112 +
   1.113 +nsINode::nsSlots::~nsSlots()
   1.114 +{
   1.115 +  if (mChildNodes) {
   1.116 +    mChildNodes->DropReference();
   1.117 +    NS_RELEASE(mChildNodes);
   1.118 +  }
   1.119 +
   1.120 +  if (mWeakReference) {
   1.121 +    mWeakReference->NoticeNodeDestruction();
   1.122 +  }
   1.123 +}
   1.124 +
   1.125 +void
   1.126 +nsINode::nsSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
   1.127 +{
   1.128 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildNodes");
   1.129 +  cb.NoteXPCOMChild(mChildNodes);
   1.130 +}
   1.131 +
   1.132 +void
   1.133 +nsINode::nsSlots::Unlink()
   1.134 +{
   1.135 +  if (mChildNodes) {
   1.136 +    mChildNodes->DropReference();
   1.137 +    NS_RELEASE(mChildNodes);
   1.138 +  }
   1.139 +}
   1.140 +
   1.141 +//----------------------------------------------------------------------
   1.142 +
   1.143 +nsINode::~nsINode()
   1.144 +{
   1.145 +  MOZ_ASSERT(!HasSlots(), "nsNodeUtils::LastRelease was not called?");
   1.146 +  MOZ_ASSERT(mSubtreeRoot == this, "Didn't restore state properly?");
   1.147 +}
   1.148 +
   1.149 +void*
   1.150 +nsINode::GetProperty(uint16_t aCategory, nsIAtom *aPropertyName,
   1.151 +                     nsresult *aStatus) const
   1.152 +{
   1.153 +  return OwnerDoc()->PropertyTable(aCategory)->GetProperty(this, aPropertyName,
   1.154 +                                                           aStatus);
   1.155 +}
   1.156 +
   1.157 +nsresult
   1.158 +nsINode::SetProperty(uint16_t aCategory, nsIAtom *aPropertyName, void *aValue,
   1.159 +                     NSPropertyDtorFunc aDtor, bool aTransfer,
   1.160 +                     void **aOldValue)
   1.161 +{
   1.162 +  nsresult rv = OwnerDoc()->PropertyTable(aCategory)->SetProperty(this,
   1.163 +                                                                  aPropertyName,
   1.164 +                                                                  aValue, aDtor,
   1.165 +                                                                  nullptr,
   1.166 +                                                                  aTransfer,
   1.167 +                                                                  aOldValue);
   1.168 +  if (NS_SUCCEEDED(rv)) {
   1.169 +    SetFlags(NODE_HAS_PROPERTIES);
   1.170 +  }
   1.171 +
   1.172 +  return rv;
   1.173 +}
   1.174 +
   1.175 +void
   1.176 +nsINode::DeleteProperty(uint16_t aCategory, nsIAtom *aPropertyName)
   1.177 +{
   1.178 +  OwnerDoc()->PropertyTable(aCategory)->DeleteProperty(this, aPropertyName);
   1.179 +}
   1.180 +
   1.181 +void*
   1.182 +nsINode::UnsetProperty(uint16_t aCategory, nsIAtom *aPropertyName,
   1.183 +                       nsresult *aStatus)
   1.184 +{
   1.185 +  return OwnerDoc()->PropertyTable(aCategory)->UnsetProperty(this,
   1.186 +                                                             aPropertyName,
   1.187 +                                                             aStatus);
   1.188 +}
   1.189 +
   1.190 +nsINode::nsSlots*
   1.191 +nsINode::CreateSlots()
   1.192 +{
   1.193 +  return new nsSlots();
   1.194 +}
   1.195 +
   1.196 +bool
   1.197 +nsINode::IsEditableInternal() const
   1.198 +{
   1.199 +  if (HasFlag(NODE_IS_EDITABLE)) {
   1.200 +    // The node is in an editable contentEditable subtree.
   1.201 +    return true;
   1.202 +  }
   1.203 +
   1.204 +  nsIDocument *doc = GetCurrentDoc();
   1.205 +
   1.206 +  // Check if the node is in a document and the document is in designMode.
   1.207 +  return doc && doc->HasFlag(NODE_IS_EDITABLE);
   1.208 +}
   1.209 +
   1.210 +static nsIContent* GetEditorRootContent(nsIEditor* aEditor)
   1.211 +{
   1.212 +  nsCOMPtr<nsIDOMElement> rootElement;
   1.213 +  aEditor->GetRootElement(getter_AddRefs(rootElement));
   1.214 +  nsCOMPtr<nsIContent> rootContent(do_QueryInterface(rootElement));
   1.215 +  return rootContent;
   1.216 +}
   1.217 +
   1.218 +nsIContent*
   1.219 +nsINode::GetTextEditorRootContent(nsIEditor** aEditor)
   1.220 +{
   1.221 +  if (aEditor)
   1.222 +    *aEditor = nullptr;
   1.223 +  for (nsINode* node = this; node; node = node->GetParentNode()) {
   1.224 +    if (!node->IsElement() ||
   1.225 +        !node->AsElement()->IsHTML())
   1.226 +      continue;
   1.227 +
   1.228 +    nsCOMPtr<nsIEditor> editor =
   1.229 +      static_cast<nsGenericHTMLElement*>(node)->GetEditorInternal();
   1.230 +    if (!editor)
   1.231 +      continue;
   1.232 +
   1.233 +    nsIContent* rootContent = GetEditorRootContent(editor);
   1.234 +    if (aEditor)
   1.235 +      editor.swap(*aEditor);
   1.236 +    return rootContent;
   1.237 +  }
   1.238 +  return nullptr;
   1.239 +}
   1.240 +
   1.241 +static nsIContent* GetRootForContentSubtree(nsIContent* aContent)
   1.242 +{
   1.243 +  NS_ENSURE_TRUE(aContent, nullptr);
   1.244 +
   1.245 +  // Special case for ShadowRoot because the ShadowRoot itself is
   1.246 +  // the root. This is necessary to prevent selection from crossing
   1.247 +  // the ShadowRoot boundary.
   1.248 +  ShadowRoot* containingShadow = aContent->GetContainingShadow();
   1.249 +  if (containingShadow) {
   1.250 +    return containingShadow;
   1.251 +  }
   1.252 +
   1.253 +  nsIContent* stop = aContent->GetBindingParent();
   1.254 +  while (aContent) {
   1.255 +    nsIContent* parent = aContent->GetParent();
   1.256 +    if (parent == stop) {
   1.257 +      break;
   1.258 +    }
   1.259 +    aContent = parent;
   1.260 +  }
   1.261 +  return aContent;
   1.262 +}
   1.263 +
   1.264 +nsIContent*
   1.265 +nsINode::GetSelectionRootContent(nsIPresShell* aPresShell)
   1.266 +{
   1.267 +  NS_ENSURE_TRUE(aPresShell, nullptr);
   1.268 +
   1.269 +  if (IsNodeOfType(eDOCUMENT))
   1.270 +    return static_cast<nsIDocument*>(this)->GetRootElement();
   1.271 +  if (!IsNodeOfType(eCONTENT))
   1.272 +    return nullptr;
   1.273 +
   1.274 +  if (GetCurrentDoc() != aPresShell->GetDocument()) {
   1.275 +    return nullptr;
   1.276 +  }
   1.277 +
   1.278 +  if (static_cast<nsIContent*>(this)->HasIndependentSelection()) {
   1.279 +    // This node should be a descendant of input/textarea editor.
   1.280 +    nsIContent* content = GetTextEditorRootContent();
   1.281 +    if (content)
   1.282 +      return content;
   1.283 +  }
   1.284 +
   1.285 +  nsPresContext* presContext = aPresShell->GetPresContext();
   1.286 +  if (presContext) {
   1.287 +    nsIEditor* editor = nsContentUtils::GetHTMLEditor(presContext);
   1.288 +    if (editor) {
   1.289 +      // This node is in HTML editor.
   1.290 +      nsIDocument* doc = GetCurrentDoc();
   1.291 +      if (!doc || doc->HasFlag(NODE_IS_EDITABLE) ||
   1.292 +          !HasFlag(NODE_IS_EDITABLE)) {
   1.293 +        nsIContent* editorRoot = GetEditorRootContent(editor);
   1.294 +        NS_ENSURE_TRUE(editorRoot, nullptr);
   1.295 +        return nsContentUtils::IsInSameAnonymousTree(this, editorRoot) ?
   1.296 +                 editorRoot :
   1.297 +                 GetRootForContentSubtree(static_cast<nsIContent*>(this));
   1.298 +      }
   1.299 +      // If the document isn't editable but this is editable, this is in
   1.300 +      // contenteditable.  Use the editing host element for selection root.
   1.301 +      return static_cast<nsIContent*>(this)->GetEditingHost();
   1.302 +    }
   1.303 +  }
   1.304 +
   1.305 +  nsRefPtr<nsFrameSelection> fs = aPresShell->FrameSelection();
   1.306 +  nsIContent* content = fs->GetLimiter();
   1.307 +  if (!content) {
   1.308 +    content = fs->GetAncestorLimiter();
   1.309 +    if (!content) {
   1.310 +      nsIDocument* doc = aPresShell->GetDocument();
   1.311 +      NS_ENSURE_TRUE(doc, nullptr);
   1.312 +      content = doc->GetRootElement();
   1.313 +      if (!content)
   1.314 +        return nullptr;
   1.315 +    }
   1.316 +  }
   1.317 +
   1.318 +  // This node might be in another subtree, if so, we should find this subtree's
   1.319 +  // root.  Otherwise, we can return the content simply.
   1.320 +  NS_ENSURE_TRUE(content, nullptr);
   1.321 +  if (!nsContentUtils::IsInSameAnonymousTree(this, content)) {
   1.322 +    content = GetRootForContentSubtree(static_cast<nsIContent*>(this));
   1.323 +    // Fixup for ShadowRoot because the ShadowRoot itself does not have a frame.
   1.324 +    // Use the host as the root.
   1.325 +    ShadowRoot* shadowRoot = ShadowRoot::FromNode(content);
   1.326 +    if (shadowRoot) {
   1.327 +      content = shadowRoot->GetHost();
   1.328 +    }
   1.329 +  }
   1.330 +
   1.331 +  return content;
   1.332 +}
   1.333 +
   1.334 +nsINodeList*
   1.335 +nsINode::ChildNodes()
   1.336 +{
   1.337 +  nsSlots* slots = Slots();
   1.338 +  if (!slots->mChildNodes) {
   1.339 +    slots->mChildNodes = new nsChildContentList(this);
   1.340 +    if (slots->mChildNodes) {
   1.341 +      NS_ADDREF(slots->mChildNodes);
   1.342 +    }
   1.343 +  }
   1.344 +
   1.345 +  return slots->mChildNodes;
   1.346 +}
   1.347 +
   1.348 +void
   1.349 +nsINode::GetTextContentInternal(nsAString& aTextContent)
   1.350 +{
   1.351 +  SetDOMStringToNull(aTextContent);
   1.352 +}
   1.353 +
   1.354 +#ifdef DEBUG
   1.355 +void
   1.356 +nsINode::CheckNotNativeAnonymous() const
   1.357 +{
   1.358 +  if (!IsNodeOfType(eCONTENT))
   1.359 +    return;
   1.360 +  nsIContent* content = static_cast<const nsIContent *>(this)->GetBindingParent();
   1.361 +  while (content) {
   1.362 +    if (content->IsRootOfNativeAnonymousSubtree()) {
   1.363 +      NS_ERROR("Element not marked to be in native anonymous subtree!");
   1.364 +      break;
   1.365 +    }
   1.366 +    content = content->GetBindingParent();
   1.367 +  }
   1.368 +}
   1.369 +#endif
   1.370 +
   1.371 +bool
   1.372 +nsINode::IsInAnonymousSubtree() const
   1.373 +{
   1.374 +  if (!IsContent()) {
   1.375 +    return false;
   1.376 +  }
   1.377 +
   1.378 +  return AsContent()->IsInAnonymousSubtree();
   1.379 +}
   1.380 +
   1.381 +bool
   1.382 +nsINode::IsAnonymousContentInSVGUseSubtree() const
   1.383 +{
   1.384 +  MOZ_ASSERT(IsInAnonymousSubtree());
   1.385 +  nsIContent* parent = AsContent()->GetBindingParent();
   1.386 +  // Watch out for parentless native-anonymous subtrees.
   1.387 +  return parent && parent->IsSVG(nsGkAtoms::use);
   1.388 +}
   1.389 +
   1.390 +nsresult
   1.391 +nsINode::GetParentNode(nsIDOMNode** aParentNode)
   1.392 +{
   1.393 +  *aParentNode = nullptr;
   1.394 +
   1.395 +  nsINode *parent = GetParentNode();
   1.396 +
   1.397 +  return parent ? CallQueryInterface(parent, aParentNode) : NS_OK;
   1.398 +}
   1.399 +
   1.400 +nsresult
   1.401 +nsINode::GetParentElement(nsIDOMElement** aParentElement)
   1.402 +{
   1.403 +  *aParentElement = nullptr;
   1.404 +  nsINode* parent = GetParentElement();
   1.405 +  return parent ? CallQueryInterface(parent, aParentElement) : NS_OK;
   1.406 +}
   1.407 +
   1.408 +nsresult
   1.409 +nsINode::GetChildNodes(nsIDOMNodeList** aChildNodes)
   1.410 +{
   1.411 +  NS_ADDREF(*aChildNodes = ChildNodes());
   1.412 +
   1.413 +  return NS_OK;
   1.414 +}
   1.415 +
   1.416 +nsresult
   1.417 +nsINode::GetFirstChild(nsIDOMNode** aNode)
   1.418 +{
   1.419 +  nsIContent* child = GetFirstChild();
   1.420 +  if (child) {
   1.421 +    return CallQueryInterface(child, aNode);
   1.422 +  }
   1.423 +
   1.424 +  *aNode = nullptr;
   1.425 +
   1.426 +  return NS_OK;
   1.427 +}
   1.428 +
   1.429 +nsresult
   1.430 +nsINode::GetLastChild(nsIDOMNode** aNode)
   1.431 +{
   1.432 +  nsIContent* child = GetLastChild();
   1.433 +  if (child) {
   1.434 +    return CallQueryInterface(child, aNode);
   1.435 +  }
   1.436 +
   1.437 +  *aNode = nullptr;
   1.438 +
   1.439 +  return NS_OK;
   1.440 +}
   1.441 +
   1.442 +nsresult
   1.443 +nsINode::GetPreviousSibling(nsIDOMNode** aPrevSibling)
   1.444 +{
   1.445 +  *aPrevSibling = nullptr;
   1.446 +
   1.447 +  nsIContent *sibling = GetPreviousSibling();
   1.448 +
   1.449 +  return sibling ? CallQueryInterface(sibling, aPrevSibling) : NS_OK;
   1.450 +}
   1.451 +
   1.452 +nsresult
   1.453 +nsINode::GetNextSibling(nsIDOMNode** aNextSibling)
   1.454 +{
   1.455 +  *aNextSibling = nullptr;
   1.456 +
   1.457 +  nsIContent *sibling = GetNextSibling();
   1.458 +
   1.459 +  return sibling ? CallQueryInterface(sibling, aNextSibling) : NS_OK;
   1.460 +}
   1.461 +
   1.462 +nsresult
   1.463 +nsINode::GetOwnerDocument(nsIDOMDocument** aOwnerDocument)
   1.464 +{
   1.465 +  *aOwnerDocument = nullptr;
   1.466 +
   1.467 +  nsIDocument *ownerDoc = GetOwnerDocument();
   1.468 +
   1.469 +  return ownerDoc ? CallQueryInterface(ownerDoc, aOwnerDocument) : NS_OK;
   1.470 +}
   1.471 +
   1.472 +void
   1.473 +nsINode::GetNodeValueInternal(nsAString& aNodeValue)
   1.474 +{
   1.475 +  SetDOMStringToNull(aNodeValue);
   1.476 +}
   1.477 +
   1.478 +nsINode*
   1.479 +nsINode::RemoveChild(nsINode& aOldChild, ErrorResult& aError)
   1.480 +{
   1.481 +  if (IsNodeOfType(eDATA_NODE)) {
   1.482 +    // aOldChild can't be one of our children.
   1.483 +    aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
   1.484 +    return nullptr;
   1.485 +  }
   1.486 +
   1.487 +  if (aOldChild.GetParentNode() == this) {
   1.488 +    nsContentUtils::MaybeFireNodeRemoved(&aOldChild, this, OwnerDoc());
   1.489 +  }
   1.490 +
   1.491 +  int32_t index = IndexOf(&aOldChild);
   1.492 +  if (index == -1) {
   1.493 +    // aOldChild isn't one of our children.
   1.494 +    aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
   1.495 +    return nullptr;
   1.496 +  }
   1.497 +
   1.498 +  RemoveChildAt(index, true);
   1.499 +  return &aOldChild;
   1.500 +}
   1.501 +
   1.502 +nsresult
   1.503 +nsINode::RemoveChild(nsIDOMNode* aOldChild, nsIDOMNode** aReturn)
   1.504 +{
   1.505 +  nsCOMPtr<nsINode> oldChild = do_QueryInterface(aOldChild);
   1.506 +  if (!oldChild) {
   1.507 +    return NS_ERROR_NULL_POINTER;
   1.508 +  }
   1.509 +
   1.510 +  ErrorResult rv;
   1.511 +  RemoveChild(*oldChild, rv);
   1.512 +  if (!rv.Failed()) {
   1.513 +    NS_ADDREF(*aReturn = aOldChild);
   1.514 +  }
   1.515 +  return rv.ErrorCode();
   1.516 +}
   1.517 +
   1.518 +void
   1.519 +nsINode::Normalize()
   1.520 +{
   1.521 +  // First collect list of nodes to be removed
   1.522 +  nsAutoTArray<nsCOMPtr<nsIContent>, 50> nodes;
   1.523 +
   1.524 +  bool canMerge = false;
   1.525 +  for (nsIContent* node = this->GetFirstChild();
   1.526 +       node;
   1.527 +       node = node->GetNextNode(this)) {
   1.528 +    if (node->NodeType() != nsIDOMNode::TEXT_NODE) {
   1.529 +      canMerge = false;
   1.530 +      continue;
   1.531 +    }
   1.532 +
   1.533 +    if (canMerge || node->TextLength() == 0) {
   1.534 +      // No need to touch canMerge. That way we can merge across empty
   1.535 +      // textnodes if and only if the node before is a textnode
   1.536 +      nodes.AppendElement(node);
   1.537 +    }
   1.538 +    else {
   1.539 +      canMerge = true;
   1.540 +    }
   1.541 +
   1.542 +    // If there's no following sibling, then we need to ensure that we don't
   1.543 +    // collect following siblings of our (grand)parent as to-be-removed
   1.544 +    canMerge = canMerge && !!node->GetNextSibling();
   1.545 +  }
   1.546 +
   1.547 +  if (nodes.IsEmpty()) {
   1.548 +    return;
   1.549 +  }
   1.550 +
   1.551 +  // We're relying on mozAutoSubtreeModified to keep the doc alive here.
   1.552 +  nsIDocument* doc = OwnerDoc();
   1.553 +
   1.554 +  // Batch possible DOMSubtreeModified events.
   1.555 +  mozAutoSubtreeModified subtree(doc, nullptr);
   1.556 +
   1.557 +  // Fire all DOMNodeRemoved events. Optimize the common case of there being
   1.558 +  // no listeners
   1.559 +  bool hasRemoveListeners = nsContentUtils::
   1.560 +      HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED);
   1.561 +  if (hasRemoveListeners) {
   1.562 +    for (uint32_t i = 0; i < nodes.Length(); ++i) {
   1.563 +      nsINode* parentNode = nodes[i]->GetParentNode();
   1.564 +      if (parentNode) { // Node may have already been removed.
   1.565 +        nsContentUtils::MaybeFireNodeRemoved(nodes[i], parentNode,
   1.566 +                                             doc);
   1.567 +      }
   1.568 +    }
   1.569 +  }
   1.570 +
   1.571 +  mozAutoDocUpdate batch(doc, UPDATE_CONTENT_MODEL, true);
   1.572 +
   1.573 +  // Merge and remove all nodes
   1.574 +  nsAutoString tmpStr;
   1.575 +  for (uint32_t i = 0; i < nodes.Length(); ++i) {
   1.576 +    nsIContent* node = nodes[i];
   1.577 +    // Merge with previous node unless empty
   1.578 +    const nsTextFragment* text = node->GetText();
   1.579 +    if (text->GetLength()) {
   1.580 +      nsIContent* target = node->GetPreviousSibling();
   1.581 +      NS_ASSERTION((target && target->NodeType() == nsIDOMNode::TEXT_NODE) ||
   1.582 +                   hasRemoveListeners,
   1.583 +                   "Should always have a previous text sibling unless "
   1.584 +                   "mutation events messed us up");
   1.585 +      if (!hasRemoveListeners ||
   1.586 +          (target && target->NodeType() == nsIDOMNode::TEXT_NODE)) {
   1.587 +        nsTextNode* t = static_cast<nsTextNode*>(target);
   1.588 +        if (text->Is2b()) {
   1.589 +          t->AppendTextForNormalize(text->Get2b(), text->GetLength(), true, node);
   1.590 +        }
   1.591 +        else {
   1.592 +          tmpStr.Truncate();
   1.593 +          text->AppendTo(tmpStr);
   1.594 +          t->AppendTextForNormalize(tmpStr.get(), tmpStr.Length(), true, node);
   1.595 +        }
   1.596 +      }
   1.597 +    }
   1.598 +
   1.599 +    // Remove node
   1.600 +    nsCOMPtr<nsINode> parent = node->GetParentNode();
   1.601 +    NS_ASSERTION(parent || hasRemoveListeners,
   1.602 +                 "Should always have a parent unless "
   1.603 +                 "mutation events messed us up");
   1.604 +    if (parent) {
   1.605 +      parent->RemoveChildAt(parent->IndexOf(node), true);
   1.606 +    }
   1.607 +  }
   1.608 +}
   1.609 +
   1.610 +void
   1.611 +nsINode::GetBaseURI(nsAString &aURI) const
   1.612 +{
   1.613 +  nsCOMPtr<nsIURI> baseURI = GetBaseURI();
   1.614 +
   1.615 +  nsAutoCString spec;
   1.616 +  if (baseURI) {
   1.617 +    baseURI->GetSpec(spec);
   1.618 +  }
   1.619 +
   1.620 +  CopyUTF8toUTF16(spec, aURI);
   1.621 +}
   1.622 +
   1.623 +void
   1.624 +nsINode::GetBaseURIFromJS(nsAString& aURI) const
   1.625 +{
   1.626 +  nsCOMPtr<nsIURI> baseURI = GetBaseURI(nsContentUtils::IsCallerChrome());
   1.627 +  nsAutoCString spec;
   1.628 +  if (baseURI) {
   1.629 +    baseURI->GetSpec(spec);
   1.630 +  }
   1.631 +  CopyUTF8toUTF16(spec, aURI);
   1.632 +}
   1.633 +
   1.634 +already_AddRefed<nsIURI>
   1.635 +nsINode::GetBaseURIObject() const
   1.636 +{
   1.637 +  return GetBaseURI(true);
   1.638 +}
   1.639 +
   1.640 +void
   1.641 +nsINode::LookupPrefix(const nsAString& aNamespaceURI, nsAString& aPrefix)
   1.642 +{
   1.643 +  Element *element = GetNameSpaceElement();
   1.644 +  if (element) {
   1.645 +    // XXX Waiting for DOM spec to list error codes.
   1.646 +  
   1.647 +    // Trace up the content parent chain looking for the namespace
   1.648 +    // declaration that defines the aNamespaceURI namespace. Once found,
   1.649 +    // return the prefix (i.e. the attribute localName).
   1.650 +    for (nsIContent* content = element; content;
   1.651 +         content = content->GetParent()) {
   1.652 +      uint32_t attrCount = content->GetAttrCount();
   1.653 +  
   1.654 +      for (uint32_t i = 0; i < attrCount; ++i) {
   1.655 +        const nsAttrName* name = content->GetAttrNameAt(i);
   1.656 +  
   1.657 +        if (name->NamespaceEquals(kNameSpaceID_XMLNS) &&
   1.658 +            content->AttrValueIs(kNameSpaceID_XMLNS, name->LocalName(),
   1.659 +                                 aNamespaceURI, eCaseMatters)) {
   1.660 +          // If the localName is "xmlns", the prefix we output should be
   1.661 +          // null.
   1.662 +          nsIAtom *localName = name->LocalName();
   1.663 +  
   1.664 +          if (localName != nsGkAtoms::xmlns) {
   1.665 +            localName->ToString(aPrefix);
   1.666 +          }
   1.667 +          else {
   1.668 +            SetDOMStringToNull(aPrefix);
   1.669 +          }
   1.670 +          return;
   1.671 +        }
   1.672 +      }
   1.673 +    }
   1.674 +  }
   1.675 +
   1.676 +  SetDOMStringToNull(aPrefix);
   1.677 +}
   1.678 +
   1.679 +static nsresult
   1.680 +SetUserDataProperty(uint16_t aCategory, nsINode *aNode, nsIAtom *aKey,
   1.681 +                    nsISupports* aValue, void** aOldValue)
   1.682 +{
   1.683 +  nsresult rv = aNode->SetProperty(aCategory, aKey, aValue,
   1.684 +                                   nsPropertyTable::SupportsDtorFunc, true,
   1.685 +                                   aOldValue);
   1.686 +  NS_ENSURE_SUCCESS(rv, rv);
   1.687 +
   1.688 +  // Property table owns it now.
   1.689 +  NS_ADDREF(aValue);
   1.690 +
   1.691 +  return NS_OK;
   1.692 +}
   1.693 +
   1.694 +nsresult
   1.695 +nsINode::SetUserData(const nsAString &aKey, nsIVariant *aData,
   1.696 +                     nsIDOMUserDataHandler *aHandler, nsIVariant **aResult)
   1.697 +{
   1.698 +  OwnerDoc()->WarnOnceAbout(nsIDocument::eGetSetUserData);
   1.699 +  *aResult = nullptr;
   1.700 +
   1.701 +  nsCOMPtr<nsIAtom> key = do_GetAtom(aKey);
   1.702 +  if (!key) {
   1.703 +    return NS_ERROR_OUT_OF_MEMORY;
   1.704 +  }
   1.705 +
   1.706 +  nsresult rv;
   1.707 +  void *data;
   1.708 +  if (aData) {
   1.709 +    rv = SetUserDataProperty(DOM_USER_DATA, this, key, aData, &data);
   1.710 +    NS_ENSURE_SUCCESS(rv, rv);
   1.711 +  }
   1.712 +  else {
   1.713 +    data = UnsetProperty(DOM_USER_DATA, key);
   1.714 +  }
   1.715 +
   1.716 +  // Take over ownership of the old data from the property table.
   1.717 +  nsCOMPtr<nsIVariant> oldData = dont_AddRef(static_cast<nsIVariant*>(data));
   1.718 +
   1.719 +  if (aData && aHandler) {
   1.720 +    nsCOMPtr<nsIDOMUserDataHandler> oldHandler;
   1.721 +    rv = SetUserDataProperty(DOM_USER_DATA_HANDLER, this, key, aHandler,
   1.722 +                             getter_AddRefs(oldHandler));
   1.723 +    if (NS_FAILED(rv)) {
   1.724 +      // We failed to set the handler, remove the data.
   1.725 +      DeleteProperty(DOM_USER_DATA, key);
   1.726 +
   1.727 +      return rv;
   1.728 +    }
   1.729 +  }
   1.730 +  else {
   1.731 +    DeleteProperty(DOM_USER_DATA_HANDLER, key);
   1.732 +  }
   1.733 +
   1.734 +  oldData.swap(*aResult);
   1.735 +
   1.736 +  return NS_OK;
   1.737 +}
   1.738 +
   1.739 +void
   1.740 +nsINode::SetUserData(JSContext* aCx, const nsAString& aKey,
   1.741 +                     JS::Handle<JS::Value> aData,
   1.742 +                     nsIDOMUserDataHandler* aHandler,
   1.743 +                     JS::MutableHandle<JS::Value> aRetval,
   1.744 +                     ErrorResult& aError)
   1.745 +{
   1.746 +  nsCOMPtr<nsIVariant> data;
   1.747 +  aError = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData, getter_AddRefs(data));
   1.748 +  if (aError.Failed()) {
   1.749 +    return;
   1.750 +  }
   1.751 +
   1.752 +  nsCOMPtr<nsIVariant> oldData;
   1.753 +  aError = SetUserData(aKey, data, aHandler, getter_AddRefs(oldData));
   1.754 +  if (aError.Failed()) {
   1.755 +    return;
   1.756 +  }
   1.757 +
   1.758 +  if (!oldData) {
   1.759 +    aRetval.setNull();
   1.760 +    return;
   1.761 +  }
   1.762 +
   1.763 +  JSAutoCompartment ac(aCx, GetWrapper());
   1.764 +  aError = nsContentUtils::XPConnect()->VariantToJS(aCx, GetWrapper(), oldData,
   1.765 +                                                    aRetval);
   1.766 +}
   1.767 +
   1.768 +nsIVariant*
   1.769 +nsINode::GetUserData(const nsAString& aKey)
   1.770 +{
   1.771 +  OwnerDoc()->WarnOnceAbout(nsIDocument::eGetSetUserData);
   1.772 +  nsCOMPtr<nsIAtom> key = do_GetAtom(aKey);
   1.773 +  if (!key) {
   1.774 +    return nullptr;
   1.775 +  }
   1.776 +
   1.777 +  return static_cast<nsIVariant*>(GetProperty(DOM_USER_DATA, key));
   1.778 +}
   1.779 +
   1.780 +void
   1.781 +nsINode::GetUserData(JSContext* aCx, const nsAString& aKey,
   1.782 +                     JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
   1.783 +{
   1.784 +  nsIVariant* data = GetUserData(aKey);
   1.785 +  if (!data) {
   1.786 +    aRetval.setNull();
   1.787 +    return;
   1.788 +  }
   1.789 +
   1.790 +  JSAutoCompartment ac(aCx, GetWrapper());
   1.791 +  aError = nsContentUtils::XPConnect()->VariantToJS(aCx, GetWrapper(), data,
   1.792 +                                                    aRetval);
   1.793 +}
   1.794 +
   1.795 +uint16_t
   1.796 +nsINode::CompareDocumentPosition(nsINode& aOtherNode) const
   1.797 +{
   1.798 +  if (this == &aOtherNode) {
   1.799 +    return 0;
   1.800 +  }
   1.801 +  if (GetPreviousSibling() == &aOtherNode) {
   1.802 +    MOZ_ASSERT(GetParentNode() == aOtherNode.GetParentNode());
   1.803 +    return static_cast<uint16_t>(nsIDOMNode::DOCUMENT_POSITION_PRECEDING);
   1.804 +  }
   1.805 +  if (GetNextSibling() == &aOtherNode) {
   1.806 +    MOZ_ASSERT(GetParentNode() == aOtherNode.GetParentNode());
   1.807 +    return static_cast<uint16_t>(nsIDOMNode::DOCUMENT_POSITION_FOLLOWING);
   1.808 +  }
   1.809 +
   1.810 +  nsAutoTArray<const nsINode*, 32> parents1, parents2;
   1.811 +
   1.812 +  const nsINode *node1 = &aOtherNode, *node2 = this;
   1.813 +
   1.814 +  // Check if either node is an attribute
   1.815 +  const Attr* attr1 = nullptr;
   1.816 +  if (node1->IsNodeOfType(nsINode::eATTRIBUTE)) {
   1.817 +    attr1 = static_cast<const Attr*>(node1);
   1.818 +    const Element* elem = attr1->GetElement();
   1.819 +    // If there is an owner element add the attribute
   1.820 +    // to the chain and walk up to the element
   1.821 +    if (elem) {
   1.822 +      node1 = elem;
   1.823 +      parents1.AppendElement(attr1);
   1.824 +    }
   1.825 +  }
   1.826 +  if (node2->IsNodeOfType(nsINode::eATTRIBUTE)) {
   1.827 +    const Attr* attr2 = static_cast<const Attr*>(node2);
   1.828 +    const Element* elem = attr2->GetElement();
   1.829 +    if (elem == node1 && attr1) {
   1.830 +      // Both nodes are attributes on the same element.
   1.831 +      // Compare position between the attributes.
   1.832 +
   1.833 +      uint32_t i;
   1.834 +      const nsAttrName* attrName;
   1.835 +      for (i = 0; (attrName = elem->GetAttrNameAt(i)); ++i) {
   1.836 +        if (attrName->Equals(attr1->NodeInfo())) {
   1.837 +          NS_ASSERTION(!attrName->Equals(attr2->NodeInfo()),
   1.838 +                       "Different attrs at same position");
   1.839 +          return nsIDOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
   1.840 +            nsIDOMNode::DOCUMENT_POSITION_PRECEDING;
   1.841 +        }
   1.842 +        if (attrName->Equals(attr2->NodeInfo())) {
   1.843 +          return nsIDOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
   1.844 +            nsIDOMNode::DOCUMENT_POSITION_FOLLOWING;
   1.845 +        }
   1.846 +      }
   1.847 +      NS_NOTREACHED("neither attribute in the element");
   1.848 +      return nsIDOMNode::DOCUMENT_POSITION_DISCONNECTED;
   1.849 +    }
   1.850 +
   1.851 +    if (elem) {
   1.852 +      node2 = elem;
   1.853 +      parents2.AppendElement(attr2);
   1.854 +    }
   1.855 +  }
   1.856 +
   1.857 +  // We now know that both nodes are either nsIContents or nsIDocuments.
   1.858 +  // If either node started out as an attribute, that attribute will have
   1.859 +  // the same relative position as its ownerElement, except if the
   1.860 +  // ownerElement ends up being the container for the other node
   1.861 +
   1.862 +  // Build the chain of parents
   1.863 +  do {
   1.864 +    parents1.AppendElement(node1);
   1.865 +    node1 = node1->GetParentNode();
   1.866 +  } while (node1);
   1.867 +  do {
   1.868 +    parents2.AppendElement(node2);
   1.869 +    node2 = node2->GetParentNode();
   1.870 +  } while (node2);
   1.871 +
   1.872 +  // Check if the nodes are disconnected.
   1.873 +  uint32_t pos1 = parents1.Length();
   1.874 +  uint32_t pos2 = parents2.Length();
   1.875 +  const nsINode* top1 = parents1.ElementAt(--pos1);
   1.876 +  const nsINode* top2 = parents2.ElementAt(--pos2);
   1.877 +  if (top1 != top2) {
   1.878 +    return top1 < top2 ?
   1.879 +      (nsIDOMNode::DOCUMENT_POSITION_PRECEDING |
   1.880 +       nsIDOMNode::DOCUMENT_POSITION_DISCONNECTED |
   1.881 +       nsIDOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC) :
   1.882 +      (nsIDOMNode::DOCUMENT_POSITION_FOLLOWING |
   1.883 +       nsIDOMNode::DOCUMENT_POSITION_DISCONNECTED |
   1.884 +       nsIDOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
   1.885 +  }
   1.886 +
   1.887 +  // Find where the parent chain differs and check indices in the parent.
   1.888 +  const nsINode* parent = top1;
   1.889 +  uint32_t len;
   1.890 +  for (len = std::min(pos1, pos2); len > 0; --len) {
   1.891 +    const nsINode* child1 = parents1.ElementAt(--pos1);
   1.892 +    const nsINode* child2 = parents2.ElementAt(--pos2);
   1.893 +    if (child1 != child2) {
   1.894 +      // child1 or child2 can be an attribute here. This will work fine since
   1.895 +      // IndexOf will return -1 for the attribute making the attribute be
   1.896 +      // considered before any child.
   1.897 +      return parent->IndexOf(child1) < parent->IndexOf(child2) ?
   1.898 +        static_cast<uint16_t>(nsIDOMNode::DOCUMENT_POSITION_PRECEDING) :
   1.899 +        static_cast<uint16_t>(nsIDOMNode::DOCUMENT_POSITION_FOLLOWING);
   1.900 +    }
   1.901 +    parent = child1;
   1.902 +  }
   1.903 +
   1.904 +  // We hit the end of one of the parent chains without finding a difference
   1.905 +  // between the chains. That must mean that one node is an ancestor of the
   1.906 +  // other. The one with the shortest chain must be the ancestor.
   1.907 +  return pos1 < pos2 ?
   1.908 +    (nsIDOMNode::DOCUMENT_POSITION_PRECEDING |
   1.909 +     nsIDOMNode::DOCUMENT_POSITION_CONTAINS) :
   1.910 +    (nsIDOMNode::DOCUMENT_POSITION_FOLLOWING |
   1.911 +     nsIDOMNode::DOCUMENT_POSITION_CONTAINED_BY);    
   1.912 +}
   1.913 +
   1.914 +bool
   1.915 +nsINode::IsEqualNode(nsINode* aOther)
   1.916 +{
   1.917 +  if (!aOther) {
   1.918 +    return false;
   1.919 +  }
   1.920 +
   1.921 +  nsAutoString string1, string2;
   1.922 +
   1.923 +  nsINode* node1 = this;
   1.924 +  nsINode* node2 = aOther;
   1.925 +  do {
   1.926 +    uint16_t nodeType = node1->NodeType();
   1.927 +    if (nodeType != node2->NodeType()) {
   1.928 +      return false;
   1.929 +    }
   1.930 +
   1.931 +    nsINodeInfo* nodeInfo1 = node1->mNodeInfo;
   1.932 +    nsINodeInfo* nodeInfo2 = node2->mNodeInfo;
   1.933 +    if (!nodeInfo1->Equals(nodeInfo2) ||
   1.934 +        nodeInfo1->GetExtraName() != nodeInfo2->GetExtraName()) {
   1.935 +      return false;
   1.936 +    }
   1.937 +
   1.938 +    switch(nodeType) {
   1.939 +      case nsIDOMNode::ELEMENT_NODE:
   1.940 +      {
   1.941 +        // Both are elements (we checked that their nodeinfos are equal). Do the
   1.942 +        // check on attributes.
   1.943 +        Element* element1 = node1->AsElement();
   1.944 +        Element* element2 = node2->AsElement();
   1.945 +        uint32_t attrCount = element1->GetAttrCount();
   1.946 +        if (attrCount != element2->GetAttrCount()) {
   1.947 +          return false;
   1.948 +        }
   1.949 +
   1.950 +        // Iterate over attributes.
   1.951 +        for (uint32_t i = 0; i < attrCount; ++i) {
   1.952 +          const nsAttrName* attrName = element1->GetAttrNameAt(i);
   1.953 +#ifdef DEBUG
   1.954 +          bool hasAttr =
   1.955 +#endif
   1.956 +          element1->GetAttr(attrName->NamespaceID(), attrName->LocalName(),
   1.957 +                            string1);
   1.958 +          NS_ASSERTION(hasAttr, "Why don't we have an attr?");
   1.959 +    
   1.960 +          if (!element2->AttrValueIs(attrName->NamespaceID(),
   1.961 +                                     attrName->LocalName(),
   1.962 +                                     string1,
   1.963 +                                     eCaseMatters)) {
   1.964 +            return false;
   1.965 +          }
   1.966 +        }
   1.967 +        break;
   1.968 +      }
   1.969 +      case nsIDOMNode::TEXT_NODE:
   1.970 +      case nsIDOMNode::COMMENT_NODE:
   1.971 +      case nsIDOMNode::CDATA_SECTION_NODE:
   1.972 +      case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
   1.973 +      {
   1.974 +        string1.Truncate();
   1.975 +        static_cast<nsIContent*>(node1)->AppendTextTo(string1);
   1.976 +        string2.Truncate();
   1.977 +        static_cast<nsIContent*>(node2)->AppendTextTo(string2);
   1.978 +
   1.979 +        if (!string1.Equals(string2)) {
   1.980 +          return false;
   1.981 +        }
   1.982 +
   1.983 +        break;
   1.984 +      }
   1.985 +      case nsIDOMNode::DOCUMENT_NODE:
   1.986 +      case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
   1.987 +        break;
   1.988 +      case nsIDOMNode::ATTRIBUTE_NODE:
   1.989 +      {
   1.990 +        NS_ASSERTION(node1 == this && node2 == aOther,
   1.991 +                     "Did we come upon an attribute node while walking a "
   1.992 +                     "subtree?");
   1.993 +        node1->GetNodeValue(string1);
   1.994 +        node2->GetNodeValue(string2);
   1.995 +        
   1.996 +        // Returning here as to not bother walking subtree. And there is no
   1.997 +        // risk that we're half way through walking some other subtree since
   1.998 +        // attribute nodes doesn't appear in subtrees.
   1.999 +        return string1.Equals(string2);
  1.1000 +      }
  1.1001 +      case nsIDOMNode::DOCUMENT_TYPE_NODE:
  1.1002 +      {
  1.1003 +        nsCOMPtr<nsIDOMDocumentType> docType1 = do_QueryInterface(node1);
  1.1004 +        nsCOMPtr<nsIDOMDocumentType> docType2 = do_QueryInterface(node2);
  1.1005 +    
  1.1006 +        NS_ASSERTION(docType1 && docType2, "Why don't we have a document type node?");
  1.1007 +
  1.1008 +        // Public ID
  1.1009 +        docType1->GetPublicId(string1);
  1.1010 +        docType2->GetPublicId(string2);
  1.1011 +        if (!string1.Equals(string2)) {
  1.1012 +          return false;
  1.1013 +        }
  1.1014 +    
  1.1015 +        // System ID
  1.1016 +        docType1->GetSystemId(string1);
  1.1017 +        docType2->GetSystemId(string2);
  1.1018 +        if (!string1.Equals(string2)) {
  1.1019 +          return false;
  1.1020 +        }
  1.1021 +    
  1.1022 +        // Internal subset
  1.1023 +        docType1->GetInternalSubset(string1);
  1.1024 +        docType2->GetInternalSubset(string2);
  1.1025 +        if (!string1.Equals(string2)) {
  1.1026 +          return false;
  1.1027 +        }
  1.1028 +
  1.1029 +        break;
  1.1030 +      }
  1.1031 +      default:
  1.1032 +        NS_ABORT_IF_FALSE(false, "Unknown node type");
  1.1033 +    }
  1.1034 +
  1.1035 +    nsINode* nextNode = node1->GetFirstChild();
  1.1036 +    if (nextNode) {
  1.1037 +      node1 = nextNode;
  1.1038 +      node2 = node2->GetFirstChild();
  1.1039 +    }
  1.1040 +    else {
  1.1041 +      if (node2->GetFirstChild()) {
  1.1042 +        // node2 has a firstChild, but node1 doesn't
  1.1043 +        return false;
  1.1044 +      }
  1.1045 +
  1.1046 +      // Find next sibling, possibly walking parent chain.
  1.1047 +      while (1) {
  1.1048 +        if (node1 == this) {
  1.1049 +          NS_ASSERTION(node2 == aOther, "Should have reached the start node "
  1.1050 +                                        "for both trees at the same time");
  1.1051 +          return true;
  1.1052 +        }
  1.1053 +
  1.1054 +        nextNode = node1->GetNextSibling();
  1.1055 +        if (nextNode) {
  1.1056 +          node1 = nextNode;
  1.1057 +          node2 = node2->GetNextSibling();
  1.1058 +          break;
  1.1059 +        }
  1.1060 +
  1.1061 +        if (node2->GetNextSibling()) {
  1.1062 +          // node2 has a nextSibling, but node1 doesn't
  1.1063 +          return false;
  1.1064 +        }
  1.1065 +        
  1.1066 +        node1 = node1->GetParentNode();
  1.1067 +        node2 = node2->GetParentNode();
  1.1068 +        NS_ASSERTION(node1 && node2, "no parent while walking subtree");
  1.1069 +      }
  1.1070 +    }
  1.1071 +  } while(node2);
  1.1072 +
  1.1073 +  return false;
  1.1074 +}
  1.1075 +
  1.1076 +void
  1.1077 +nsINode::LookupNamespaceURI(const nsAString& aNamespacePrefix,
  1.1078 +                            nsAString& aNamespaceURI)
  1.1079 +{
  1.1080 +  Element *element = GetNameSpaceElement();
  1.1081 +  if (!element ||
  1.1082 +      NS_FAILED(element->LookupNamespaceURIInternal(aNamespacePrefix,
  1.1083 +                                                    aNamespaceURI))) {
  1.1084 +    SetDOMStringToNull(aNamespaceURI);
  1.1085 +  }
  1.1086 +}
  1.1087 +
  1.1088 +NS_IMPL_DOMTARGET_DEFAULTS(nsINode)
  1.1089 +
  1.1090 +NS_IMETHODIMP
  1.1091 +nsINode::AddEventListener(const nsAString& aType,
  1.1092 +                          nsIDOMEventListener *aListener,
  1.1093 +                          bool aUseCapture,
  1.1094 +                          bool aWantsUntrusted,
  1.1095 +                          uint8_t aOptionalArgc)
  1.1096 +{
  1.1097 +  NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1,
  1.1098 +               "Won't check if this is chrome, you want to set "
  1.1099 +               "aWantsUntrusted to false or make the aWantsUntrusted "
  1.1100 +               "explicit by making aOptionalArgc non-zero.");
  1.1101 +
  1.1102 +  if (!aWantsUntrusted &&
  1.1103 +      (aOptionalArgc < 2 &&
  1.1104 +       !nsContentUtils::IsChromeDoc(OwnerDoc()))) {
  1.1105 +    aWantsUntrusted = true;
  1.1106 +  }
  1.1107 +
  1.1108 +  EventListenerManager* listener_manager = GetOrCreateListenerManager();
  1.1109 +  NS_ENSURE_STATE(listener_manager);
  1.1110 +  listener_manager->AddEventListener(aType, aListener, aUseCapture,
  1.1111 +                                     aWantsUntrusted);
  1.1112 +  return NS_OK;
  1.1113 +}
  1.1114 +
  1.1115 +void
  1.1116 +nsINode::AddEventListener(const nsAString& aType,
  1.1117 +                          EventListener* aListener,
  1.1118 +                          bool aUseCapture,
  1.1119 +                          const Nullable<bool>& aWantsUntrusted,
  1.1120 +                          ErrorResult& aRv)
  1.1121 +{
  1.1122 +  bool wantsUntrusted;
  1.1123 +  if (aWantsUntrusted.IsNull()) {
  1.1124 +    wantsUntrusted = !nsContentUtils::IsChromeDoc(OwnerDoc());
  1.1125 +  } else {
  1.1126 +    wantsUntrusted = aWantsUntrusted.Value();
  1.1127 +  }
  1.1128 +
  1.1129 +  EventListenerManager* listener_manager = GetOrCreateListenerManager();
  1.1130 +  if (!listener_manager) {
  1.1131 +    aRv.Throw(NS_ERROR_UNEXPECTED);
  1.1132 +    return;
  1.1133 +  }
  1.1134 +  listener_manager->AddEventListener(aType, aListener, aUseCapture,
  1.1135 +                                     wantsUntrusted);
  1.1136 +}
  1.1137 +
  1.1138 +NS_IMETHODIMP
  1.1139 +nsINode::AddSystemEventListener(const nsAString& aType,
  1.1140 +                                nsIDOMEventListener *aListener,
  1.1141 +                                bool aUseCapture,
  1.1142 +                                bool aWantsUntrusted,
  1.1143 +                                uint8_t aOptionalArgc)
  1.1144 +{
  1.1145 +  NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1,
  1.1146 +               "Won't check if this is chrome, you want to set "
  1.1147 +               "aWantsUntrusted to false or make the aWantsUntrusted "
  1.1148 +               "explicit by making aOptionalArgc non-zero.");
  1.1149 +
  1.1150 +  if (!aWantsUntrusted &&
  1.1151 +      (aOptionalArgc < 2 &&
  1.1152 +       !nsContentUtils::IsChromeDoc(OwnerDoc()))) {
  1.1153 +    aWantsUntrusted = true;
  1.1154 +  }
  1.1155 +
  1.1156 +  return NS_AddSystemEventListener(this, aType, aListener, aUseCapture,
  1.1157 +                                   aWantsUntrusted);
  1.1158 +}
  1.1159 +
  1.1160 +NS_IMETHODIMP
  1.1161 +nsINode::RemoveEventListener(const nsAString& aType,
  1.1162 +                             nsIDOMEventListener* aListener,
  1.1163 +                             bool aUseCapture)
  1.1164 +{
  1.1165 +  EventListenerManager* elm = GetExistingListenerManager();
  1.1166 +  if (elm) {
  1.1167 +    elm->RemoveEventListener(aType, aListener, aUseCapture);
  1.1168 +  }
  1.1169 +  return NS_OK;
  1.1170 +}
  1.1171 +
  1.1172 +NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(nsINode)
  1.1173 +
  1.1174 +nsresult
  1.1175 +nsINode::PreHandleEvent(EventChainPreVisitor& aVisitor)
  1.1176 +{
  1.1177 +  // This is only here so that we can use the NS_DECL_NSIDOMTARGET macro
  1.1178 +  NS_ABORT();
  1.1179 +  return NS_ERROR_NOT_IMPLEMENTED;
  1.1180 +}
  1.1181 +
  1.1182 +void
  1.1183 +nsINode::GetBoxQuads(const BoxQuadOptions& aOptions,
  1.1184 +                     nsTArray<nsRefPtr<DOMQuad> >& aResult,
  1.1185 +                     mozilla::ErrorResult& aRv)
  1.1186 +{
  1.1187 +  mozilla::GetBoxQuads(this, aOptions, aResult, aRv);
  1.1188 +}
  1.1189 +
  1.1190 +already_AddRefed<DOMQuad>
  1.1191 +nsINode::ConvertQuadFromNode(DOMQuad& aQuad,
  1.1192 +                             const GeometryNode& aFrom,
  1.1193 +                             const ConvertCoordinateOptions& aOptions,
  1.1194 +                             ErrorResult& aRv)
  1.1195 +{
  1.1196 +  return mozilla::ConvertQuadFromNode(this, aQuad, aFrom, aOptions, aRv);
  1.1197 +}
  1.1198 +
  1.1199 +already_AddRefed<DOMQuad>
  1.1200 +nsINode::ConvertRectFromNode(DOMRectReadOnly& aRect,
  1.1201 +                             const GeometryNode& aFrom,
  1.1202 +                             const ConvertCoordinateOptions& aOptions,
  1.1203 +                             ErrorResult& aRv)
  1.1204 +{
  1.1205 +  return mozilla::ConvertRectFromNode(this, aRect, aFrom, aOptions, aRv);
  1.1206 +}
  1.1207 +
  1.1208 +already_AddRefed<DOMPoint>
  1.1209 +nsINode::ConvertPointFromNode(const DOMPointInit& aPoint,
  1.1210 +                              const GeometryNode& aFrom,
  1.1211 +                              const ConvertCoordinateOptions& aOptions,
  1.1212 +                              ErrorResult& aRv)
  1.1213 +{
  1.1214 +  return mozilla::ConvertPointFromNode(this, aPoint, aFrom, aOptions, aRv);
  1.1215 +}
  1.1216 +
  1.1217 +nsresult
  1.1218 +nsINode::DispatchEvent(nsIDOMEvent *aEvent, bool* aRetVal)
  1.1219 +{
  1.1220 +  // XXX sXBL/XBL2 issue -- do we really want the owner here?  What
  1.1221 +  // if that's the XBL document?  Would we want its presshell?  Or what?
  1.1222 +  nsCOMPtr<nsIDocument> document = OwnerDoc();
  1.1223 +
  1.1224 +  // Do nothing if the element does not belong to a document
  1.1225 +  if (!document) {
  1.1226 +    *aRetVal = true;
  1.1227 +    return NS_OK;
  1.1228 +  }
  1.1229 +
  1.1230 +  // Obtain a presentation shell
  1.1231 +  nsIPresShell *shell = document->GetShell();
  1.1232 +  nsRefPtr<nsPresContext> context;
  1.1233 +  if (shell) {
  1.1234 +    context = shell->GetPresContext();
  1.1235 +  }
  1.1236 +
  1.1237 +  nsEventStatus status = nsEventStatus_eIgnore;
  1.1238 +  nsresult rv =
  1.1239 +    EventDispatcher::DispatchDOMEvent(this, nullptr, aEvent, context, &status);
  1.1240 +  *aRetVal = (status != nsEventStatus_eConsumeNoDefault);
  1.1241 +  return rv;
  1.1242 +}
  1.1243 +
  1.1244 +nsresult
  1.1245 +nsINode::PostHandleEvent(EventChainPostVisitor& /*aVisitor*/)
  1.1246 +{
  1.1247 +  return NS_OK;
  1.1248 +}
  1.1249 +
  1.1250 +nsresult
  1.1251 +nsINode::DispatchDOMEvent(WidgetEvent* aEvent,
  1.1252 +                          nsIDOMEvent* aDOMEvent,
  1.1253 +                          nsPresContext* aPresContext,
  1.1254 +                          nsEventStatus* aEventStatus)
  1.1255 +{
  1.1256 +  return EventDispatcher::DispatchDOMEvent(this, aEvent, aDOMEvent,
  1.1257 +                                           aPresContext, aEventStatus);
  1.1258 +}
  1.1259 +
  1.1260 +EventListenerManager*
  1.1261 +nsINode::GetOrCreateListenerManager()
  1.1262 +{
  1.1263 +  return nsContentUtils::GetListenerManagerForNode(this);
  1.1264 +}
  1.1265 +
  1.1266 +EventListenerManager*
  1.1267 +nsINode::GetExistingListenerManager() const
  1.1268 +{
  1.1269 +  return nsContentUtils::GetExistingListenerManagerForNode(this);
  1.1270 +}
  1.1271 +
  1.1272 +nsIScriptContext*
  1.1273 +nsINode::GetContextForEventHandlers(nsresult* aRv)
  1.1274 +{
  1.1275 +  return nsContentUtils::GetContextForEventHandlers(this, aRv);
  1.1276 +}
  1.1277 +
  1.1278 +nsIDOMWindow*
  1.1279 +nsINode::GetOwnerGlobal()
  1.1280 +{
  1.1281 +  bool dummy;
  1.1282 +  return nsPIDOMWindow::GetOuterFromCurrentInner(
  1.1283 +    static_cast<nsGlobalWindow*>(OwnerDoc()->GetScriptHandlingObject(dummy)));
  1.1284 +}
  1.1285 +
  1.1286 +bool
  1.1287 +nsINode::UnoptimizableCCNode() const
  1.1288 +{
  1.1289 +  const uintptr_t problematicFlags = (NODE_IS_ANONYMOUS_ROOT |
  1.1290 +                                      NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE |
  1.1291 +                                      NODE_IS_NATIVE_ANONYMOUS_ROOT |
  1.1292 +                                      NODE_MAY_BE_IN_BINDING_MNGR |
  1.1293 +                                      NODE_IS_IN_SHADOW_TREE);
  1.1294 +  return HasFlag(problematicFlags) ||
  1.1295 +         NodeType() == nsIDOMNode::ATTRIBUTE_NODE ||
  1.1296 +         // For strange cases like xbl:content/xbl:children
  1.1297 +         (IsElement() &&
  1.1298 +          AsElement()->IsInNamespace(kNameSpaceID_XBL));
  1.1299 +}
  1.1300 +
  1.1301 +/* static */
  1.1302 +bool
  1.1303 +nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb)
  1.1304 +{
  1.1305 +  if (MOZ_LIKELY(!cb.WantAllTraces())) {
  1.1306 +    nsIDocument *currentDoc = tmp->GetCurrentDoc();
  1.1307 +    if (currentDoc &&
  1.1308 +        nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
  1.1309 +      return false;
  1.1310 +    }
  1.1311 +
  1.1312 +    if (nsCCUncollectableMarker::sGeneration) {
  1.1313 +      // If we're black no need to traverse.
  1.1314 +      if (tmp->IsBlack() || tmp->InCCBlackTree()) {
  1.1315 +        return false;
  1.1316 +      }
  1.1317 +
  1.1318 +      if (!tmp->UnoptimizableCCNode()) {
  1.1319 +        // If we're in a black document, return early.
  1.1320 +        if ((currentDoc && currentDoc->IsBlack())) {
  1.1321 +          return false;
  1.1322 +        }
  1.1323 +        // If we're not in anonymous content and we have a black parent,
  1.1324 +        // return early.
  1.1325 +        nsIContent* parent = tmp->GetParent();
  1.1326 +        if (parent && !parent->UnoptimizableCCNode() && parent->IsBlack()) {
  1.1327 +          NS_ABORT_IF_FALSE(parent->IndexOf(tmp) >= 0, "Parent doesn't own us?");
  1.1328 +          return false;
  1.1329 +        }
  1.1330 +      }
  1.1331 +    }
  1.1332 +  }
  1.1333 +
  1.1334 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfo)
  1.1335 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetParent())
  1.1336 +
  1.1337 +  nsSlots *slots = tmp->GetExistingSlots();
  1.1338 +  if (slots) {
  1.1339 +    slots->Traverse(cb);
  1.1340 +  }
  1.1341 +
  1.1342 +  if (tmp->HasProperties()) {
  1.1343 +    nsNodeUtils::TraverseUserData(tmp, cb);
  1.1344 +    nsCOMArray<nsISupports>* objects =
  1.1345 +      static_cast<nsCOMArray<nsISupports>*>(tmp->GetProperty(nsGkAtoms::keepobjectsalive));
  1.1346 +    if (objects) {
  1.1347 +      for (int32_t i = 0; i < objects->Count(); ++i) {
  1.1348 +         cb.NoteXPCOMChild(objects->ObjectAt(i));
  1.1349 +      }
  1.1350 +    }
  1.1351 +  }
  1.1352 +
  1.1353 +  if (tmp->NodeType() != nsIDOMNode::DOCUMENT_NODE &&
  1.1354 +      tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) {
  1.1355 +    nsContentUtils::TraverseListenerManager(tmp, cb);
  1.1356 +  }
  1.1357 +
  1.1358 +  return true;
  1.1359 +}
  1.1360 +
  1.1361 +/* static */
  1.1362 +void
  1.1363 +nsINode::Unlink(nsINode* tmp)
  1.1364 +{
  1.1365 +  tmp->ReleaseWrapper(tmp);
  1.1366 +
  1.1367 +  nsSlots *slots = tmp->GetExistingSlots();
  1.1368 +  if (slots) {
  1.1369 +    slots->Unlink();
  1.1370 +  }
  1.1371 +
  1.1372 +  if (tmp->NodeType() != nsIDOMNode::DOCUMENT_NODE &&
  1.1373 +      tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) {
  1.1374 +    nsContentUtils::RemoveListenerManager(tmp);
  1.1375 +    tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
  1.1376 +  }
  1.1377 +
  1.1378 +  if (tmp->HasProperties()) {
  1.1379 +    nsNodeUtils::UnlinkUserData(tmp);
  1.1380 +    tmp->DeleteProperty(nsGkAtoms::keepobjectsalive);
  1.1381 +  }
  1.1382 +}
  1.1383 +
  1.1384 +static void
  1.1385 +ReleaseURI(void*, /* aObject*/
  1.1386 +           nsIAtom*, /* aPropertyName */
  1.1387 +           void* aPropertyValue,
  1.1388 +           void* /* aData */)
  1.1389 +{
  1.1390 +  nsIURI* uri = static_cast<nsIURI*>(aPropertyValue);
  1.1391 +  NS_RELEASE(uri);
  1.1392 +}
  1.1393 +
  1.1394 +nsresult
  1.1395 +nsINode::SetExplicitBaseURI(nsIURI* aURI)
  1.1396 +{
  1.1397 +  nsresult rv = SetProperty(nsGkAtoms::baseURIProperty, aURI, ReleaseURI);
  1.1398 +  if (NS_SUCCEEDED(rv)) {
  1.1399 +    SetHasExplicitBaseURI();
  1.1400 +    NS_ADDREF(aURI);
  1.1401 +  }
  1.1402 +  return rv;
  1.1403 +}
  1.1404 +
  1.1405 +static nsresult
  1.1406 +AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode)
  1.1407 +{
  1.1408 +  NS_ASSERTION(!aNode->GetParentNode(),
  1.1409 +               "Should have removed from parent already");
  1.1410 +
  1.1411 +  nsIDocument *doc = aParent->OwnerDoc();
  1.1412 +
  1.1413 +  nsresult rv;
  1.1414 +  nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc, &rv);
  1.1415 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1416 +
  1.1417 +  nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode, &rv);
  1.1418 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1419 +
  1.1420 +  nsCOMPtr<nsIDOMNode> adoptedNode;
  1.1421 +  rv = domDoc->AdoptNode(node, getter_AddRefs(adoptedNode));
  1.1422 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1423 +
  1.1424 +  NS_ASSERTION(aParent->OwnerDoc() == doc,
  1.1425 +               "ownerDoc chainged while adopting");
  1.1426 +  NS_ASSERTION(adoptedNode == node, "Uh, adopt node changed nodes?");
  1.1427 +  NS_ASSERTION(aParent->OwnerDoc() == aNode->OwnerDoc(),
  1.1428 +               "ownerDocument changed again after adopting!");
  1.1429 +
  1.1430 +  return NS_OK;
  1.1431 +}
  1.1432 +
  1.1433 +static nsresult
  1.1434 +CheckForOutdatedParent(nsINode* aParent, nsINode* aNode)
  1.1435 +{
  1.1436 +  if (JSObject* existingObjUnrooted = aNode->GetWrapper()) {
  1.1437 +    AutoJSContext cx;
  1.1438 +    JS::Rooted<JSObject*> existingObj(cx, existingObjUnrooted);
  1.1439 +    nsIGlobalObject* global = aParent->OwnerDoc()->GetScopeObject();
  1.1440 +    MOZ_ASSERT(global);
  1.1441 +
  1.1442 +    if (js::GetGlobalForObjectCrossCompartment(existingObj) !=
  1.1443 +        global->GetGlobalJSObject()) {
  1.1444 +      JSAutoCompartment ac(cx, existingObj);
  1.1445 +      nsresult rv = ReparentWrapper(cx, existingObj);
  1.1446 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1447 +    }
  1.1448 +  }
  1.1449 +
  1.1450 +  return NS_OK;
  1.1451 +}
  1.1452 +
  1.1453 +nsresult
  1.1454 +nsINode::doInsertChildAt(nsIContent* aKid, uint32_t aIndex,
  1.1455 +                         bool aNotify, nsAttrAndChildArray& aChildArray)
  1.1456 +{
  1.1457 +  NS_PRECONDITION(!aKid->GetParentNode(),
  1.1458 +                  "Inserting node that already has parent");
  1.1459 +  nsresult rv;
  1.1460 +
  1.1461 +  // The id-handling code, and in the future possibly other code, need to
  1.1462 +  // react to unexpected attribute changes.
  1.1463 +  nsMutationGuard::DidMutate();
  1.1464 +
  1.1465 +  // Do this before checking the child-count since this could cause mutations
  1.1466 +  nsIDocument* doc = GetCurrentDoc();
  1.1467 +  mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, aNotify);
  1.1468 +
  1.1469 +  if (OwnerDoc() != aKid->OwnerDoc()) {
  1.1470 +    rv = AdoptNodeIntoOwnerDoc(this, aKid);
  1.1471 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1472 +  } else if (OwnerDoc()->DidDocumentOpen()) {
  1.1473 +    rv = CheckForOutdatedParent(this, aKid);
  1.1474 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1475 +  }
  1.1476 +
  1.1477 +  uint32_t childCount = aChildArray.ChildCount();
  1.1478 +  NS_ENSURE_TRUE(aIndex <= childCount, NS_ERROR_ILLEGAL_VALUE);
  1.1479 +  bool isAppend = (aIndex == childCount);
  1.1480 +
  1.1481 +  rv = aChildArray.InsertChildAt(aKid, aIndex);
  1.1482 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1483 +  if (aIndex == 0) {
  1.1484 +    mFirstChild = aKid;
  1.1485 +  }
  1.1486 +
  1.1487 +  nsIContent* parent =
  1.1488 +    IsNodeOfType(eDOCUMENT) ? nullptr : static_cast<nsIContent*>(this);
  1.1489 +
  1.1490 +  rv = aKid->BindToTree(doc, parent,
  1.1491 +                        parent ? parent->GetBindingParent() : nullptr,
  1.1492 +                        true);
  1.1493 +  if (NS_FAILED(rv)) {
  1.1494 +    if (GetFirstChild() == aKid) {
  1.1495 +      mFirstChild = aKid->GetNextSibling();
  1.1496 +    }
  1.1497 +    aChildArray.RemoveChildAt(aIndex);
  1.1498 +    aKid->UnbindFromTree();
  1.1499 +    return rv;
  1.1500 +  }
  1.1501 +
  1.1502 +  NS_ASSERTION(aKid->GetParentNode() == this,
  1.1503 +               "Did we run script inappropriately?");
  1.1504 +
  1.1505 +  if (aNotify) {
  1.1506 +    // Note that we always want to call ContentInserted when things are added
  1.1507 +    // as kids to documents
  1.1508 +    if (parent && isAppend) {
  1.1509 +      nsNodeUtils::ContentAppended(parent, aKid, aIndex);
  1.1510 +    } else {
  1.1511 +      nsNodeUtils::ContentInserted(this, aKid, aIndex);
  1.1512 +    }
  1.1513 +
  1.1514 +    if (nsContentUtils::HasMutationListeners(aKid,
  1.1515 +          NS_EVENT_BITS_MUTATION_NODEINSERTED, this)) {
  1.1516 +      InternalMutationEvent mutation(true, NS_MUTATION_NODEINSERTED);
  1.1517 +      mutation.mRelatedNode = do_QueryInterface(this);
  1.1518 +
  1.1519 +      mozAutoSubtreeModified subtree(OwnerDoc(), this);
  1.1520 +      (new AsyncEventDispatcher(aKid, mutation))->RunDOMEventWhenSafe();
  1.1521 +    }
  1.1522 +  }
  1.1523 +
  1.1524 +  return NS_OK;
  1.1525 +}
  1.1526 +
  1.1527 +Element*
  1.1528 +nsINode::GetPreviousElementSibling() const
  1.1529 +{
  1.1530 +  nsIContent* previousSibling = GetPreviousSibling();
  1.1531 +  while (previousSibling) {
  1.1532 +    if (previousSibling->IsElement()) {
  1.1533 +      return previousSibling->AsElement();
  1.1534 +    }
  1.1535 +    previousSibling = previousSibling->GetPreviousSibling();
  1.1536 +  }
  1.1537 +
  1.1538 +  return nullptr;
  1.1539 +}
  1.1540 +
  1.1541 +Element*
  1.1542 +nsINode::GetNextElementSibling() const
  1.1543 +{
  1.1544 +  nsIContent* nextSibling = GetNextSibling();
  1.1545 +  while (nextSibling) {
  1.1546 +    if (nextSibling->IsElement()) {
  1.1547 +      return nextSibling->AsElement();
  1.1548 +    }
  1.1549 +    nextSibling = nextSibling->GetNextSibling();
  1.1550 +  }
  1.1551 +
  1.1552 +  return nullptr;
  1.1553 +}
  1.1554 +
  1.1555 +void
  1.1556 +nsINode::Remove()
  1.1557 +{
  1.1558 +  nsCOMPtr<nsINode> parent = GetParentNode();
  1.1559 +  if (!parent) {
  1.1560 +    return;
  1.1561 +  }
  1.1562 +  int32_t index = parent->IndexOf(this);
  1.1563 +  if (index < 0) {
  1.1564 +    NS_WARNING("Ignoring call to nsINode::Remove on anonymous child.");
  1.1565 +    return;
  1.1566 +  }
  1.1567 +  parent->RemoveChildAt(uint32_t(index), true);
  1.1568 +}
  1.1569 +
  1.1570 +Element*
  1.1571 +nsINode::GetFirstElementChild() const
  1.1572 +{
  1.1573 +  for (nsIContent* child = GetFirstChild();
  1.1574 +       child;
  1.1575 +       child = child->GetNextSibling()) {
  1.1576 +    if (child->IsElement()) {
  1.1577 +      return child->AsElement();
  1.1578 +    }
  1.1579 +  }
  1.1580 +
  1.1581 +  return nullptr;
  1.1582 +}
  1.1583 +
  1.1584 +Element*
  1.1585 +nsINode::GetLastElementChild() const
  1.1586 +{
  1.1587 +  for (nsIContent* child = GetLastChild();
  1.1588 +       child;
  1.1589 +       child = child->GetPreviousSibling()) {
  1.1590 +    if (child->IsElement()) {
  1.1591 +      return child->AsElement();
  1.1592 +    }
  1.1593 +  }
  1.1594 +
  1.1595 +  return nullptr;
  1.1596 +}
  1.1597 +
  1.1598 +void
  1.1599 +nsINode::doRemoveChildAt(uint32_t aIndex, bool aNotify,
  1.1600 +                         nsIContent* aKid, nsAttrAndChildArray& aChildArray)
  1.1601 +{
  1.1602 +  NS_PRECONDITION(aKid && aKid->GetParentNode() == this &&
  1.1603 +                  aKid == GetChildAt(aIndex) &&
  1.1604 +                  IndexOf(aKid) == (int32_t)aIndex, "Bogus aKid");
  1.1605 +
  1.1606 +  nsMutationGuard::DidMutate();
  1.1607 +
  1.1608 +  nsIDocument* doc = GetCurrentDoc();
  1.1609 +
  1.1610 +  mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, aNotify);
  1.1611 +
  1.1612 +  nsIContent* previousSibling = aKid->GetPreviousSibling();
  1.1613 +
  1.1614 +  if (GetFirstChild() == aKid) {
  1.1615 +    mFirstChild = aKid->GetNextSibling();
  1.1616 +  }
  1.1617 +
  1.1618 +  aChildArray.RemoveChildAt(aIndex);
  1.1619 +
  1.1620 +  if (aNotify) {
  1.1621 +    nsNodeUtils::ContentRemoved(this, aKid, aIndex, previousSibling);
  1.1622 +  }
  1.1623 +
  1.1624 +  aKid->UnbindFromTree();
  1.1625 +}
  1.1626 +
  1.1627 +// When replacing, aRefChild is the content being replaced; when
  1.1628 +// inserting it's the content before which we're inserting.  In the
  1.1629 +// latter case it may be null.
  1.1630 +static
  1.1631 +bool IsAllowedAsChild(nsIContent* aNewChild, nsINode* aParent,
  1.1632 +                      bool aIsReplace, nsINode* aRefChild)
  1.1633 +{
  1.1634 +  MOZ_ASSERT(aNewChild, "Must have new child");
  1.1635 +  MOZ_ASSERT_IF(aIsReplace, aRefChild);
  1.1636 +  MOZ_ASSERT(aParent);
  1.1637 +  MOZ_ASSERT(aParent->IsNodeOfType(nsINode::eDOCUMENT) ||
  1.1638 +             aParent->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT) ||
  1.1639 +             aParent->IsElement(),
  1.1640 +             "Nodes that are not documents, document fragments or elements "
  1.1641 +             "can't be parents!");
  1.1642 +
  1.1643 +  // A common case is that aNewChild has no kids, in which case
  1.1644 +  // aParent can't be a descendant of aNewChild unless they're
  1.1645 +  // actually equal to each other.  Fast-path that case, since aParent
  1.1646 +  // could be pretty deep in the DOM tree.
  1.1647 +  if (aNewChild == aParent ||
  1.1648 +      ((aNewChild->GetFirstChild() ||
  1.1649 +        // HTML template elements and ShadowRoot hosts need
  1.1650 +        // to be checked to ensure that they are not inserted into
  1.1651 +        // the hosted content.
  1.1652 +        aNewChild->Tag() == nsGkAtoms::_template ||
  1.1653 +        aNewChild->GetShadowRoot()) &&
  1.1654 +       nsContentUtils::ContentIsHostIncludingDescendantOf(aParent,
  1.1655 +                                                          aNewChild))) {
  1.1656 +    return false;
  1.1657 +  }
  1.1658 +
  1.1659 +  // The allowed child nodes differ for documents and elements
  1.1660 +  switch (aNewChild->NodeType()) {
  1.1661 +  case nsIDOMNode::COMMENT_NODE :
  1.1662 +  case nsIDOMNode::PROCESSING_INSTRUCTION_NODE :
  1.1663 +    // OK in both cases
  1.1664 +    return true;
  1.1665 +  case nsIDOMNode::TEXT_NODE :
  1.1666 +  case nsIDOMNode::CDATA_SECTION_NODE :
  1.1667 +  case nsIDOMNode::ENTITY_REFERENCE_NODE :
  1.1668 +    // Allowed under Elements and DocumentFragments
  1.1669 +    return aParent->NodeType() != nsIDOMNode::DOCUMENT_NODE;
  1.1670 +  case nsIDOMNode::ELEMENT_NODE :
  1.1671 +    {
  1.1672 +      if (!aParent->IsNodeOfType(nsINode::eDOCUMENT)) {
  1.1673 +        // Always ok to have elements under other elements or document fragments
  1.1674 +        return true;
  1.1675 +      }
  1.1676 +
  1.1677 +      nsIDocument* parentDocument = static_cast<nsIDocument*>(aParent);
  1.1678 +      Element* rootElement = parentDocument->GetRootElement();
  1.1679 +      if (rootElement) {
  1.1680 +        // Already have a documentElement, so this is only OK if we're
  1.1681 +        // replacing it.
  1.1682 +        return aIsReplace && rootElement == aRefChild;
  1.1683 +      }
  1.1684 +
  1.1685 +      // We don't have a documentElement yet.  Our one remaining constraint is
  1.1686 +      // that the documentElement must come after the doctype.
  1.1687 +      if (!aRefChild) {
  1.1688 +        // Appending is just fine.
  1.1689 +        return true;
  1.1690 +      }
  1.1691 +
  1.1692 +      nsIContent* docTypeContent = parentDocument->GetDoctype();
  1.1693 +      if (!docTypeContent) {
  1.1694 +        // It's all good.
  1.1695 +        return true;
  1.1696 +      }
  1.1697 +
  1.1698 +      int32_t doctypeIndex = aParent->IndexOf(docTypeContent);
  1.1699 +      int32_t insertIndex = aParent->IndexOf(aRefChild);
  1.1700 +
  1.1701 +      // Now we're OK in the following two cases only:
  1.1702 +      // 1) We're replacing something that's not before the doctype
  1.1703 +      // 2) We're inserting before something that comes after the doctype 
  1.1704 +      return aIsReplace ? (insertIndex >= doctypeIndex) :
  1.1705 +        insertIndex > doctypeIndex;
  1.1706 +    }
  1.1707 +  case nsIDOMNode::DOCUMENT_TYPE_NODE :
  1.1708 +    {
  1.1709 +      if (!aParent->IsNodeOfType(nsINode::eDOCUMENT)) {
  1.1710 +        // doctypes only allowed under documents
  1.1711 +        return false;
  1.1712 +      }
  1.1713 +
  1.1714 +      nsIDocument* parentDocument = static_cast<nsIDocument*>(aParent);
  1.1715 +      nsIContent* docTypeContent = parentDocument->GetDoctype();
  1.1716 +      if (docTypeContent) {
  1.1717 +        // Already have a doctype, so this is only OK if we're replacing it
  1.1718 +        return aIsReplace && docTypeContent == aRefChild;
  1.1719 +      }
  1.1720 +
  1.1721 +      // We don't have a doctype yet.  Our one remaining constraint is
  1.1722 +      // that the doctype must come before the documentElement.
  1.1723 +      Element* rootElement = parentDocument->GetRootElement();
  1.1724 +      if (!rootElement) {
  1.1725 +        // It's all good
  1.1726 +        return true;
  1.1727 +      }
  1.1728 +
  1.1729 +      if (!aRefChild) {
  1.1730 +        // Trying to append a doctype, but have a documentElement
  1.1731 +        return false;
  1.1732 +      }
  1.1733 +
  1.1734 +      int32_t rootIndex = aParent->IndexOf(rootElement);
  1.1735 +      int32_t insertIndex = aParent->IndexOf(aRefChild);
  1.1736 +
  1.1737 +      // Now we're OK if and only if insertIndex <= rootIndex.  Indeed, either
  1.1738 +      // we end up replacing aRefChild or we end up before it.  Either one is
  1.1739 +      // ok as long as aRefChild is not after rootElement.
  1.1740 +      return insertIndex <= rootIndex;
  1.1741 +    }
  1.1742 +  case nsIDOMNode::DOCUMENT_FRAGMENT_NODE :
  1.1743 +    {
  1.1744 +      // Note that for now we only allow nodes inside document fragments if
  1.1745 +      // they're allowed inside elements.  If we ever change this to allow
  1.1746 +      // doctype nodes in document fragments, we'll need to update this code.
  1.1747 +      // Also, there's a version of this code in ReplaceOrInsertBefore.  If you
  1.1748 +      // change this code, change that too.
  1.1749 +      if (!aParent->IsNodeOfType(nsINode::eDOCUMENT)) {
  1.1750 +        // All good here
  1.1751 +        return true;
  1.1752 +      }
  1.1753 +
  1.1754 +      bool sawElement = false;
  1.1755 +      for (nsIContent* child = aNewChild->GetFirstChild();
  1.1756 +           child;
  1.1757 +           child = child->GetNextSibling()) {
  1.1758 +        if (child->IsElement()) {
  1.1759 +          if (sawElement) {
  1.1760 +            // Can't put two elements into a document
  1.1761 +            return false;
  1.1762 +          }
  1.1763 +          sawElement = true;
  1.1764 +        }
  1.1765 +        // If we can put this content at the the right place, we might be ok;
  1.1766 +        // if not, we bail out.
  1.1767 +        if (!IsAllowedAsChild(child, aParent, aIsReplace, aRefChild)) {
  1.1768 +          return false;
  1.1769 +        }
  1.1770 +      }
  1.1771 +
  1.1772 +      // Everything in the fragment checked out ok, so we can stick it in here
  1.1773 +      return true;
  1.1774 +    }
  1.1775 +  default:
  1.1776 +    /*
  1.1777 +     * aNewChild is of invalid type.
  1.1778 +     */
  1.1779 +    break;
  1.1780 +  }
  1.1781 +
  1.1782 +  return false;
  1.1783 +}
  1.1784 +
  1.1785 +nsINode*
  1.1786 +nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
  1.1787 +                               nsINode* aRefChild, ErrorResult& aError)
  1.1788 +{
  1.1789 +  // XXXbz I wish I could assert that nsContentUtils::IsSafeToRunScript() so we
  1.1790 +  // could rely on scriptblockers going out of scope to actually run XBL
  1.1791 +  // teardown, but various crud adds nodes under scriptblockers (e.g. native
  1.1792 +  // anonymous content).  The only good news is those insertions can't trigger
  1.1793 +  // the bad XBL cases.
  1.1794 +  MOZ_ASSERT_IF(aReplace, aRefChild);
  1.1795 +
  1.1796 +  if ((!IsNodeOfType(eDOCUMENT) &&
  1.1797 +       !IsNodeOfType(eDOCUMENT_FRAGMENT) &&
  1.1798 +       !IsElement()) ||
  1.1799 +      !aNewChild->IsNodeOfType(eCONTENT)) {
  1.1800 +    aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  1.1801 +    return nullptr;
  1.1802 +  }
  1.1803 +
  1.1804 +  uint16_t nodeType = aNewChild->NodeType();
  1.1805 +
  1.1806 +  // Before we do anything else, fire all DOMNodeRemoved mutation events
  1.1807 +  // We do this up front as to avoid having to deal with script running
  1.1808 +  // at random places further down.
  1.1809 +  // Scope firing mutation events so that we don't carry any state that
  1.1810 +  // might be stale
  1.1811 +  {
  1.1812 +    // This check happens again further down (though then using IndexOf).
  1.1813 +    // We're only checking this here to avoid firing mutation events when
  1.1814 +    // none should be fired.
  1.1815 +    // It's ok that we do the check twice in the case when firing mutation
  1.1816 +    // events as we need to recheck after running script anyway.
  1.1817 +    if (aRefChild && aRefChild->GetParentNode() != this) {
  1.1818 +      aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
  1.1819 +      return nullptr;
  1.1820 +    }
  1.1821 +
  1.1822 +    // If we're replacing, fire for node-to-be-replaced.
  1.1823 +    // If aRefChild == aNewChild then we'll fire for it in check below
  1.1824 +    if (aReplace && aRefChild != aNewChild) {
  1.1825 +      nsContentUtils::MaybeFireNodeRemoved(aRefChild, this, OwnerDoc());
  1.1826 +    }
  1.1827 +
  1.1828 +    // If the new node already has a parent, fire for removing from old
  1.1829 +    // parent
  1.1830 +    nsINode* oldParent = aNewChild->GetParentNode();
  1.1831 +    if (oldParent) {
  1.1832 +      nsContentUtils::MaybeFireNodeRemoved(aNewChild, oldParent,
  1.1833 +                                           aNewChild->OwnerDoc());
  1.1834 +    }
  1.1835 +
  1.1836 +    // If we're inserting a fragment, fire for all the children of the
  1.1837 +    // fragment
  1.1838 +    if (nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
  1.1839 +      static_cast<FragmentOrElement*>(aNewChild)->FireNodeRemovedForChildren();
  1.1840 +    }
  1.1841 +    // Verify that our aRefChild is still sensible
  1.1842 +    if (aRefChild && aRefChild->GetParentNode() != this) {
  1.1843 +      aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
  1.1844 +      return nullptr;
  1.1845 +    }
  1.1846 +  }
  1.1847 +
  1.1848 +  nsIDocument* doc = OwnerDoc();
  1.1849 +  nsIContent* newContent = static_cast<nsIContent*>(aNewChild);
  1.1850 +  if (newContent->IsRootOfAnonymousSubtree()) {
  1.1851 +    // This is anonymous content.  Don't allow its insertion
  1.1852 +    // anywhere, since it might have UnbindFromTree calls coming
  1.1853 +    // its way.
  1.1854 +    aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  1.1855 +    return nullptr;
  1.1856 +  }
  1.1857 +
  1.1858 +  // Make sure that the inserted node is allowed as a child of its new parent.
  1.1859 +  if (!IsAllowedAsChild(newContent, this, aReplace, aRefChild)) {
  1.1860 +    aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  1.1861 +    return nullptr;
  1.1862 +  }
  1.1863 +
  1.1864 +  // Record the node to insert before, if any
  1.1865 +  nsINode* nodeToInsertBefore;
  1.1866 +  if (aReplace) {
  1.1867 +    nodeToInsertBefore = aRefChild->GetNextSibling();
  1.1868 +  } else {
  1.1869 +    nodeToInsertBefore = aRefChild;
  1.1870 +  }
  1.1871 +  if (nodeToInsertBefore == aNewChild) {
  1.1872 +    // We're going to remove aNewChild from its parent, so use its next sibling
  1.1873 +    // as the node to insert before.
  1.1874 +    nodeToInsertBefore = nodeToInsertBefore->GetNextSibling();
  1.1875 +  }
  1.1876 +
  1.1877 +  Maybe<nsAutoTArray<nsCOMPtr<nsIContent>, 50> > fragChildren;
  1.1878 +
  1.1879 +  // Remove the new child from the old parent if one exists
  1.1880 +  nsCOMPtr<nsINode> oldParent = newContent->GetParentNode();
  1.1881 +  if (oldParent) {
  1.1882 +    int32_t removeIndex = oldParent->IndexOf(newContent);
  1.1883 +    if (removeIndex < 0) {
  1.1884 +      // newContent is anonymous.  We can't deal with this, so just bail
  1.1885 +      NS_ERROR("How come our flags didn't catch this?");
  1.1886 +      aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  1.1887 +      return nullptr;
  1.1888 +    }
  1.1889 +
  1.1890 +    // Hold a strong ref to nodeToInsertBefore across the removal of newContent
  1.1891 +    nsCOMPtr<nsINode> kungFuDeathGrip = nodeToInsertBefore;
  1.1892 +
  1.1893 +    // Removing a child can run script, via XBL destructors.
  1.1894 +    nsMutationGuard guard;
  1.1895 +
  1.1896 +    // Scope for the mutation batch and scriptblocker, so they go away
  1.1897 +    // while kungFuDeathGrip is still alive.
  1.1898 +    {
  1.1899 +      mozAutoDocUpdate batch(newContent->GetCurrentDoc(),
  1.1900 +                             UPDATE_CONTENT_MODEL, true);
  1.1901 +      nsAutoMutationBatch mb(oldParent, true, true);
  1.1902 +      oldParent->RemoveChildAt(removeIndex, true);
  1.1903 +      if (nsAutoMutationBatch::GetCurrentBatch() == &mb) {
  1.1904 +        mb.RemovalDone();
  1.1905 +        mb.SetPrevSibling(oldParent->GetChildAt(removeIndex - 1));
  1.1906 +        mb.SetNextSibling(oldParent->GetChildAt(removeIndex));
  1.1907 +      }
  1.1908 +    }
  1.1909 +
  1.1910 +    // We expect one mutation (the removal) to have happened.
  1.1911 +    if (guard.Mutated(1)) {
  1.1912 +      // XBL destructors, yuck.
  1.1913 +      
  1.1914 +      // Verify that nodeToInsertBefore, if non-null, is still our child.  If
  1.1915 +      // it's not, there's no way we can do this insert sanely; just bail out.
  1.1916 +      if (nodeToInsertBefore && nodeToInsertBefore->GetParent() != this) {
  1.1917 +        aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  1.1918 +        return nullptr;
  1.1919 +      }
  1.1920 +
  1.1921 +      // Verify that newContent has no parent.
  1.1922 +      if (newContent->GetParentNode()) {
  1.1923 +        aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  1.1924 +        return nullptr;
  1.1925 +      }
  1.1926 +
  1.1927 +      // And verify that newContent is still allowed as our child.
  1.1928 +      if (aNewChild == aRefChild) {
  1.1929 +        // We've already removed aRefChild.  So even if we were doing a replace,
  1.1930 +        // now we're doing a simple insert before nodeToInsertBefore.
  1.1931 +        if (!IsAllowedAsChild(newContent, this, false, nodeToInsertBefore)) {
  1.1932 +          aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  1.1933 +          return nullptr;
  1.1934 +        }
  1.1935 +      } else {
  1.1936 +        if ((aRefChild && aRefChild->GetParent() != this) ||
  1.1937 +            !IsAllowedAsChild(newContent, this, aReplace, aRefChild)) {
  1.1938 +          aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  1.1939 +          return nullptr;
  1.1940 +        }
  1.1941 +        // And recompute nodeToInsertBefore, just in case.
  1.1942 +        if (aReplace) {
  1.1943 +          nodeToInsertBefore = aRefChild->GetNextSibling();
  1.1944 +        } else {
  1.1945 +          nodeToInsertBefore = aRefChild;
  1.1946 +        }
  1.1947 +      }
  1.1948 +    }
  1.1949 +  } else if (nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
  1.1950 +    // Make sure to remove all the fragment's kids.  We need to do this before
  1.1951 +    // we start inserting anything, so we will run out XBL destructors and
  1.1952 +    // binding teardown (GOD, I HATE THESE THINGS) before we insert anything
  1.1953 +    // into the DOM.
  1.1954 +    uint32_t count = newContent->GetChildCount();
  1.1955 +
  1.1956 +    fragChildren.construct();
  1.1957 +
  1.1958 +    // Copy the children into a separate array to avoid having to deal with
  1.1959 +    // mutations to the fragment later on here.
  1.1960 +    fragChildren.ref().SetCapacity(count);
  1.1961 +    for (nsIContent* child = newContent->GetFirstChild();
  1.1962 +         child;
  1.1963 +         child = child->GetNextSibling()) {
  1.1964 +      NS_ASSERTION(child->GetCurrentDoc() == nullptr,
  1.1965 +                   "How did we get a child with a current doc?");
  1.1966 +      fragChildren.ref().AppendElement(child);
  1.1967 +    }
  1.1968 +
  1.1969 +    // Hold a strong ref to nodeToInsertBefore across the removals
  1.1970 +    nsCOMPtr<nsINode> kungFuDeathGrip = nodeToInsertBefore;
  1.1971 +
  1.1972 +    nsMutationGuard guard;
  1.1973 +
  1.1974 +    // Scope for the mutation batch and scriptblocker, so they go away
  1.1975 +    // while kungFuDeathGrip is still alive.
  1.1976 +    {
  1.1977 +      mozAutoDocUpdate batch(newContent->GetCurrentDoc(),
  1.1978 +                             UPDATE_CONTENT_MODEL, true);
  1.1979 +      nsAutoMutationBatch mb(newContent, false, true);
  1.1980 +
  1.1981 +      for (uint32_t i = count; i > 0;) {
  1.1982 +        newContent->RemoveChildAt(--i, true);
  1.1983 +      }
  1.1984 +    }
  1.1985 +
  1.1986 +    // We expect |count| removals
  1.1987 +    if (guard.Mutated(count)) {
  1.1988 +      // XBL destructors, yuck.
  1.1989 +      
  1.1990 +      // Verify that nodeToInsertBefore, if non-null, is still our child.  If
  1.1991 +      // it's not, there's no way we can do this insert sanely; just bail out.
  1.1992 +      if (nodeToInsertBefore && nodeToInsertBefore->GetParent() != this) {
  1.1993 +        aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  1.1994 +        return nullptr;
  1.1995 +      }
  1.1996 +
  1.1997 +      // Verify that all the things in fragChildren have no parent.
  1.1998 +      for (uint32_t i = 0; i < count; ++i) {
  1.1999 +        if (fragChildren.ref().ElementAt(i)->GetParentNode()) {
  1.2000 +          aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  1.2001 +          return nullptr;
  1.2002 +        }
  1.2003 +      }
  1.2004 +
  1.2005 +      // Note that unlike the single-element case above, none of our kids can
  1.2006 +      // be aRefChild, so we can always pass through aReplace in the
  1.2007 +      // IsAllowedAsChild checks below and don't have to worry about whether
  1.2008 +      // recomputing nodeToInsertBefore is OK.
  1.2009 +
  1.2010 +      // Verify that our aRefChild is still sensible
  1.2011 +      if (aRefChild && aRefChild->GetParent() != this) {
  1.2012 +        aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  1.2013 +        return nullptr;
  1.2014 +      }
  1.2015 +
  1.2016 +      // Recompute nodeToInsertBefore, just in case.
  1.2017 +      if (aReplace) {
  1.2018 +        nodeToInsertBefore = aRefChild->GetNextSibling();
  1.2019 +      } else {
  1.2020 +        nodeToInsertBefore = aRefChild;
  1.2021 +      }      
  1.2022 +
  1.2023 +      // And verify that newContent is still allowed as our child.  Sadly, we
  1.2024 +      // need to reimplement the relevant part of IsAllowedAsChild() because
  1.2025 +      // now our nodes are in an array and all.  If you change this code,
  1.2026 +      // change the code there.
  1.2027 +      if (IsNodeOfType(nsINode::eDOCUMENT)) {
  1.2028 +        bool sawElement = false;
  1.2029 +        for (uint32_t i = 0; i < count; ++i) {
  1.2030 +          nsIContent* child = fragChildren.ref().ElementAt(i);
  1.2031 +          if (child->IsElement()) {
  1.2032 +            if (sawElement) {
  1.2033 +              // No good
  1.2034 +              aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  1.2035 +              return nullptr;
  1.2036 +            }
  1.2037 +            sawElement = true;
  1.2038 +          }
  1.2039 +          if (!IsAllowedAsChild(child, this, aReplace, aRefChild)) {
  1.2040 +            aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  1.2041 +            return nullptr;
  1.2042 +          }
  1.2043 +        }
  1.2044 +      }
  1.2045 +    }
  1.2046 +  }
  1.2047 +
  1.2048 +  mozAutoDocUpdate batch(GetCurrentDoc(), UPDATE_CONTENT_MODEL, true);
  1.2049 +  nsAutoMutationBatch mb;
  1.2050 +
  1.2051 +  // Figure out which index we want to insert at.  Note that we use
  1.2052 +  // nodeToInsertBefore to determine this, because it's possible that
  1.2053 +  // aRefChild == aNewChild, in which case we just removed it from the
  1.2054 +  // parent list.
  1.2055 +  int32_t insPos;
  1.2056 +  if (nodeToInsertBefore) {
  1.2057 +    insPos = IndexOf(nodeToInsertBefore);
  1.2058 +    if (insPos < 0) {
  1.2059 +      // XXXbz How the heck would _that_ happen, exactly?
  1.2060 +      aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
  1.2061 +      return nullptr;
  1.2062 +    }
  1.2063 +  }
  1.2064 +  else {
  1.2065 +    insPos = GetChildCount();
  1.2066 +  }
  1.2067 +
  1.2068 +  // If we're replacing and we haven't removed aRefChild yet, do so now
  1.2069 +  if (aReplace && aRefChild != aNewChild) {
  1.2070 +    mb.Init(this, true, true);
  1.2071 +
  1.2072 +    // Since aRefChild is never null in the aReplace case, we know that at
  1.2073 +    // this point nodeToInsertBefore is the next sibling of aRefChild.
  1.2074 +    NS_ASSERTION(aRefChild->GetNextSibling() == nodeToInsertBefore,
  1.2075 +                 "Unexpected nodeToInsertBefore");
  1.2076 +
  1.2077 +    // An since nodeToInsertBefore is at index insPos, we want to remove
  1.2078 +    // at the previous index.
  1.2079 +    NS_ASSERTION(insPos >= 1, "insPos too small");
  1.2080 +    RemoveChildAt(insPos-1, true);
  1.2081 +    --insPos;
  1.2082 +  }
  1.2083 +
  1.2084 +  // Move new child over to our document if needed. Do this after removing
  1.2085 +  // it from its parent so that AdoptNode doesn't fire DOMNodeRemoved
  1.2086 +  // DocumentType nodes are the only nodes that can have a null
  1.2087 +  // ownerDocument according to the DOM spec, and we need to allow
  1.2088 +  // inserting them w/o calling AdoptNode().
  1.2089 +  if (doc != newContent->OwnerDoc()) {
  1.2090 +    aError = AdoptNodeIntoOwnerDoc(this, aNewChild);
  1.2091 +    if (aError.Failed()) {
  1.2092 +      return nullptr;
  1.2093 +    }
  1.2094 +  } else if (doc->DidDocumentOpen()) {
  1.2095 +    aError = CheckForOutdatedParent(this, aNewChild);
  1.2096 +    if (aError.Failed()) {
  1.2097 +      return nullptr;
  1.2098 +    }
  1.2099 +  }
  1.2100 +
  1.2101 +  /*
  1.2102 +   * Check if we're inserting a document fragment. If we are, we need
  1.2103 +   * to actually add its children individually (i.e. we don't add the
  1.2104 +   * actual document fragment).
  1.2105 +   */
  1.2106 +  nsINode* result = aReplace ? aRefChild : aNewChild;
  1.2107 +  if (nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
  1.2108 +    if (!aReplace) {
  1.2109 +      mb.Init(this, true, true);
  1.2110 +    }
  1.2111 +    nsAutoMutationBatch* mutationBatch = nsAutoMutationBatch::GetCurrentBatch();
  1.2112 +    if (mutationBatch) {
  1.2113 +      mutationBatch->RemovalDone();
  1.2114 +      mutationBatch->SetPrevSibling(GetChildAt(insPos - 1));
  1.2115 +      mutationBatch->SetNextSibling(GetChildAt(insPos));
  1.2116 +    }
  1.2117 +
  1.2118 +    uint32_t count = fragChildren.ref().Length();
  1.2119 +    if (!count) {
  1.2120 +      return result;
  1.2121 +    }
  1.2122 +
  1.2123 +    bool appending =
  1.2124 +      !IsNodeOfType(eDOCUMENT) && uint32_t(insPos) == GetChildCount();
  1.2125 +    int32_t firstInsPos = insPos;
  1.2126 +    nsIContent* firstInsertedContent = fragChildren.ref().ElementAt(0);
  1.2127 +
  1.2128 +    // Iterate through the fragment's children, and insert them in the new
  1.2129 +    // parent
  1.2130 +    for (uint32_t i = 0; i < count; ++i, ++insPos) {
  1.2131 +      // XXXbz how come no reparenting here?  That seems odd...
  1.2132 +      // Insert the child.
  1.2133 +      aError = InsertChildAt(fragChildren.ref().ElementAt(i), insPos,
  1.2134 +                             !appending);
  1.2135 +      if (aError.Failed()) {
  1.2136 +        // Make sure to notify on any children that we did succeed to insert
  1.2137 +        if (appending && i != 0) {
  1.2138 +          nsNodeUtils::ContentAppended(static_cast<nsIContent*>(this),
  1.2139 +                                       firstInsertedContent,
  1.2140 +                                       firstInsPos);
  1.2141 +        }
  1.2142 +        return nullptr;
  1.2143 +      }
  1.2144 +    }
  1.2145 +
  1.2146 +    if (mutationBatch && !appending) {
  1.2147 +      mutationBatch->NodesAdded();
  1.2148 +    }
  1.2149 +
  1.2150 +    // Notify and fire mutation events when appending
  1.2151 +    if (appending) {
  1.2152 +      nsNodeUtils::ContentAppended(static_cast<nsIContent*>(this),
  1.2153 +                                   firstInsertedContent, firstInsPos);
  1.2154 +      if (mutationBatch) {
  1.2155 +        mutationBatch->NodesAdded();
  1.2156 +      }
  1.2157 +      // Optimize for the case when there are no listeners
  1.2158 +      if (nsContentUtils::
  1.2159 +            HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
  1.2160 +        Element::FireNodeInserted(doc, this, fragChildren.ref());
  1.2161 +      }
  1.2162 +    }
  1.2163 +  }
  1.2164 +  else {
  1.2165 +    // Not inserting a fragment but rather a single node.
  1.2166 +
  1.2167 +    // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=544654
  1.2168 +    //       We need to reparent here for nodes for which the parent of their
  1.2169 +    //       wrapper is not the wrapper for their ownerDocument (XUL elements,
  1.2170 +    //       form controls, ...). Also applies in the fragment code above.
  1.2171 +
  1.2172 +    if (nsAutoMutationBatch::GetCurrentBatch() == &mb) {
  1.2173 +      mb.RemovalDone();
  1.2174 +      mb.SetPrevSibling(GetChildAt(insPos - 1));
  1.2175 +      mb.SetNextSibling(GetChildAt(insPos));
  1.2176 +    }
  1.2177 +    aError = InsertChildAt(newContent, insPos, true);
  1.2178 +    if (aError.Failed()) {
  1.2179 +      return nullptr;
  1.2180 +    }
  1.2181 +  }
  1.2182 +
  1.2183 +  return result;
  1.2184 +}
  1.2185 +
  1.2186 +nsresult
  1.2187 +nsINode::ReplaceOrInsertBefore(bool aReplace, nsIDOMNode *aNewChild,
  1.2188 +                               nsIDOMNode *aRefChild, nsIDOMNode **aReturn)
  1.2189 +{
  1.2190 +  nsCOMPtr<nsINode> newChild = do_QueryInterface(aNewChild);
  1.2191 +  if (!newChild) {
  1.2192 +    return NS_ERROR_NULL_POINTER;
  1.2193 +  }
  1.2194 +
  1.2195 +  if (aReplace && !aRefChild) {
  1.2196 +    return NS_ERROR_NULL_POINTER;
  1.2197 +  }
  1.2198 +
  1.2199 +  nsCOMPtr<nsINode> refChild = do_QueryInterface(aRefChild);
  1.2200 +  if (aRefChild && !refChild) {
  1.2201 +    return NS_NOINTERFACE;
  1.2202 +  }
  1.2203 +
  1.2204 +  ErrorResult rv;
  1.2205 +  nsINode* result = ReplaceOrInsertBefore(aReplace, newChild, refChild, rv);
  1.2206 +  if (result) {
  1.2207 +    NS_ADDREF(*aReturn = result->AsDOMNode());
  1.2208 +  }
  1.2209 +  return rv.ErrorCode();
  1.2210 +}
  1.2211 +
  1.2212 +nsresult
  1.2213 +nsINode::CompareDocumentPosition(nsIDOMNode* aOther, uint16_t* aReturn)
  1.2214 +{
  1.2215 +  nsCOMPtr<nsINode> other = do_QueryInterface(aOther);
  1.2216 +  NS_ENSURE_ARG(other);
  1.2217 +  *aReturn = CompareDocumentPosition(*other);
  1.2218 +  return NS_OK;
  1.2219 +}
  1.2220 +
  1.2221 +nsresult
  1.2222 +nsINode::IsEqualNode(nsIDOMNode* aOther, bool* aReturn)
  1.2223 +{
  1.2224 +  nsCOMPtr<nsINode> other = do_QueryInterface(aOther);
  1.2225 +  *aReturn = IsEqualNode(other);
  1.2226 +  return NS_OK;
  1.2227 +}
  1.2228 +
  1.2229 +void
  1.2230 +nsINode::BindObject(nsISupports* aObject)
  1.2231 +{
  1.2232 +  nsCOMArray<nsISupports>* objects =
  1.2233 +    static_cast<nsCOMArray<nsISupports>*>(GetProperty(nsGkAtoms::keepobjectsalive));
  1.2234 +  if (!objects) {
  1.2235 +    objects = new nsCOMArray<nsISupports>();
  1.2236 +    SetProperty(nsGkAtoms::keepobjectsalive, objects,
  1.2237 +                nsINode::DeleteProperty< nsCOMArray<nsISupports> >, true);
  1.2238 +  }
  1.2239 +  objects->AppendObject(aObject);
  1.2240 +}
  1.2241 +
  1.2242 +void
  1.2243 +nsINode::UnbindObject(nsISupports* aObject)
  1.2244 +{
  1.2245 +  nsCOMArray<nsISupports>* objects =
  1.2246 +    static_cast<nsCOMArray<nsISupports>*>(GetProperty(nsGkAtoms::keepobjectsalive));
  1.2247 +  if (objects) {
  1.2248 +    objects->RemoveObject(aObject);
  1.2249 +  }
  1.2250 +}
  1.2251 +
  1.2252 +void
  1.2253 +nsINode::GetBoundMutationObservers(nsTArray<nsRefPtr<nsDOMMutationObserver> >& aResult)
  1.2254 +{
  1.2255 +  nsCOMArray<nsISupports>* objects =
  1.2256 +    static_cast<nsCOMArray<nsISupports>*>(GetProperty(nsGkAtoms::keepobjectsalive));
  1.2257 +  if (objects) {
  1.2258 +    for (int32_t i = 0; i < objects->Count(); ++i) {
  1.2259 +      nsCOMPtr<nsDOMMutationObserver> mo = do_QueryInterface(objects->ObjectAt(i));
  1.2260 +      if (mo) {
  1.2261 +        MOZ_ASSERT(!aResult.Contains(mo));
  1.2262 +        aResult.AppendElement(mo);
  1.2263 +      }
  1.2264 +    }
  1.2265 +  }
  1.2266 +}
  1.2267 +
  1.2268 +size_t
  1.2269 +nsINode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  1.2270 +{
  1.2271 +  size_t n = 0;
  1.2272 +  EventListenerManager* elm = GetExistingListenerManager();
  1.2273 +  if (elm) {
  1.2274 +    n += elm->SizeOfIncludingThis(aMallocSizeOf);
  1.2275 +  }
  1.2276 +
  1.2277 +  // Measurement of the following members may be added later if DMD finds it is
  1.2278 +  // worthwhile:
  1.2279 +  // - mNodeInfo
  1.2280 +  // - mSlots
  1.2281 +  //
  1.2282 +  // The following members are not measured:
  1.2283 +  // - mParent, mNextSibling, mPreviousSibling, mFirstChild: because they're
  1.2284 +  //   non-owning
  1.2285 +  return n;
  1.2286 +}
  1.2287 +
  1.2288 +#define EVENT(name_, id_, type_, struct_)                                    \
  1.2289 +  EventHandlerNonNull* nsINode::GetOn##name_() {                             \
  1.2290 +    EventListenerManager *elm = GetExistingListenerManager();                \
  1.2291 +    return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString())   \
  1.2292 +               : nullptr;                                                    \
  1.2293 +  }                                                                          \
  1.2294 +  void nsINode::SetOn##name_(EventHandlerNonNull* handler)                   \
  1.2295 +  {                                                                          \
  1.2296 +    EventListenerManager *elm = GetOrCreateListenerManager();                \
  1.2297 +    if (elm) {                                                               \
  1.2298 +      elm->SetEventHandler(nsGkAtoms::on##name_, EmptyString(), handler);    \
  1.2299 +    }                                                                        \
  1.2300 +  }
  1.2301 +#define TOUCH_EVENT EVENT
  1.2302 +#define DOCUMENT_ONLY_EVENT EVENT
  1.2303 +#include "mozilla/EventNameList.h"
  1.2304 +#undef DOCUMENT_ONLY_EVENT
  1.2305 +#undef TOUCH_EVENT
  1.2306 +#undef EVENT
  1.2307 +
  1.2308 +bool
  1.2309 +nsINode::Contains(const nsINode* aOther) const
  1.2310 +{
  1.2311 +  if (aOther == this) {
  1.2312 +    return true;
  1.2313 +  }
  1.2314 +  if (!aOther ||
  1.2315 +      OwnerDoc() != aOther->OwnerDoc() ||
  1.2316 +      IsInDoc() != aOther->IsInDoc() ||
  1.2317 +      !(aOther->IsElement() ||
  1.2318 +        aOther->IsNodeOfType(nsINode::eCONTENT)) ||
  1.2319 +      !GetFirstChild()) {
  1.2320 +    return false;
  1.2321 +  }
  1.2322 +
  1.2323 +  const nsIContent* other = static_cast<const nsIContent*>(aOther);
  1.2324 +  if (this == OwnerDoc()) {
  1.2325 +    // document.contains(aOther) returns true if aOther is in the document,
  1.2326 +    // but is not in any anonymous subtree.
  1.2327 +    // IsInDoc() check is done already before this.
  1.2328 +    return !other->IsInAnonymousSubtree();
  1.2329 +  }
  1.2330 +
  1.2331 +  if (!IsElement() && !IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT)) {
  1.2332 +    return false;
  1.2333 +  }
  1.2334 +
  1.2335 +  const nsIContent* thisContent = static_cast<const nsIContent*>(this);
  1.2336 +  if (thisContent->GetBindingParent() != other->GetBindingParent()) {
  1.2337 +    return false;
  1.2338 +  }
  1.2339 +
  1.2340 +  return nsContentUtils::ContentIsDescendantOf(other, this);
  1.2341 +}
  1.2342 +
  1.2343 +nsresult
  1.2344 +nsINode::Contains(nsIDOMNode* aOther, bool* aReturn)
  1.2345 +{
  1.2346 +  nsCOMPtr<nsINode> node = do_QueryInterface(aOther);
  1.2347 +  *aReturn = Contains(node);
  1.2348 +  return NS_OK;
  1.2349 +}
  1.2350 +
  1.2351 +uint32_t
  1.2352 +nsINode::Length() const
  1.2353 +{
  1.2354 +  switch (NodeType()) {
  1.2355 +  case nsIDOMNode::DOCUMENT_TYPE_NODE:
  1.2356 +    return 0;
  1.2357 +
  1.2358 +  case nsIDOMNode::TEXT_NODE:
  1.2359 +  case nsIDOMNode::CDATA_SECTION_NODE:
  1.2360 +  case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
  1.2361 +  case nsIDOMNode::COMMENT_NODE:
  1.2362 +    MOZ_ASSERT(IsNodeOfType(eCONTENT));
  1.2363 +    return static_cast<const nsIContent*>(this)->TextLength();
  1.2364 +
  1.2365 +  default:
  1.2366 +    return GetChildCount();
  1.2367 +  }
  1.2368 +}
  1.2369 +
  1.2370 +nsCSSSelectorList*
  1.2371 +nsINode::ParseSelectorList(const nsAString& aSelectorString,
  1.2372 +                           ErrorResult& aRv)
  1.2373 +{
  1.2374 +  nsIDocument* doc = OwnerDoc();
  1.2375 +  nsIDocument::SelectorCache& cache = doc->GetSelectorCache();
  1.2376 +  nsCSSSelectorList* selectorList = nullptr;
  1.2377 +  bool haveCachedList = cache.GetList(aSelectorString, &selectorList);
  1.2378 +  if (haveCachedList) {
  1.2379 +    if (!selectorList) {
  1.2380 +      // Invalid selector.
  1.2381 +      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
  1.2382 +    }
  1.2383 +    return selectorList;
  1.2384 +  }
  1.2385 +
  1.2386 +  nsCSSParser parser(doc->CSSLoader());
  1.2387 +
  1.2388 +  aRv = parser.ParseSelectorString(aSelectorString,
  1.2389 +                                   doc->GetDocumentURI(),
  1.2390 +                                   0, // XXXbz get the line number!
  1.2391 +                                   &selectorList);
  1.2392 +  if (aRv.Failed()) {
  1.2393 +    // We hit this for syntax errors, which are quite common, so don't
  1.2394 +    // use NS_ENSURE_SUCCESS.  (For example, jQuery has an extended set
  1.2395 +    // of selectors, but it sees if we can parse them first.)
  1.2396 +    MOZ_ASSERT(aRv.ErrorCode() == NS_ERROR_DOM_SYNTAX_ERR,
  1.2397 +               "Unexpected error, so cached version won't return it");
  1.2398 +    cache.CacheList(aSelectorString, nullptr);
  1.2399 +    return nullptr;
  1.2400 +  }
  1.2401 +
  1.2402 +  // Filter out pseudo-element selectors from selectorList
  1.2403 +  nsCSSSelectorList** slot = &selectorList;
  1.2404 +  do {
  1.2405 +    nsCSSSelectorList* cur = *slot;
  1.2406 +    if (cur->mSelectors->IsPseudoElement()) {
  1.2407 +      *slot = cur->mNext;
  1.2408 +      cur->mNext = nullptr;
  1.2409 +      delete cur;
  1.2410 +    } else {
  1.2411 +      slot = &cur->mNext;
  1.2412 +    }
  1.2413 +  } while (*slot);
  1.2414 +
  1.2415 +  if (selectorList) {
  1.2416 +    NS_ASSERTION(selectorList->mSelectors,
  1.2417 +                 "How can we not have any selectors?");
  1.2418 +    cache.CacheList(aSelectorString, selectorList);
  1.2419 +  } else {
  1.2420 +    // This is the "only pseudo-element selectors" case, which is
  1.2421 +    // not common, so just don't worry about caching it.  That way a
  1.2422 +    // null cached value can always indicate an invalid selector.
  1.2423 +  }
  1.2424 +
  1.2425 +  return selectorList;
  1.2426 +}
  1.2427 +
  1.2428 +static void
  1.2429 +AddScopeElements(TreeMatchContext& aMatchContext,
  1.2430 +                 nsINode* aMatchContextNode)
  1.2431 +{
  1.2432 +  if (aMatchContextNode->IsElement()) {
  1.2433 +    aMatchContext.SetHasSpecifiedScope();
  1.2434 +    aMatchContext.AddScopeElement(aMatchContextNode->AsElement());
  1.2435 +  }
  1.2436 +}
  1.2437 +
  1.2438 +namespace {
  1.2439 +struct SelectorMatchInfo {
  1.2440 +  nsCSSSelectorList* const mSelectorList;
  1.2441 +  TreeMatchContext& mMatchContext;
  1.2442 +};
  1.2443 +}
  1.2444 +
  1.2445 +// Given an id, find elements with that id under aRoot that match aMatchInfo if
  1.2446 +// any is provided.  If no SelectorMatchInfo is provided, just find the ones
  1.2447 +// with the given id.  aRoot must be in the document.
  1.2448 +template<bool onlyFirstMatch, class T>
  1.2449 +inline static void
  1.2450 +FindMatchingElementsWithId(const nsAString& aId, nsINode* aRoot,
  1.2451 +                           SelectorMatchInfo* aMatchInfo,
  1.2452 +                           T& aList)
  1.2453 +{
  1.2454 +  MOZ_ASSERT(aRoot->IsInDoc(),
  1.2455 +             "Don't call me if the root is not in the document");
  1.2456 +  MOZ_ASSERT(aRoot->IsElement() || aRoot->IsNodeOfType(nsINode::eDOCUMENT),
  1.2457 +             "The optimization below to check ContentIsDescendantOf only for "
  1.2458 +             "elements depends on aRoot being either an element or a "
  1.2459 +             "document if it's in the document.  Note that document fragments "
  1.2460 +             "can't be IsInDoc(), so should never show up here.");
  1.2461 +
  1.2462 +  const nsSmallVoidArray* elements = aRoot->OwnerDoc()->GetAllElementsForId(aId);
  1.2463 +
  1.2464 +  if (!elements) {
  1.2465 +    // Nothing to do; we're done
  1.2466 +    return;
  1.2467 +  }
  1.2468 +
  1.2469 +  // XXXbz: Should we fall back to the tree walk if aRoot is not the
  1.2470 +  // document and |elements| is long, for some value of "long"?
  1.2471 +  for (int32_t i = 0; i < elements->Count(); ++i) {
  1.2472 +    Element *element = static_cast<Element*>(elements->ElementAt(i));
  1.2473 +    if (!aRoot->IsElement() ||
  1.2474 +        (element != aRoot &&
  1.2475 +           nsContentUtils::ContentIsDescendantOf(element, aRoot))) {
  1.2476 +      // We have an element with the right id and it's a strict descendant
  1.2477 +      // of aRoot.  Make sure it really matches the selector.
  1.2478 +      if (!aMatchInfo ||
  1.2479 +          nsCSSRuleProcessor::SelectorListMatches(element,
  1.2480 +                                                  aMatchInfo->mMatchContext,
  1.2481 +                                                  aMatchInfo->mSelectorList)) {
  1.2482 +        aList.AppendElement(element);
  1.2483 +        if (onlyFirstMatch) {
  1.2484 +          return;
  1.2485 +        }
  1.2486 +      }
  1.2487 +    }
  1.2488 +  }
  1.2489 +}
  1.2490 +
  1.2491 +// Actually find elements matching aSelectorList (which must not be
  1.2492 +// null) and which are descendants of aRoot and put them in aList.  If
  1.2493 +// onlyFirstMatch, then stop once the first one is found.
  1.2494 +template<bool onlyFirstMatch, class Collector, class T>
  1.2495 +MOZ_ALWAYS_INLINE static void
  1.2496 +FindMatchingElements(nsINode* aRoot, nsCSSSelectorList* aSelectorList, T &aList,
  1.2497 +                     ErrorResult& aRv)
  1.2498 +{
  1.2499 +  nsIDocument* doc = aRoot->OwnerDoc();
  1.2500 +
  1.2501 +  TreeMatchContext matchingContext(false, nsRuleWalker::eRelevantLinkUnvisited,
  1.2502 +                                   doc, TreeMatchContext::eNeverMatchVisited);
  1.2503 +  doc->FlushPendingLinkUpdates();
  1.2504 +  AddScopeElements(matchingContext, aRoot);
  1.2505 +
  1.2506 +  // Fast-path selectors involving IDs.  We can only do this if aRoot
  1.2507 +  // is in the document and the document is not in quirks mode, since
  1.2508 +  // ID selectors are case-insensitive in quirks mode.  Also, only do
  1.2509 +  // this if aSelectorList only has one selector, because otherwise
  1.2510 +  // ordering the elements correctly is a pain.
  1.2511 +  NS_ASSERTION(aRoot->IsElement() || aRoot->IsNodeOfType(nsINode::eDOCUMENT) ||
  1.2512 +               !aRoot->IsInDoc(),
  1.2513 +               "The optimization below to check ContentIsDescendantOf only for "
  1.2514 +               "elements depends on aRoot being either an element or a "
  1.2515 +               "document if it's in the document.");
  1.2516 +  if (aRoot->IsInDoc() &&
  1.2517 +      doc->GetCompatibilityMode() != eCompatibility_NavQuirks &&
  1.2518 +      !aSelectorList->mNext &&
  1.2519 +      aSelectorList->mSelectors->mIDList) {
  1.2520 +    nsIAtom* id = aSelectorList->mSelectors->mIDList->mAtom;
  1.2521 +    SelectorMatchInfo info = { aSelectorList, matchingContext };
  1.2522 +    FindMatchingElementsWithId<onlyFirstMatch, T>(nsDependentAtomString(id),
  1.2523 +                                                  aRoot, &info, aList);
  1.2524 +    return;
  1.2525 +  }
  1.2526 +
  1.2527 +  Collector results;
  1.2528 +  for (nsIContent* cur = aRoot->GetFirstChild();
  1.2529 +       cur;
  1.2530 +       cur = cur->GetNextNode(aRoot)) {
  1.2531 +    if (cur->IsElement() &&
  1.2532 +        nsCSSRuleProcessor::SelectorListMatches(cur->AsElement(),
  1.2533 +                                                matchingContext,
  1.2534 +                                                aSelectorList)) {
  1.2535 +      if (onlyFirstMatch) {
  1.2536 +        aList.AppendElement(cur->AsElement());
  1.2537 +        return;
  1.2538 +      }
  1.2539 +      results.AppendElement(cur->AsElement());
  1.2540 +    }
  1.2541 +  }
  1.2542 +
  1.2543 +  const uint32_t len = results.Length();
  1.2544 +  if (len) {
  1.2545 +    aList.SetCapacity(len);
  1.2546 +    for (uint32_t i = 0; i < len; ++i) {
  1.2547 +      aList.AppendElement(results.ElementAt(i));
  1.2548 +    }
  1.2549 +  }
  1.2550 +}
  1.2551 +
  1.2552 +struct ElementHolder {
  1.2553 +  ElementHolder() : mElement(nullptr) {}
  1.2554 +  void AppendElement(Element* aElement) {
  1.2555 +    NS_ABORT_IF_FALSE(!mElement, "Should only get one element");
  1.2556 +    mElement = aElement;
  1.2557 +  }
  1.2558 +  void SetCapacity(uint32_t aCapacity) { MOZ_CRASH("Don't call me!"); }
  1.2559 +  uint32_t Length() { return 0; }
  1.2560 +  Element* ElementAt(uint32_t aIndex) { return nullptr; }
  1.2561 +
  1.2562 +  Element* mElement;
  1.2563 +};
  1.2564 +
  1.2565 +Element*
  1.2566 +nsINode::QuerySelector(const nsAString& aSelector, ErrorResult& aResult)
  1.2567 +{
  1.2568 +  nsCSSSelectorList* selectorList = ParseSelectorList(aSelector, aResult);
  1.2569 +  if (!selectorList) {
  1.2570 +    // Either we failed (and aResult already has the exception), or this
  1.2571 +    // is a pseudo-element-only selector that matches nothing.
  1.2572 +    return nullptr;
  1.2573 +  }
  1.2574 +  ElementHolder holder;
  1.2575 +  FindMatchingElements<true, ElementHolder>(this, selectorList, holder, aResult);
  1.2576 +  return holder.mElement;
  1.2577 +}
  1.2578 +
  1.2579 +already_AddRefed<nsINodeList>
  1.2580 +nsINode::QuerySelectorAll(const nsAString& aSelector, ErrorResult& aResult)
  1.2581 +{
  1.2582 +  nsRefPtr<nsSimpleContentList> contentList = new nsSimpleContentList(this);
  1.2583 +
  1.2584 +  nsCSSSelectorList* selectorList = ParseSelectorList(aSelector, aResult);
  1.2585 +  if (selectorList) {
  1.2586 +    FindMatchingElements<false, nsAutoTArray<Element*, 128>>(this,
  1.2587 +                                                             selectorList,
  1.2588 +                                                             *contentList,
  1.2589 +                                                             aResult);
  1.2590 +  } else {
  1.2591 +    // Either we failed (and aResult already has the exception), or this
  1.2592 +    // is a pseudo-element-only selector that matches nothing.
  1.2593 +  }
  1.2594 +
  1.2595 +  return contentList.forget();
  1.2596 +}
  1.2597 +
  1.2598 +nsresult
  1.2599 +nsINode::QuerySelector(const nsAString& aSelector, nsIDOMElement **aReturn)
  1.2600 +{
  1.2601 +  ErrorResult rv;
  1.2602 +  Element* result = nsINode::QuerySelector(aSelector, rv);
  1.2603 +  if (rv.Failed()) {
  1.2604 +    return rv.ErrorCode();
  1.2605 +  }
  1.2606 +  nsCOMPtr<nsIDOMElement> elt = do_QueryInterface(result);
  1.2607 +  elt.forget(aReturn);
  1.2608 +  return NS_OK;
  1.2609 +}
  1.2610 +
  1.2611 +nsresult
  1.2612 +nsINode::QuerySelectorAll(const nsAString& aSelector, nsIDOMNodeList **aReturn)
  1.2613 +{
  1.2614 +  ErrorResult rv;
  1.2615 +  *aReturn = nsINode::QuerySelectorAll(aSelector, rv).take();
  1.2616 +  return rv.ErrorCode();
  1.2617 +}
  1.2618 +
  1.2619 +Element*
  1.2620 +nsINode::GetElementById(const nsAString& aId)
  1.2621 +{
  1.2622 +  MOZ_ASSERT(IsElement() || IsNodeOfType(eDOCUMENT_FRAGMENT),
  1.2623 +             "Bogus this object for GetElementById call");
  1.2624 +  if (IsInDoc()) {
  1.2625 +    ElementHolder holder;
  1.2626 +    FindMatchingElementsWithId<true>(aId, this, nullptr, holder);
  1.2627 +    return holder.mElement;
  1.2628 +  }
  1.2629 +
  1.2630 +  for (nsIContent* kid = GetFirstChild(); kid; kid = kid->GetNextNode(this)) {
  1.2631 +    if (!kid->IsElement()) {
  1.2632 +      continue;
  1.2633 +    }
  1.2634 +    nsIAtom* id = kid->AsElement()->GetID();
  1.2635 +    if (id && id->Equals(aId)) {
  1.2636 +      return kid->AsElement();
  1.2637 +    }
  1.2638 +  }
  1.2639 +  return nullptr;
  1.2640 +}
  1.2641 +
  1.2642 +JSObject*
  1.2643 +nsINode::WrapObject(JSContext *aCx)
  1.2644 +{
  1.2645 +  MOZ_ASSERT(IsDOMBinding());
  1.2646 +
  1.2647 +  // Make sure one of these is true
  1.2648 +  // (1) our owner document has a script handling object,
  1.2649 +  // (2) Our owner document has had a script handling object, or has been marked
  1.2650 +  //     to have had one,
  1.2651 +  // (3) we are running a privileged script.
  1.2652 +  // Event handling is possible only if (1). If (2) event handling is
  1.2653 +  // prevented.
  1.2654 +  // If the document has never had a script handling object, untrusted
  1.2655 +  // scripts (3) shouldn't touch it!
  1.2656 +  bool hasHadScriptHandlingObject = false;
  1.2657 +  if (!OwnerDoc()->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
  1.2658 +      !hasHadScriptHandlingObject &&
  1.2659 +      !nsContentUtils::IsCallerChrome()) {
  1.2660 +    Throw(aCx, NS_ERROR_UNEXPECTED);
  1.2661 +    return nullptr;
  1.2662 +  }
  1.2663 +
  1.2664 +  JS::Rooted<JSObject*> obj(aCx, WrapNode(aCx));
  1.2665 +  MOZ_ASSERT_IF(ChromeOnlyAccess(),
  1.2666 +                xpc::IsInXBLScope(obj) || !xpc::UseXBLScope(js::GetObjectCompartment(obj)));
  1.2667 +  return obj;
  1.2668 +}
  1.2669 +
  1.2670 +already_AddRefed<nsINode>
  1.2671 +nsINode::CloneNode(bool aDeep, ErrorResult& aError)
  1.2672 +{
  1.2673 +  bool callUserDataHandlers = NodeType() != nsIDOMNode::DOCUMENT_NODE ||
  1.2674 +                              !static_cast<nsIDocument*>(this)->CreatingStaticClone();
  1.2675 +
  1.2676 +  nsCOMPtr<nsINode> result;
  1.2677 +  aError = nsNodeUtils::CloneNodeImpl(this, aDeep, callUserDataHandlers,
  1.2678 +                                      getter_AddRefs(result));
  1.2679 +  return result.forget();
  1.2680 +}
  1.2681 +
  1.2682 +nsDOMAttributeMap*
  1.2683 +nsINode::GetAttributes()
  1.2684 +{
  1.2685 +  if (!IsElement()) {
  1.2686 +    return nullptr;
  1.2687 +  }
  1.2688 +  return AsElement()->Attributes();
  1.2689 +}
  1.2690 +
  1.2691 +bool
  1.2692 +EventTarget::DispatchEvent(Event& aEvent,
  1.2693 +                           ErrorResult& aRv)
  1.2694 +{
  1.2695 +  bool result = false;
  1.2696 +  aRv = DispatchEvent(&aEvent, &result);
  1.2697 +  return result;
  1.2698 +}

mercurial