content/base/src/FragmentOrElement.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/base/src/FragmentOrElement.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2807 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 sw=2 et tw=79: */
     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 element classes; this provides an implementation
    1.12 + * of DOM Core's nsIDOMElement, implements nsIContent, provides
    1.13 + * utility methods for subclasses, and so forth.
    1.14 + */
    1.15 +
    1.16 +#include "mozilla/ArrayUtils.h"
    1.17 +#include "mozilla/Likely.h"
    1.18 +#include "mozilla/MemoryReporting.h"
    1.19 +#include "mozilla/StaticPtr.h"
    1.20 +
    1.21 +#include "mozilla/dom/FragmentOrElement.h"
    1.22 +
    1.23 +#include "mozilla/AsyncEventDispatcher.h"
    1.24 +#include "mozilla/EventDispatcher.h"
    1.25 +#include "mozilla/EventListenerManager.h"
    1.26 +#include "mozilla/EventStates.h"
    1.27 +#include "mozilla/dom/Attr.h"
    1.28 +#include "nsDOMAttributeMap.h"
    1.29 +#include "nsIAtom.h"
    1.30 +#include "nsINodeInfo.h"
    1.31 +#include "nsIDocumentInlines.h"
    1.32 +#include "nsIDocumentEncoder.h"
    1.33 +#include "nsIDOMNodeList.h"
    1.34 +#include "nsIContentIterator.h"
    1.35 +#include "nsFocusManager.h"
    1.36 +#include "nsILinkHandler.h"
    1.37 +#include "nsIScriptGlobalObject.h"
    1.38 +#include "nsIURL.h"
    1.39 +#include "nsNetUtil.h"
    1.40 +#include "nsIFrame.h"
    1.41 +#include "nsIAnonymousContentCreator.h"
    1.42 +#include "nsIPresShell.h"
    1.43 +#include "nsPresContext.h"
    1.44 +#include "nsStyleConsts.h"
    1.45 +#include "nsString.h"
    1.46 +#include "nsUnicharUtils.h"
    1.47 +#include "nsIDOMEvent.h"
    1.48 +#include "nsDOMCID.h"
    1.49 +#include "nsIServiceManager.h"
    1.50 +#include "nsIDOMCSSStyleDeclaration.h"
    1.51 +#include "nsDOMCSSAttrDeclaration.h"
    1.52 +#include "nsNameSpaceManager.h"
    1.53 +#include "nsContentList.h"
    1.54 +#include "nsDOMTokenList.h"
    1.55 +#include "nsXBLPrototypeBinding.h"
    1.56 +#include "nsError.h"
    1.57 +#include "nsDOMString.h"
    1.58 +#include "nsIScriptSecurityManager.h"
    1.59 +#include "nsIDOMMutationEvent.h"
    1.60 +#include "mozilla/InternalMutationEvent.h"
    1.61 +#include "mozilla/MouseEvents.h"
    1.62 +#include "nsNodeUtils.h"
    1.63 +#include "nsDocument.h"
    1.64 +#include "nsAttrValueOrString.h"
    1.65 +#ifdef MOZ_XUL
    1.66 +#include "nsXULElement.h"
    1.67 +#endif /* MOZ_XUL */
    1.68 +#include "nsFrameManager.h"
    1.69 +#include "nsFrameSelection.h"
    1.70 +#ifdef DEBUG
    1.71 +#include "nsRange.h"
    1.72 +#endif
    1.73 +
    1.74 +#include "nsBindingManager.h"
    1.75 +#include "nsXBLBinding.h"
    1.76 +#include "nsPIDOMWindow.h"
    1.77 +#include "nsPIBoxObject.h"
    1.78 +#include "nsSVGUtils.h"
    1.79 +#include "nsLayoutUtils.h"
    1.80 +#include "nsGkAtoms.h"
    1.81 +#include "nsContentUtils.h"
    1.82 +#include "nsTextFragment.h"
    1.83 +#include "nsContentCID.h"
    1.84 +
    1.85 +#include "nsIDOMEventListener.h"
    1.86 +#include "nsIWebNavigation.h"
    1.87 +#include "nsIBaseWindow.h"
    1.88 +#include "nsIWidget.h"
    1.89 +
    1.90 +#include "js/GCAPI.h"
    1.91 +
    1.92 +#include "nsNodeInfoManager.h"
    1.93 +#include "nsICategoryManager.h"
    1.94 +#include "nsIDOMUserDataHandler.h"
    1.95 +#include "nsGenericHTMLElement.h"
    1.96 +#include "nsIEditor.h"
    1.97 +#include "nsIEditorIMESupport.h"
    1.98 +#include "nsContentCreatorFunctions.h"
    1.99 +#include "nsIControllers.h"
   1.100 +#include "nsView.h"
   1.101 +#include "nsViewManager.h"
   1.102 +#include "nsIScrollableFrame.h"
   1.103 +#include "ChildIterator.h"
   1.104 +#include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
   1.105 +#include "nsRuleProcessorData.h"
   1.106 +#include "nsTextNode.h"
   1.107 +#include "mozilla/dom/NodeListBinding.h"
   1.108 +#include "mozilla/dom/UndoManager.h"
   1.109 +
   1.110 +#ifdef MOZ_XUL
   1.111 +#include "nsIXULDocument.h"
   1.112 +#endif /* MOZ_XUL */
   1.113 +
   1.114 +#include "nsCCUncollectableMarker.h"
   1.115 +
   1.116 +#include "mozAutoDocUpdate.h"
   1.117 +
   1.118 +#include "prprf.h"
   1.119 +#include "nsDOMMutationObserver.h"
   1.120 +#include "nsWrapperCacheInlines.h"
   1.121 +#include "nsCycleCollector.h"
   1.122 +#include "xpcpublic.h"
   1.123 +#include "nsIScriptError.h"
   1.124 +#include "mozilla/Telemetry.h"
   1.125 +
   1.126 +#include "mozilla/CORSMode.h"
   1.127 +
   1.128 +#include "mozilla/dom/ShadowRoot.h"
   1.129 +#include "mozilla/dom/HTMLTemplateElement.h"
   1.130 +
   1.131 +#include "nsStyledElement.h"
   1.132 +#include "nsIContentInlines.h"
   1.133 +
   1.134 +using namespace mozilla;
   1.135 +using namespace mozilla::dom;
   1.136 +
   1.137 +int32_t nsIContent::sTabFocusModel = eTabFocus_any;
   1.138 +bool nsIContent::sTabFocusModelAppliesToXUL = false;
   1.139 +uint32_t nsMutationGuard::sMutationCount = 0;
   1.140 +
   1.141 +nsIContent*
   1.142 +nsIContent::FindFirstNonChromeOnlyAccessContent() const
   1.143 +{
   1.144 +  // This handles also nested native anonymous content.
   1.145 +  for (const nsIContent *content = this; content;
   1.146 +       content = content->GetBindingParent()) {
   1.147 +    if (!content->ChromeOnlyAccess()) {
   1.148 +      // Oops, this function signature allows casting const to
   1.149 +      // non-const.  (Then again, so does GetChildAt(0)->GetParent().)
   1.150 +      return const_cast<nsIContent*>(content);
   1.151 +    }
   1.152 +  }
   1.153 +  return nullptr;
   1.154 +}
   1.155 +
   1.156 +nsIContent*
   1.157 +nsIContent::GetFlattenedTreeParent() const
   1.158 +{
   1.159 +  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
   1.160 +    nsIContent* parent = GetXBLInsertionParent();
   1.161 +    if (parent) {
   1.162 +      return parent;
   1.163 +    }
   1.164 +  }
   1.165 +
   1.166 +  return GetParent();
   1.167 +}
   1.168 +
   1.169 +nsIContent::IMEState
   1.170 +nsIContent::GetDesiredIMEState()
   1.171 +{
   1.172 +  if (!IsEditableInternal()) {
   1.173 +    // Check for the special case where we're dealing with elements which don't
   1.174 +    // have the editable flag set, but are readwrite (such as text controls).
   1.175 +    if (!IsElement() ||
   1.176 +        !AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
   1.177 +      return IMEState(IMEState::DISABLED);
   1.178 +    }
   1.179 +  }
   1.180 +  // NOTE: The content for independent editors (e.g., input[type=text],
   1.181 +  // textarea) must override this method, so, we don't need to worry about
   1.182 +  // that here.
   1.183 +  nsIContent *editableAncestor = GetEditingHost();
   1.184 +
   1.185 +  // This is in another editable content, use the result of it.
   1.186 +  if (editableAncestor && editableAncestor != this) {
   1.187 +    return editableAncestor->GetDesiredIMEState();
   1.188 +  }
   1.189 +  nsIDocument* doc = GetCurrentDoc();
   1.190 +  if (!doc) {
   1.191 +    return IMEState(IMEState::DISABLED);
   1.192 +  }
   1.193 +  nsIPresShell* ps = doc->GetShell();
   1.194 +  if (!ps) {
   1.195 +    return IMEState(IMEState::DISABLED);
   1.196 +  }
   1.197 +  nsPresContext* pc = ps->GetPresContext();
   1.198 +  if (!pc) {
   1.199 +    return IMEState(IMEState::DISABLED);
   1.200 +  }
   1.201 +  nsIEditor* editor = nsContentUtils::GetHTMLEditor(pc);
   1.202 +  nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(editor);
   1.203 +  if (!imeEditor) {
   1.204 +    return IMEState(IMEState::DISABLED);
   1.205 +  }
   1.206 +  IMEState state;
   1.207 +  imeEditor->GetPreferredIMEState(&state);
   1.208 +  return state;
   1.209 +}
   1.210 +
   1.211 +bool
   1.212 +nsIContent::HasIndependentSelection()
   1.213 +{
   1.214 +  nsIFrame* frame = GetPrimaryFrame();
   1.215 +  return (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
   1.216 +}
   1.217 +
   1.218 +dom::Element*
   1.219 +nsIContent::GetEditingHost()
   1.220 +{
   1.221 +  // If this isn't editable, return nullptr.
   1.222 +  NS_ENSURE_TRUE(IsEditableInternal(), nullptr);
   1.223 +
   1.224 +  nsIDocument* doc = GetCurrentDoc();
   1.225 +  NS_ENSURE_TRUE(doc, nullptr);
   1.226 +  // If this is in designMode, we should return <body>
   1.227 +  if (doc->HasFlag(NODE_IS_EDITABLE)) {
   1.228 +    return doc->GetBodyElement();
   1.229 +  }
   1.230 +
   1.231 +  nsIContent* content = this;
   1.232 +  for (dom::Element* parent = GetParentElement();
   1.233 +       parent && parent->HasFlag(NODE_IS_EDITABLE);
   1.234 +       parent = content->GetParentElement()) {
   1.235 +    content = parent;
   1.236 +  }
   1.237 +  return content->AsElement();
   1.238 +}
   1.239 +
   1.240 +nsresult
   1.241 +nsIContent::LookupNamespaceURIInternal(const nsAString& aNamespacePrefix,
   1.242 +                                       nsAString& aNamespaceURI) const
   1.243 +{
   1.244 +  if (aNamespacePrefix.EqualsLiteral("xml")) {
   1.245 +    // Special-case for xml prefix
   1.246 +    aNamespaceURI.AssignLiteral("http://www.w3.org/XML/1998/namespace");
   1.247 +    return NS_OK;
   1.248 +  }
   1.249 +
   1.250 +  if (aNamespacePrefix.EqualsLiteral("xmlns")) {
   1.251 +    // Special-case for xmlns prefix
   1.252 +    aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/");
   1.253 +    return NS_OK;
   1.254 +  }
   1.255 +
   1.256 +  nsCOMPtr<nsIAtom> name;
   1.257 +  if (!aNamespacePrefix.IsEmpty()) {
   1.258 +    name = do_GetAtom(aNamespacePrefix);
   1.259 +    NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
   1.260 +  }
   1.261 +  else {
   1.262 +    name = nsGkAtoms::xmlns;
   1.263 +  }
   1.264 +  // Trace up the content parent chain looking for the namespace
   1.265 +  // declaration that declares aNamespacePrefix.
   1.266 +  const nsIContent* content = this;
   1.267 +  do {
   1.268 +    if (content->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI))
   1.269 +      return NS_OK;
   1.270 +  } while ((content = content->GetParent()));
   1.271 +  return NS_ERROR_FAILURE;
   1.272 +}
   1.273 +
   1.274 +already_AddRefed<nsIURI>
   1.275 +nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const
   1.276 +{
   1.277 +  nsIDocument* doc = OwnerDoc();
   1.278 +  // Start with document base
   1.279 +  nsCOMPtr<nsIURI> base = doc->GetBaseURI(aTryUseXHRDocBaseURI);
   1.280 +
   1.281 +  // Collect array of xml:base attribute values up the parent chain. This
   1.282 +  // is slightly slower for the case when there are xml:base attributes, but
   1.283 +  // faster for the far more common case of there not being any such
   1.284 +  // attributes.
   1.285 +  // Also check for SVG elements which require special handling
   1.286 +  nsAutoTArray<nsString, 5> baseAttrs;
   1.287 +  nsString attr;
   1.288 +  const nsIContent *elem = this;
   1.289 +  do {
   1.290 +    // First check for SVG specialness (why is this SVG specific?)
   1.291 +    if (elem->IsSVG()) {
   1.292 +      nsIContent* bindingParent = elem->GetBindingParent();
   1.293 +      if (bindingParent) {
   1.294 +        nsXBLBinding* binding = bindingParent->GetXBLBinding();
   1.295 +        if (binding) {
   1.296 +          // XXX sXBL/XBL2 issue
   1.297 +          // If this is an anonymous XBL element use the binding
   1.298 +          // document for the base URI. 
   1.299 +          // XXX Will fail with xml:base
   1.300 +          base = binding->PrototypeBinding()->DocURI();
   1.301 +          break;
   1.302 +        }
   1.303 +      }
   1.304 +    }
   1.305 +
   1.306 +    nsIURI* explicitBaseURI = elem->GetExplicitBaseURI();
   1.307 +    if (explicitBaseURI) {
   1.308 +      base = explicitBaseURI;
   1.309 +      break;
   1.310 +    }
   1.311 +    
   1.312 +    // Otherwise check for xml:base attribute
   1.313 +    elem->GetAttr(kNameSpaceID_XML, nsGkAtoms::base, attr);
   1.314 +    if (!attr.IsEmpty()) {
   1.315 +      baseAttrs.AppendElement(attr);
   1.316 +    }
   1.317 +    elem = elem->GetParent();
   1.318 +  } while(elem);
   1.319 +  
   1.320 +  // Now resolve against all xml:base attrs
   1.321 +  for (uint32_t i = baseAttrs.Length() - 1; i != uint32_t(-1); --i) {
   1.322 +    nsCOMPtr<nsIURI> newBase;
   1.323 +    nsresult rv = NS_NewURI(getter_AddRefs(newBase), baseAttrs[i],
   1.324 +                            doc->GetDocumentCharacterSet().get(), base);
   1.325 +    // Do a security check, almost the same as nsDocument::SetBaseURL()
   1.326 +    // Only need to do this on the final uri
   1.327 +    if (NS_SUCCEEDED(rv) && i == 0) {
   1.328 +      rv = nsContentUtils::GetSecurityManager()->
   1.329 +        CheckLoadURIWithPrincipal(NodePrincipal(), newBase,
   1.330 +                                  nsIScriptSecurityManager::STANDARD);
   1.331 +    }
   1.332 +    if (NS_SUCCEEDED(rv)) {
   1.333 +      base.swap(newBase);
   1.334 +    }
   1.335 +  }
   1.336 +
   1.337 +  return base.forget();
   1.338 +}
   1.339 +
   1.340 +//----------------------------------------------------------------------
   1.341 +
   1.342 +static inline JSObject*
   1.343 +GetJSObjectChild(nsWrapperCache* aCache)
   1.344 +{
   1.345 +  return aCache->PreservingWrapper() ? aCache->GetWrapperPreserveColor() : nullptr;
   1.346 +}
   1.347 +
   1.348 +static bool
   1.349 +NeedsScriptTraverse(nsINode* aNode)
   1.350 +{
   1.351 +  return aNode->PreservingWrapper() && aNode->GetWrapperPreserveColor() &&
   1.352 +         !aNode->IsBlackAndDoesNotNeedTracing(aNode);
   1.353 +}
   1.354 +
   1.355 +//----------------------------------------------------------------------
   1.356 +
   1.357 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsChildContentList)
   1.358 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsChildContentList)
   1.359 +
   1.360 +// If nsChildContentList is changed so that any additional fields are
   1.361 +// traversed by the cycle collector, then CAN_SKIP must be updated to
   1.362 +// check that the additional fields are null.
   1.363 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsChildContentList)
   1.364 +
   1.365 +// nsChildContentList only ever has a single child, its wrapper, so if
   1.366 +// the wrapper is black, the list can't be part of a garbage cycle.
   1.367 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsChildContentList)
   1.368 +  return tmp->IsBlack();
   1.369 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
   1.370 +
   1.371 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsChildContentList)
   1.372 +  return tmp->IsBlackAndDoesNotNeedTracing(tmp);
   1.373 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
   1.374 +
   1.375 +// CanSkipThis returns false to avoid problems with incomplete unlinking.
   1.376 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsChildContentList)
   1.377 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
   1.378 +
   1.379 +NS_INTERFACE_TABLE_HEAD(nsChildContentList)
   1.380 +  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   1.381 +  NS_INTERFACE_TABLE(nsChildContentList, nsINodeList, nsIDOMNodeList)
   1.382 +  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsChildContentList)
   1.383 +NS_INTERFACE_MAP_END
   1.384 +
   1.385 +JSObject*
   1.386 +nsChildContentList::WrapObject(JSContext *cx)
   1.387 +{
   1.388 +  return NodeListBinding::Wrap(cx, this);
   1.389 +}
   1.390 +
   1.391 +NS_IMETHODIMP
   1.392 +nsChildContentList::GetLength(uint32_t* aLength)
   1.393 +{
   1.394 +  *aLength = mNode ? mNode->GetChildCount() : 0;
   1.395 +
   1.396 +  return NS_OK;
   1.397 +}
   1.398 +
   1.399 +NS_IMETHODIMP
   1.400 +nsChildContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
   1.401 +{
   1.402 +  nsINode* node = Item(aIndex);
   1.403 +  if (!node) {
   1.404 +    *aReturn = nullptr;
   1.405 +
   1.406 +    return NS_OK;
   1.407 +  }
   1.408 +
   1.409 +  return CallQueryInterface(node, aReturn);
   1.410 +}
   1.411 +
   1.412 +nsIContent*
   1.413 +nsChildContentList::Item(uint32_t aIndex)
   1.414 +{
   1.415 +  if (mNode) {
   1.416 +    return mNode->GetChildAt(aIndex);
   1.417 +  }
   1.418 +
   1.419 +  return nullptr;
   1.420 +}
   1.421 +
   1.422 +int32_t
   1.423 +nsChildContentList::IndexOf(nsIContent* aContent)
   1.424 +{
   1.425 +  if (mNode) {
   1.426 +    return mNode->IndexOf(aContent);
   1.427 +  }
   1.428 +
   1.429 +  return -1;
   1.430 +}
   1.431 +
   1.432 +//----------------------------------------------------------------------
   1.433 +
   1.434 +NS_IMPL_CYCLE_COLLECTION(nsNode3Tearoff, mNode)
   1.435 +
   1.436 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNode3Tearoff)
   1.437 +  NS_INTERFACE_MAP_ENTRY(nsIDOMXPathNSResolver)
   1.438 +NS_INTERFACE_MAP_END_AGGREGATED(mNode)
   1.439 +
   1.440 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNode3Tearoff)
   1.441 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNode3Tearoff)
   1.442 +
   1.443 +NS_IMETHODIMP
   1.444 +nsNode3Tearoff::LookupNamespaceURI(const nsAString& aNamespacePrefix,
   1.445 +                                   nsAString& aNamespaceURI)
   1.446 +{
   1.447 +  mNode->LookupNamespaceURI(aNamespacePrefix, aNamespaceURI);
   1.448 +  return NS_OK;
   1.449 +}
   1.450 +
   1.451 +nsIHTMLCollection*
   1.452 +FragmentOrElement::Children()
   1.453 +{
   1.454 +  FragmentOrElement::nsDOMSlots *slots = DOMSlots();
   1.455 +
   1.456 +  if (!slots->mChildrenList) {
   1.457 +    slots->mChildrenList = new nsContentList(this, kNameSpaceID_Wildcard, 
   1.458 +                                             nsGkAtoms::_asterix, nsGkAtoms::_asterix,
   1.459 +                                             false);
   1.460 +  }
   1.461 +
   1.462 +  return slots->mChildrenList;
   1.463 +}
   1.464 +
   1.465 +
   1.466 +//----------------------------------------------------------------------
   1.467 +
   1.468 +
   1.469 +NS_IMPL_ISUPPORTS(nsNodeWeakReference,
   1.470 +                  nsIWeakReference)
   1.471 +
   1.472 +nsNodeWeakReference::~nsNodeWeakReference()
   1.473 +{
   1.474 +  if (mNode) {
   1.475 +    NS_ASSERTION(mNode->Slots()->mWeakReference == this,
   1.476 +                 "Weak reference has wrong value");
   1.477 +    mNode->Slots()->mWeakReference = nullptr;
   1.478 +  }
   1.479 +}
   1.480 +
   1.481 +NS_IMETHODIMP
   1.482 +nsNodeWeakReference::QueryReferent(const nsIID& aIID, void** aInstancePtr)
   1.483 +{
   1.484 +  return mNode ? mNode->QueryInterface(aIID, aInstancePtr) :
   1.485 +                 NS_ERROR_NULL_POINTER;
   1.486 +}
   1.487 +
   1.488 +
   1.489 +NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff, mNode)
   1.490 +
   1.491 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff)
   1.492 +  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   1.493 +NS_INTERFACE_MAP_END_AGGREGATED(mNode)
   1.494 +
   1.495 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff)
   1.496 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff)
   1.497 +
   1.498 +NS_IMETHODIMP
   1.499 +nsNodeSupportsWeakRefTearoff::GetWeakReference(nsIWeakReference** aInstancePtr)
   1.500 +{
   1.501 +  nsINode::nsSlots* slots = mNode->Slots();
   1.502 +  if (!slots->mWeakReference) {
   1.503 +    slots->mWeakReference = new nsNodeWeakReference(mNode);
   1.504 +    NS_ENSURE_TRUE(slots->mWeakReference, NS_ERROR_OUT_OF_MEMORY);
   1.505 +  }
   1.506 +
   1.507 +  NS_ADDREF(*aInstancePtr = slots->mWeakReference);
   1.508 +
   1.509 +  return NS_OK;
   1.510 +}
   1.511 +
   1.512 +//----------------------------------------------------------------------
   1.513 +FragmentOrElement::nsDOMSlots::nsDOMSlots()
   1.514 +  : nsINode::nsSlots(),
   1.515 +    mDataset(nullptr),
   1.516 +    mUndoManager(nullptr),
   1.517 +    mBindingParent(nullptr)
   1.518 +{
   1.519 +}
   1.520 +
   1.521 +FragmentOrElement::nsDOMSlots::~nsDOMSlots()
   1.522 +{
   1.523 +  if (mAttributeMap) {
   1.524 +    mAttributeMap->DropReference();
   1.525 +  }
   1.526 +}
   1.527 +
   1.528 +void
   1.529 +FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb, bool aIsXUL)
   1.530 +{
   1.531 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mStyle");
   1.532 +  cb.NoteXPCOMChild(mStyle.get());
   1.533 +
   1.534 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mSMILOverrideStyle");
   1.535 +  cb.NoteXPCOMChild(mSMILOverrideStyle.get());
   1.536 +
   1.537 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mAttributeMap");
   1.538 +  cb.NoteXPCOMChild(mAttributeMap.get());
   1.539 +
   1.540 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mUndoManager");
   1.541 +  cb.NoteXPCOMChild(mUndoManager.get());
   1.542 +
   1.543 +  if (aIsXUL) {
   1.544 +    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mControllers");
   1.545 +    cb.NoteXPCOMChild(mControllers);
   1.546 +  }
   1.547 +
   1.548 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLBinding");
   1.549 +  cb.NoteNativeChild(mXBLBinding, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
   1.550 +
   1.551 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
   1.552 +  cb.NoteXPCOMChild(mXBLInsertionParent.get());
   1.553 +
   1.554 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mShadowRoot");
   1.555 +  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
   1.556 +
   1.557 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
   1.558 +  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
   1.559 +
   1.560 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
   1.561 +  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
   1.562 +
   1.563 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
   1.564 +  cb.NoteXPCOMChild(mClassList.get());
   1.565 +
   1.566 +  if (mCustomElementData) {
   1.567 +    for (uint32_t i = 0; i < mCustomElementData->mCallbackQueue.Length(); i++) {
   1.568 +      mCustomElementData->mCallbackQueue[i]->Traverse(cb);
   1.569 +    }
   1.570 +  }
   1.571 +}
   1.572 +
   1.573 +void
   1.574 +FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL)
   1.575 +{
   1.576 +  mStyle = nullptr;
   1.577 +  mSMILOverrideStyle = nullptr;
   1.578 +  if (mAttributeMap) {
   1.579 +    mAttributeMap->DropReference();
   1.580 +    mAttributeMap = nullptr;
   1.581 +  }
   1.582 +  if (aIsXUL)
   1.583 +    NS_IF_RELEASE(mControllers);
   1.584 +  mXBLBinding = nullptr;
   1.585 +  mXBLInsertionParent = nullptr;
   1.586 +  mShadowRoot = nullptr;
   1.587 +  mContainingShadow = nullptr;
   1.588 +  mChildrenList = nullptr;
   1.589 +  mUndoManager = nullptr;
   1.590 +  mCustomElementData = nullptr;
   1.591 +  mClassList = nullptr;
   1.592 +}
   1.593 +
   1.594 +size_t
   1.595 +FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   1.596 +{
   1.597 +  size_t n = aMallocSizeOf(this);
   1.598 +
   1.599 +  if (mAttributeMap) {
   1.600 +    n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf);
   1.601 +  }
   1.602 +
   1.603 +  // Measurement of the following members may be added later if DMD finds it is
   1.604 +  // worthwhile:
   1.605 +  // - Superclass members (nsINode::nsSlots)
   1.606 +  // - mStyle
   1.607 +  // - mDataSet
   1.608 +  // - mSMILOverrideStyle
   1.609 +  // - mSMILOverrideStyleRule
   1.610 +  // - mChildrenList
   1.611 +  // - mClassList
   1.612 +
   1.613 +  // The following members are not measured:
   1.614 +  // - mBindingParent / mControllers: because they're   non-owning
   1.615 +  return n;
   1.616 +}
   1.617 +
   1.618 +FragmentOrElement::FragmentOrElement(already_AddRefed<nsINodeInfo>& aNodeInfo)
   1.619 +  : nsIContent(aNodeInfo)
   1.620 +{
   1.621 +}
   1.622 +
   1.623 +FragmentOrElement::FragmentOrElement(already_AddRefed<nsINodeInfo>&& aNodeInfo)
   1.624 +  : nsIContent(aNodeInfo)
   1.625 +{
   1.626 +}
   1.627 +
   1.628 +FragmentOrElement::~FragmentOrElement()
   1.629 +{
   1.630 +  NS_PRECONDITION(!IsInDoc(),
   1.631 +                  "Please remove this from the document properly");
   1.632 +  if (GetParent()) {
   1.633 +    NS_RELEASE(mParent);
   1.634 +  }
   1.635 +}
   1.636 +
   1.637 +already_AddRefed<nsINodeList>
   1.638 +FragmentOrElement::GetChildren(uint32_t aFilter)
   1.639 +{
   1.640 +  nsRefPtr<nsSimpleContentList> list = new nsSimpleContentList(this);
   1.641 +  if (!list) {
   1.642 +    return nullptr;
   1.643 +  }
   1.644 +
   1.645 +  nsIFrame *frame = GetPrimaryFrame();
   1.646 +
   1.647 +  // Append :before generated content.
   1.648 +  if (frame) {
   1.649 +    nsIFrame *beforeFrame = nsLayoutUtils::GetBeforeFrame(frame);
   1.650 +    if (beforeFrame) {
   1.651 +      list->AppendElement(beforeFrame->GetContent());
   1.652 +    }
   1.653 +  }
   1.654 +
   1.655 +  // If XBL is bound to this node then append XBL anonymous content including
   1.656 +  // explict content altered by insertion point if we were requested for XBL
   1.657 +  // anonymous content, otherwise append explicit content with respect to
   1.658 +  // insertion point if any.
   1.659 +  if (!(aFilter & eAllButXBL)) {
   1.660 +    FlattenedChildIterator iter(this);
   1.661 +    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
   1.662 +      list->AppendElement(child);
   1.663 +    }
   1.664 +  } else {
   1.665 +    ExplicitChildIterator iter(this);
   1.666 +    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
   1.667 +      list->AppendElement(child);
   1.668 +    }
   1.669 +  }
   1.670 +
   1.671 +  if (frame) {
   1.672 +    // Append native anonymous content to the end.
   1.673 +    nsIAnonymousContentCreator* creator = do_QueryFrame(frame);
   1.674 +    if (creator) {
   1.675 +      creator->AppendAnonymousContentTo(*list, aFilter);
   1.676 +    }
   1.677 +
   1.678 +    // Append :after generated content.
   1.679 +    nsIFrame *afterFrame = nsLayoutUtils::GetAfterFrame(frame);
   1.680 +    if (afterFrame) {
   1.681 +      list->AppendElement(afterFrame->GetContent());
   1.682 +    }
   1.683 +  }
   1.684 +
   1.685 +  return list.forget();
   1.686 +}
   1.687 +
   1.688 +static nsIContent*
   1.689 +FindChromeAccessOnlySubtreeOwner(nsIContent* aContent)
   1.690 +{
   1.691 +  if (aContent->ChromeOnlyAccess()) {
   1.692 +    bool chromeAccessOnly = false;
   1.693 +    while (aContent && !chromeAccessOnly) {
   1.694 +      chromeAccessOnly = aContent->IsRootOfChromeAccessOnlySubtree();
   1.695 +      aContent = aContent->GetParent();
   1.696 +    }
   1.697 +  }
   1.698 +  return aContent;
   1.699 +}
   1.700 +
   1.701 +nsresult
   1.702 +nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor)
   1.703 +{
   1.704 +  //FIXME! Document how this event retargeting works, Bug 329124.
   1.705 +  aVisitor.mCanHandle = true;
   1.706 +  aVisitor.mMayHaveListenerManager = HasListenerManager();
   1.707 +
   1.708 +  // Don't propagate mouseover and mouseout events when mouse is moving
   1.709 +  // inside chrome access only content.
   1.710 +  bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
   1.711 +  if ((aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH ||
   1.712 +       aVisitor.mEvent->message == NS_MOUSE_EXIT_SYNTH ||
   1.713 +       aVisitor.mEvent->message == NS_POINTER_OVER ||
   1.714 +       aVisitor.mEvent->message == NS_POINTER_OUT) &&
   1.715 +      // Check if we should stop event propagation when event has just been
   1.716 +      // dispatched or when we're about to propagate from
   1.717 +      // chrome access only subtree.
   1.718 +      ((this == aVisitor.mEvent->originalTarget &&
   1.719 +        !ChromeOnlyAccess()) || isAnonForEvents)) {
   1.720 +     nsCOMPtr<nsIContent> relatedTarget =
   1.721 +       do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->relatedTarget);
   1.722 +    if (relatedTarget &&
   1.723 +        relatedTarget->OwnerDoc() == OwnerDoc()) {
   1.724 +
   1.725 +      // If current target is anonymous for events or we know that related
   1.726 +      // target is descendant of an element which is anonymous for events,
   1.727 +      // we may want to stop event propagation.
   1.728 +      // If this is the original target, aVisitor.mRelatedTargetIsInAnon
   1.729 +      // must be updated.
   1.730 +      if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
   1.731 +          (aVisitor.mEvent->originalTarget == this &&
   1.732 +           (aVisitor.mRelatedTargetIsInAnon =
   1.733 +            relatedTarget->ChromeOnlyAccess()))) {
   1.734 +        nsIContent* anonOwner = FindChromeAccessOnlySubtreeOwner(this);
   1.735 +        if (anonOwner) {
   1.736 +          nsIContent* anonOwnerRelated =
   1.737 +            FindChromeAccessOnlySubtreeOwner(relatedTarget);
   1.738 +          if (anonOwnerRelated) {
   1.739 +            // Note, anonOwnerRelated may still be inside some other
   1.740 +            // native anonymous subtree. The case where anonOwner is still
   1.741 +            // inside native anonymous subtree will be handled when event
   1.742 +            // propagates up in the DOM tree.
   1.743 +            while (anonOwner != anonOwnerRelated &&
   1.744 +                   anonOwnerRelated->ChromeOnlyAccess()) {
   1.745 +              anonOwnerRelated = FindChromeAccessOnlySubtreeOwner(anonOwnerRelated);
   1.746 +            }
   1.747 +            if (anonOwner == anonOwnerRelated) {
   1.748 +#ifdef DEBUG_smaug
   1.749 +              nsCOMPtr<nsIContent> originalTarget =
   1.750 +                do_QueryInterface(aVisitor.mEvent->originalTarget);
   1.751 +              nsAutoString ot, ct, rt;
   1.752 +              if (originalTarget) {
   1.753 +                originalTarget->Tag()->ToString(ot);
   1.754 +              }
   1.755 +              Tag()->ToString(ct);
   1.756 +              relatedTarget->Tag()->ToString(rt);
   1.757 +              printf("Stopping %s propagation:"
   1.758 +                     "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s"
   1.759 +                     "\n\trelatedTarget=%s %s \n%s",
   1.760 +                     (aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH)
   1.761 +                       ? "mouseover" : "mouseout",
   1.762 +                     NS_ConvertUTF16toUTF8(ot).get(),
   1.763 +                     NS_ConvertUTF16toUTF8(ct).get(),
   1.764 +                     isAnonForEvents
   1.765 +                       ? "(is native anonymous)"
   1.766 +                       : (ChromeOnlyAccess()
   1.767 +                           ? "(is in native anonymous subtree)" : ""),
   1.768 +                     NS_ConvertUTF16toUTF8(rt).get(),
   1.769 +                     relatedTarget->ChromeOnlyAccess()
   1.770 +                       ? "(is in native anonymous subtree)" : "",
   1.771 +                     (originalTarget &&
   1.772 +                      relatedTarget->FindFirstNonChromeOnlyAccessContent() ==
   1.773 +                        originalTarget->FindFirstNonChromeOnlyAccessContent())
   1.774 +                       ? "" : "Wrong event propagation!?!\n");
   1.775 +#endif
   1.776 +              aVisitor.mParentTarget = nullptr;
   1.777 +              // Event should not propagate to non-anon content.
   1.778 +              aVisitor.mCanHandle = isAnonForEvents;
   1.779 +              return NS_OK;
   1.780 +            }
   1.781 +          }
   1.782 +        }
   1.783 +      }
   1.784 +    }
   1.785 +  }
   1.786 +
   1.787 +  nsIContent* parent = GetParent();
   1.788 +  // Event may need to be retargeted if this is the root of a native
   1.789 +  // anonymous content subtree or event is dispatched somewhere inside XBL.
   1.790 +  if (isAnonForEvents) {
   1.791 +#ifdef DEBUG
   1.792 +    // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
   1.793 +    // all the events are allowed even in the native anonymous content..
   1.794 +    nsCOMPtr<nsIContent> t = do_QueryInterface(aVisitor.mEvent->originalTarget);
   1.795 +    NS_ASSERTION(!t || !t->ChromeOnlyAccess() ||
   1.796 +                 aVisitor.mEvent->eventStructType != NS_MUTATION_EVENT ||
   1.797 +                 aVisitor.mDOMEvent,
   1.798 +                 "Mutation event dispatched in native anonymous content!?!");
   1.799 +#endif
   1.800 +    aVisitor.mEventTargetAtParent = parent;
   1.801 +  } else if (parent && aVisitor.mOriginalTargetIsInAnon) {
   1.802 +    nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->target));
   1.803 +    if (content && content->GetBindingParent() == parent) {
   1.804 +      aVisitor.mEventTargetAtParent = parent;
   1.805 +    }
   1.806 +  }
   1.807 +
   1.808 +  // check for an anonymous parent
   1.809 +  // XXX XBL2/sXBL issue
   1.810 +  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
   1.811 +    nsIContent* insertionParent = GetXBLInsertionParent();
   1.812 +    NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent &&
   1.813 +                   aVisitor.mEventTargetAtParent != insertionParent),
   1.814 +                 "Retargeting and having insertion parent!");
   1.815 +    if (insertionParent) {
   1.816 +      parent = insertionParent;
   1.817 +    }
   1.818 +  }
   1.819 +
   1.820 +  if (parent) {
   1.821 +    aVisitor.mParentTarget = parent;
   1.822 +  } else {
   1.823 +    aVisitor.mParentTarget = GetCurrentDoc();
   1.824 +  }
   1.825 +  return NS_OK;
   1.826 +}
   1.827 +
   1.828 +bool
   1.829 +nsIContent::GetAttr(int32_t aNameSpaceID, nsIAtom* aName,
   1.830 +                    nsAString& aResult) const
   1.831 +{
   1.832 +  if (IsElement()) {
   1.833 +    return AsElement()->GetAttr(aNameSpaceID, aName, aResult);
   1.834 +  }
   1.835 +  aResult.Truncate();
   1.836 +  return false;
   1.837 +}
   1.838 +
   1.839 +bool
   1.840 +nsIContent::HasAttr(int32_t aNameSpaceID, nsIAtom* aName) const
   1.841 +{
   1.842 +  return IsElement() && AsElement()->HasAttr(aNameSpaceID, aName);
   1.843 +}
   1.844 +
   1.845 +bool
   1.846 +nsIContent::AttrValueIs(int32_t aNameSpaceID,
   1.847 +                        nsIAtom* aName,
   1.848 +                        const nsAString& aValue,
   1.849 +                        nsCaseTreatment aCaseSensitive) const
   1.850 +{
   1.851 +  return IsElement() &&
   1.852 +    AsElement()->AttrValueIs(aNameSpaceID, aName, aValue, aCaseSensitive);
   1.853 +}
   1.854 +
   1.855 +bool
   1.856 +nsIContent::AttrValueIs(int32_t aNameSpaceID,
   1.857 +                        nsIAtom* aName,
   1.858 +                        nsIAtom* aValue,
   1.859 +                        nsCaseTreatment aCaseSensitive) const
   1.860 +{
   1.861 +  return IsElement() &&
   1.862 +    AsElement()->AttrValueIs(aNameSpaceID, aName, aValue, aCaseSensitive);
   1.863 +}
   1.864 +
   1.865 +bool
   1.866 +nsIContent::IsFocusable(int32_t* aTabIndex, bool aWithMouse)
   1.867 +{
   1.868 +  bool focusable = IsFocusableInternal(aTabIndex, aWithMouse);
   1.869 +  // Ensure that the return value and aTabIndex are consistent in the case
   1.870 +  // we're in userfocusignored context.
   1.871 +  if (focusable || (aTabIndex && *aTabIndex != -1)) {
   1.872 +    if (nsContentUtils::IsUserFocusIgnored(this)) {
   1.873 +      if (aTabIndex) {
   1.874 +        *aTabIndex = -1;
   1.875 +      }
   1.876 +      return false;
   1.877 +    }
   1.878 +    return focusable;
   1.879 +  }
   1.880 +  return false;
   1.881 +}
   1.882 +
   1.883 +bool
   1.884 +nsIContent::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse)
   1.885 +{
   1.886 +  if (aTabIndex) {
   1.887 +    *aTabIndex = -1; // Default, not tabbable
   1.888 +  }
   1.889 +  return false;
   1.890 +}
   1.891 +
   1.892 +const nsAttrValue*
   1.893 +FragmentOrElement::DoGetClasses() const
   1.894 +{
   1.895 +  NS_NOTREACHED("Shouldn't ever be called");
   1.896 +  return nullptr;
   1.897 +}
   1.898 +
   1.899 +NS_IMETHODIMP
   1.900 +FragmentOrElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
   1.901 +{
   1.902 +  return NS_OK;
   1.903 +}
   1.904 +
   1.905 +bool
   1.906 +FragmentOrElement::IsLink(nsIURI** aURI) const
   1.907 +{
   1.908 +  *aURI = nullptr;
   1.909 +  return false;
   1.910 +}
   1.911 +
   1.912 +nsIContent*
   1.913 +FragmentOrElement::GetBindingParent() const
   1.914 +{
   1.915 +  nsDOMSlots *slots = GetExistingDOMSlots();
   1.916 +
   1.917 +  if (slots) {
   1.918 +    return slots->mBindingParent;
   1.919 +  }
   1.920 +  return nullptr;
   1.921 +}
   1.922 +
   1.923 +nsXBLBinding*
   1.924 +FragmentOrElement::GetXBLBinding() const
   1.925 +{
   1.926 +  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
   1.927 +    nsDOMSlots *slots = GetExistingDOMSlots();
   1.928 +    if (slots) {
   1.929 +      return slots->mXBLBinding;
   1.930 +    }
   1.931 +  }
   1.932 +
   1.933 +  return nullptr;
   1.934 +}
   1.935 +
   1.936 +void
   1.937 +FragmentOrElement::SetXBLBinding(nsXBLBinding* aBinding,
   1.938 +                                 nsBindingManager* aOldBindingManager)
   1.939 +{
   1.940 +  nsBindingManager* bindingManager;
   1.941 +  if (aOldBindingManager) {
   1.942 +    MOZ_ASSERT(!aBinding, "aOldBindingManager should only be provided "
   1.943 +                          "when removing a binding.");
   1.944 +    bindingManager = aOldBindingManager;
   1.945 +  } else {
   1.946 +    bindingManager = OwnerDoc()->BindingManager();
   1.947 +  }
   1.948 +
   1.949 +  // After this point, aBinding will be the most-derived binding for aContent.
   1.950 +  // If we already have a binding for aContent, make sure to
   1.951 +  // remove it from the attached stack.  Otherwise we might end up firing its
   1.952 +  // constructor twice (if aBinding inherits from it) or firing its constructor
   1.953 +  // after aContent has been deleted (if aBinding is null and the content node
   1.954 +  // dies before we process mAttachedStack).
   1.955 +  nsRefPtr<nsXBLBinding> oldBinding = GetXBLBinding();
   1.956 +  if (oldBinding) {
   1.957 +    bindingManager->RemoveFromAttachedQueue(oldBinding);
   1.958 +  }
   1.959 +
   1.960 +  nsDOMSlots *slots = DOMSlots();
   1.961 +  if (aBinding) {
   1.962 +    SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
   1.963 +    slots->mXBLBinding = aBinding;
   1.964 +    bindingManager->AddBoundContent(this);
   1.965 +  } else {
   1.966 +    slots->mXBLBinding = nullptr;
   1.967 +    bindingManager->RemoveBoundContent(this);
   1.968 +    if (oldBinding) {
   1.969 +      oldBinding->SetBoundElement(nullptr);
   1.970 +    }
   1.971 +  }
   1.972 +}
   1.973 +
   1.974 +nsIContent*
   1.975 +FragmentOrElement::GetXBLInsertionParent() const
   1.976 +{
   1.977 +  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
   1.978 +    nsDOMSlots *slots = GetExistingDOMSlots();
   1.979 +    if (slots) {
   1.980 +      return slots->mXBLInsertionParent;
   1.981 +    }
   1.982 +  }
   1.983 +
   1.984 +  return nullptr;
   1.985 +}
   1.986 +
   1.987 +ShadowRoot*
   1.988 +FragmentOrElement::GetShadowRoot() const
   1.989 +{
   1.990 +  nsDOMSlots *slots = GetExistingDOMSlots();
   1.991 +  if (slots) {
   1.992 +    return slots->mShadowRoot;
   1.993 +  }
   1.994 +  return nullptr;
   1.995 +}
   1.996 +
   1.997 +ShadowRoot*
   1.998 +FragmentOrElement::GetContainingShadow() const
   1.999 +{
  1.1000 +  nsDOMSlots *slots = GetExistingDOMSlots();
  1.1001 +  if (slots) {
  1.1002 +    return slots->mContainingShadow;
  1.1003 +  }
  1.1004 +  return nullptr;
  1.1005 +}
  1.1006 +
  1.1007 +void
  1.1008 +FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot)
  1.1009 +{
  1.1010 +  nsDOMSlots *slots = DOMSlots();
  1.1011 +  slots->mShadowRoot = aShadowRoot;
  1.1012 +}
  1.1013 +
  1.1014 +void
  1.1015 +FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
  1.1016 +{
  1.1017 +  nsDOMSlots *slots = DOMSlots();
  1.1018 +  if (aContent) {
  1.1019 +    SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
  1.1020 +  }
  1.1021 +  slots->mXBLInsertionParent = aContent;
  1.1022 +}
  1.1023 +
  1.1024 +CustomElementData*
  1.1025 +FragmentOrElement::GetCustomElementData() const
  1.1026 +{
  1.1027 +  nsDOMSlots *slots = GetExistingDOMSlots();
  1.1028 +  if (slots) {
  1.1029 +    return slots->mCustomElementData;
  1.1030 +  }
  1.1031 +  return nullptr;
  1.1032 +}
  1.1033 +
  1.1034 +void
  1.1035 +FragmentOrElement::SetCustomElementData(CustomElementData* aData)
  1.1036 +{
  1.1037 +  nsDOMSlots *slots = DOMSlots();
  1.1038 +  MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
  1.1039 +  slots->mCustomElementData = aData;
  1.1040 +}
  1.1041 +
  1.1042 +nsresult
  1.1043 +FragmentOrElement::InsertChildAt(nsIContent* aKid,
  1.1044 +                                uint32_t aIndex,
  1.1045 +                                bool aNotify)
  1.1046 +{
  1.1047 +  NS_PRECONDITION(aKid, "null ptr");
  1.1048 +
  1.1049 +  return doInsertChildAt(aKid, aIndex, aNotify, mAttrsAndChildren);
  1.1050 +}
  1.1051 +
  1.1052 +void
  1.1053 +FragmentOrElement::RemoveChildAt(uint32_t aIndex, bool aNotify)
  1.1054 +{
  1.1055 +  nsCOMPtr<nsIContent> oldKid = mAttrsAndChildren.GetSafeChildAt(aIndex);
  1.1056 +  NS_ASSERTION(oldKid == GetChildAt(aIndex), "Unexpected child in RemoveChildAt");
  1.1057 +
  1.1058 +  if (oldKid) {
  1.1059 +    doRemoveChildAt(aIndex, aNotify, oldKid, mAttrsAndChildren);
  1.1060 +  }
  1.1061 +}
  1.1062 +
  1.1063 +void
  1.1064 +FragmentOrElement::GetTextContentInternal(nsAString& aTextContent)
  1.1065 +{
  1.1066 +  if(!nsContentUtils::GetNodeTextContent(this, true, aTextContent))
  1.1067 +    NS_RUNTIMEABORT("OOM");
  1.1068 +}
  1.1069 +
  1.1070 +void
  1.1071 +FragmentOrElement::SetTextContentInternal(const nsAString& aTextContent,
  1.1072 +                                          ErrorResult& aError)
  1.1073 +{
  1.1074 +  aError = nsContentUtils::SetNodeTextContent(this, aTextContent, false);
  1.1075 +}
  1.1076 +
  1.1077 +void
  1.1078 +FragmentOrElement::DestroyContent()
  1.1079 +{
  1.1080 +  nsIDocument *document = OwnerDoc();
  1.1081 +  document->BindingManager()->RemovedFromDocument(this, document);
  1.1082 +  document->ClearBoxObjectFor(this);
  1.1083 +
  1.1084 +  // XXX We really should let cycle collection do this, but that currently still
  1.1085 +  //     leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
  1.1086 +  ReleaseWrapper(this);
  1.1087 +
  1.1088 +  uint32_t i, count = mAttrsAndChildren.ChildCount();
  1.1089 +  for (i = 0; i < count; ++i) {
  1.1090 +    // The child can remove itself from the parent in BindToTree.
  1.1091 +    mAttrsAndChildren.ChildAt(i)->DestroyContent();
  1.1092 +  }
  1.1093 +}
  1.1094 +
  1.1095 +void
  1.1096 +FragmentOrElement::SaveSubtreeState()
  1.1097 +{
  1.1098 +  uint32_t i, count = mAttrsAndChildren.ChildCount();
  1.1099 +  for (i = 0; i < count; ++i) {
  1.1100 +    mAttrsAndChildren.ChildAt(i)->SaveSubtreeState();
  1.1101 +  }
  1.1102 +}
  1.1103 +
  1.1104 +//----------------------------------------------------------------------
  1.1105 +
  1.1106 +// Generic DOMNode implementations
  1.1107 +
  1.1108 +void
  1.1109 +FragmentOrElement::FireNodeInserted(nsIDocument* aDoc,
  1.1110 +                                   nsINode* aParent,
  1.1111 +                                   nsTArray<nsCOMPtr<nsIContent> >& aNodes)
  1.1112 +{
  1.1113 +  uint32_t count = aNodes.Length();
  1.1114 +  for (uint32_t i = 0; i < count; ++i) {
  1.1115 +    nsIContent* childContent = aNodes[i];
  1.1116 +
  1.1117 +    if (nsContentUtils::HasMutationListeners(childContent,
  1.1118 +          NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) {
  1.1119 +      InternalMutationEvent mutation(true, NS_MUTATION_NODEINSERTED);
  1.1120 +      mutation.mRelatedNode = do_QueryInterface(aParent);
  1.1121 +
  1.1122 +      mozAutoSubtreeModified subtree(aDoc, aParent);
  1.1123 +      (new AsyncEventDispatcher(childContent, mutation))->RunDOMEventWhenSafe();
  1.1124 +    }
  1.1125 +  }
  1.1126 +}
  1.1127 +
  1.1128 +//----------------------------------------------------------------------
  1.1129 +
  1.1130 +// nsISupports implementation
  1.1131 +
  1.1132 +#define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
  1.1133 +
  1.1134 +class ContentUnbinder : public nsRunnable
  1.1135 +{
  1.1136 +public:
  1.1137 +  ContentUnbinder()
  1.1138 +  {
  1.1139 +    mLast = this;
  1.1140 +  }
  1.1141 +
  1.1142 +  ~ContentUnbinder()
  1.1143 +  {
  1.1144 +    Run();
  1.1145 +  }
  1.1146 +
  1.1147 +  void UnbindSubtree(nsIContent* aNode)
  1.1148 +  {
  1.1149 +    if (aNode->NodeType() != nsIDOMNode::ELEMENT_NODE &&
  1.1150 +        aNode->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
  1.1151 +      return;
  1.1152 +    }
  1.1153 +    FragmentOrElement* container = static_cast<FragmentOrElement*>(aNode);
  1.1154 +    uint32_t childCount = container->mAttrsAndChildren.ChildCount();
  1.1155 +    if (childCount) {
  1.1156 +      while (childCount-- > 0) {
  1.1157 +        // Hold a strong ref to the node when we remove it, because we may be
  1.1158 +        // the last reference to it.  We need to call TakeChildAt() and
  1.1159 +        // update mFirstChild before calling UnbindFromTree, since this last
  1.1160 +        // can notify various observers and they should really see consistent
  1.1161 +        // tree state.
  1.1162 +        nsCOMPtr<nsIContent> child =
  1.1163 +          container->mAttrsAndChildren.TakeChildAt(childCount);
  1.1164 +        if (childCount == 0) {
  1.1165 +          container->mFirstChild = nullptr;
  1.1166 +        }
  1.1167 +        UnbindSubtree(child);
  1.1168 +        child->UnbindFromTree();
  1.1169 +      }
  1.1170 +    }
  1.1171 +  }
  1.1172 +
  1.1173 +  NS_IMETHOD Run()
  1.1174 +  {
  1.1175 +    nsAutoScriptBlocker scriptBlocker;
  1.1176 +    uint32_t len = mSubtreeRoots.Length();
  1.1177 +    if (len) {
  1.1178 +      PRTime start = PR_Now();
  1.1179 +      for (uint32_t i = 0; i < len; ++i) {
  1.1180 +        UnbindSubtree(mSubtreeRoots[i]);
  1.1181 +      }
  1.1182 +      mSubtreeRoots.Clear();
  1.1183 +      Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND,
  1.1184 +                            uint32_t(PR_Now() - start) / PR_USEC_PER_MSEC);
  1.1185 +    }
  1.1186 +    nsCycleCollector_dispatchDeferredDeletion();
  1.1187 +    if (this == sContentUnbinder) {
  1.1188 +      sContentUnbinder = nullptr;
  1.1189 +      if (mNext) {
  1.1190 +        nsRefPtr<ContentUnbinder> next;
  1.1191 +        next.swap(mNext);
  1.1192 +        sContentUnbinder = next;
  1.1193 +        next->mLast = mLast;
  1.1194 +        mLast = nullptr;
  1.1195 +        NS_DispatchToMainThread(next);
  1.1196 +      }
  1.1197 +    }
  1.1198 +    return NS_OK;
  1.1199 +  }
  1.1200 +
  1.1201 +  static void UnbindAll()
  1.1202 +  {
  1.1203 +    nsRefPtr<ContentUnbinder> ub = sContentUnbinder;
  1.1204 +    sContentUnbinder = nullptr;
  1.1205 +    while (ub) {
  1.1206 +      ub->Run();
  1.1207 +      ub = ub->mNext;
  1.1208 +    }
  1.1209 +  }
  1.1210 +
  1.1211 +  static void Append(nsIContent* aSubtreeRoot)
  1.1212 +  {
  1.1213 +    if (!sContentUnbinder) {
  1.1214 +      sContentUnbinder = new ContentUnbinder();
  1.1215 +      nsCOMPtr<nsIRunnable> e = sContentUnbinder;
  1.1216 +      NS_DispatchToMainThread(e);
  1.1217 +    }
  1.1218 +
  1.1219 +    if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
  1.1220 +        SUBTREE_UNBINDINGS_PER_RUNNABLE) {
  1.1221 +      sContentUnbinder->mLast->mNext = new ContentUnbinder();
  1.1222 +      sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
  1.1223 +    }
  1.1224 +    sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
  1.1225 +  }
  1.1226 +
  1.1227 +private:
  1.1228 +  nsAutoTArray<nsCOMPtr<nsIContent>,
  1.1229 +               SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots;
  1.1230 +  nsRefPtr<ContentUnbinder>                     mNext;
  1.1231 +  ContentUnbinder*                              mLast;
  1.1232 +  static ContentUnbinder*                       sContentUnbinder;
  1.1233 +};
  1.1234 +
  1.1235 +ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr;
  1.1236 +
  1.1237 +void
  1.1238 +FragmentOrElement::ClearContentUnbinder()
  1.1239 +{
  1.1240 +  ContentUnbinder::UnbindAll();
  1.1241 +}
  1.1242 +
  1.1243 +NS_IMPL_CYCLE_COLLECTION_CLASS(FragmentOrElement)
  1.1244 +
  1.1245 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
  1.1246 +  nsINode::Unlink(tmp);
  1.1247 +
  1.1248 +  if (tmp->HasProperties()) {
  1.1249 +    if (tmp->IsHTML()) {
  1.1250 +      nsIAtom*** props = nsGenericHTMLElement::PropertiesToTraverseAndUnlink();
  1.1251 +      for (uint32_t i = 0; props[i]; ++i) {
  1.1252 +        tmp->DeleteProperty(*props[i]);
  1.1253 +      }
  1.1254 +    }
  1.1255 +  }
  1.1256 +
  1.1257 +  // Unlink child content (and unbind our subtree).
  1.1258 +  if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) {
  1.1259 +    uint32_t childCount = tmp->mAttrsAndChildren.ChildCount();
  1.1260 +    if (childCount) {
  1.1261 +      // Don't allow script to run while we're unbinding everything.
  1.1262 +      nsAutoScriptBlocker scriptBlocker;
  1.1263 +      while (childCount-- > 0) {
  1.1264 +        // Hold a strong ref to the node when we remove it, because we may be
  1.1265 +        // the last reference to it.  We need to call TakeChildAt() and
  1.1266 +        // update mFirstChild before calling UnbindFromTree, since this last
  1.1267 +        // can notify various observers and they should really see consistent
  1.1268 +        // tree state.
  1.1269 +        nsCOMPtr<nsIContent> child = tmp->mAttrsAndChildren.TakeChildAt(childCount);
  1.1270 +        if (childCount == 0) {
  1.1271 +          tmp->mFirstChild = nullptr;
  1.1272 +        }
  1.1273 +        child->UnbindFromTree();
  1.1274 +      }
  1.1275 +    }
  1.1276 +  } else if (!tmp->GetParent() && tmp->mAttrsAndChildren.ChildCount()) {
  1.1277 +    ContentUnbinder::Append(tmp);
  1.1278 +  } /* else {
  1.1279 +    The subtree root will end up to a ContentUnbinder, and that will
  1.1280 +    unbind the child nodes.
  1.1281 +  } */
  1.1282 +
  1.1283 +  // Unlink any DOM slots of interest.
  1.1284 +  {
  1.1285 +    nsDOMSlots *slots = tmp->GetExistingDOMSlots();
  1.1286 +    if (slots) {
  1.1287 +      slots->Unlink(tmp->IsXUL());
  1.1288 +    }
  1.1289 +  }
  1.1290 +
  1.1291 +  {
  1.1292 +    nsIDocument *doc;
  1.1293 +    if (!tmp->GetParentNode() && (doc = tmp->OwnerDoc())) {
  1.1294 +      doc->BindingManager()->RemovedFromDocument(tmp, doc);
  1.1295 +    }
  1.1296 +  }
  1.1297 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  1.1298 +
  1.1299 +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)
  1.1300 +
  1.1301 +void
  1.1302 +FragmentOrElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
  1.1303 +                               void* aData)
  1.1304 +{
  1.1305 +  uint32_t* gen = static_cast<uint32_t*>(aData);
  1.1306 +  xpc_MarkInCCGeneration(static_cast<nsISupports*>(aChild), *gen);
  1.1307 +}
  1.1308 +
  1.1309 +void
  1.1310 +FragmentOrElement::MarkUserDataHandler(void* aObject, nsIAtom* aKey,
  1.1311 +                                      void* aChild, void* aData)
  1.1312 +{
  1.1313 +  xpc_TryUnmarkWrappedGrayObject(static_cast<nsISupports*>(aChild));
  1.1314 +}
  1.1315 +
  1.1316 +void
  1.1317 +FragmentOrElement::MarkNodeChildren(nsINode* aNode)
  1.1318 +{
  1.1319 +  JSObject* o = GetJSObjectChild(aNode);
  1.1320 +  if (o) {
  1.1321 +    JS::ExposeObjectToActiveJS(o);
  1.1322 +  }
  1.1323 +
  1.1324 +  EventListenerManager* elm = aNode->GetExistingListenerManager();
  1.1325 +  if (elm) {
  1.1326 +    elm->MarkForCC();
  1.1327 +  }
  1.1328 +
  1.1329 +  if (aNode->HasProperties()) {
  1.1330 +    nsIDocument* ownerDoc = aNode->OwnerDoc();
  1.1331 +    ownerDoc->PropertyTable(DOM_USER_DATA)->
  1.1332 +      Enumerate(aNode, FragmentOrElement::MarkUserData,
  1.1333 +                &nsCCUncollectableMarker::sGeneration);
  1.1334 +    ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->
  1.1335 +      Enumerate(aNode, FragmentOrElement::MarkUserDataHandler,
  1.1336 +                &nsCCUncollectableMarker::sGeneration);
  1.1337 +  }
  1.1338 +}
  1.1339 +
  1.1340 +nsINode*
  1.1341 +FindOptimizableSubtreeRoot(nsINode* aNode)
  1.1342 +{
  1.1343 +  nsINode* p;
  1.1344 +  while ((p = aNode->GetParentNode())) {
  1.1345 +    if (aNode->UnoptimizableCCNode()) {
  1.1346 +      return nullptr;
  1.1347 +    }
  1.1348 +    aNode = p;
  1.1349 +  }
  1.1350 +
  1.1351 +  if (aNode->UnoptimizableCCNode()) {
  1.1352 +    return nullptr;
  1.1353 +  }
  1.1354 +  return aNode;
  1.1355 +}
  1.1356 +
  1.1357 +StaticAutoPtr<nsTHashtable<nsPtrHashKey<nsINode>>> gCCBlackMarkedNodes;
  1.1358 +
  1.1359 +static PLDHashOperator
  1.1360 +VisitBlackMarkedNode(nsPtrHashKey<nsINode>* aEntry, void*)
  1.1361 +{
  1.1362 +  nsINode* n = aEntry->GetKey();
  1.1363 +  n->SetCCMarkedRoot(false);
  1.1364 +  n->SetInCCBlackTree(false);
  1.1365 +  return PL_DHASH_NEXT;
  1.1366 +}
  1.1367 +
  1.1368 +static void
  1.1369 +ClearBlackMarkedNodes()
  1.1370 +{
  1.1371 +  if (!gCCBlackMarkedNodes) {
  1.1372 +    return;
  1.1373 +  }
  1.1374 +  gCCBlackMarkedNodes->EnumerateEntries(VisitBlackMarkedNode, nullptr);
  1.1375 +  gCCBlackMarkedNodes = nullptr;
  1.1376 +}
  1.1377 +
  1.1378 +// static
  1.1379 +void
  1.1380 +FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode)
  1.1381 +{
  1.1382 +  if (!gCCBlackMarkedNodes) {
  1.1383 +    return;
  1.1384 +  }
  1.1385 +  gCCBlackMarkedNodes->RemoveEntry(aNode);
  1.1386 +}
  1.1387 +
  1.1388 +// static
  1.1389 +bool
  1.1390 +FragmentOrElement::CanSkipInCC(nsINode* aNode)
  1.1391 +{
  1.1392 +  // Don't try to optimize anything during shutdown.
  1.1393 +  if (nsCCUncollectableMarker::sGeneration == 0) {
  1.1394 +    return false;
  1.1395 +  }
  1.1396 +
  1.1397 +  nsIDocument* currentDoc = aNode->GetCurrentDoc();
  1.1398 +  if (currentDoc &&
  1.1399 +      nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
  1.1400 +    return !NeedsScriptTraverse(aNode);
  1.1401 +  }
  1.1402 +
  1.1403 +  // Bail out early if aNode is somewhere in anonymous content,
  1.1404 +  // or otherwise unusual.
  1.1405 +  if (aNode->UnoptimizableCCNode()) {
  1.1406 +    return false;
  1.1407 +  }
  1.1408 +
  1.1409 +  nsINode* root =
  1.1410 +    currentDoc ? static_cast<nsINode*>(currentDoc) :
  1.1411 +                 FindOptimizableSubtreeRoot(aNode);
  1.1412 +  if (!root) {
  1.1413 +    return false;
  1.1414 +  }
  1.1415 +
  1.1416 +  // Subtree has been traversed already.
  1.1417 +  if (root->CCMarkedRoot()) {
  1.1418 +    return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
  1.1419 +  }
  1.1420 +
  1.1421 +  if (!gCCBlackMarkedNodes) {
  1.1422 +    gCCBlackMarkedNodes = new nsTHashtable<nsPtrHashKey<nsINode> >(1020);
  1.1423 +  }
  1.1424 +
  1.1425 +  // nodesToUnpurple contains nodes which will be removed
  1.1426 +  // from the purple buffer if the DOM tree is black.
  1.1427 +  nsAutoTArray<nsIContent*, 1020> nodesToUnpurple;
  1.1428 +  // grayNodes need script traverse, so they aren't removed from
  1.1429 +  // the purple buffer, but are marked to be in black subtree so that
  1.1430 +  // traverse is faster.
  1.1431 +  nsAutoTArray<nsINode*, 1020> grayNodes;
  1.1432 +
  1.1433 +  bool foundBlack = root->IsBlack();
  1.1434 +  if (root != currentDoc) {
  1.1435 +    currentDoc = nullptr;
  1.1436 +    if (NeedsScriptTraverse(root)) {
  1.1437 +      grayNodes.AppendElement(root);
  1.1438 +    } else if (static_cast<nsIContent*>(root)->IsPurple()) {
  1.1439 +      nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root));
  1.1440 +    }
  1.1441 +  }
  1.1442 +
  1.1443 +  // Traverse the subtree and check if we could know without CC
  1.1444 +  // that it is black.
  1.1445 +  // Note, this traverse is non-virtual and inline, so it should be a lot faster
  1.1446 +  // than CC's generic traverse.
  1.1447 +  for (nsIContent* node = root->GetFirstChild(); node;
  1.1448 +       node = node->GetNextNode(root)) {
  1.1449 +    foundBlack = foundBlack || node->IsBlack();
  1.1450 +    if (foundBlack && currentDoc) {
  1.1451 +      // If we can mark the whole document black, no need to optimize
  1.1452 +      // so much, since when the next purple node in the document will be
  1.1453 +      // handled, it is fast to check that currentDoc is in CCGeneration.
  1.1454 +      break;
  1.1455 +    }
  1.1456 +    if (NeedsScriptTraverse(node)) {
  1.1457 +      // Gray nodes need real CC traverse.
  1.1458 +      grayNodes.AppendElement(node);
  1.1459 +    } else if (node->IsPurple()) {
  1.1460 +      nodesToUnpurple.AppendElement(node);
  1.1461 +    }
  1.1462 +  }
  1.1463 +
  1.1464 +  root->SetCCMarkedRoot(true);
  1.1465 +  root->SetInCCBlackTree(foundBlack);
  1.1466 +  gCCBlackMarkedNodes->PutEntry(root);
  1.1467 +
  1.1468 +  if (!foundBlack) {
  1.1469 +    return false;
  1.1470 +  }
  1.1471 +
  1.1472 +  if (currentDoc) {
  1.1473 +    // Special case documents. If we know the document is black,
  1.1474 +    // we can mark the document to be in CCGeneration.
  1.1475 +    currentDoc->
  1.1476 +      MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
  1.1477 +  } else {
  1.1478 +    for (uint32_t i = 0; i < grayNodes.Length(); ++i) {
  1.1479 +      nsINode* node = grayNodes[i];
  1.1480 +      node->SetInCCBlackTree(true);
  1.1481 +      gCCBlackMarkedNodes->PutEntry(node);
  1.1482 +    }
  1.1483 +  }
  1.1484 +
  1.1485 +  // Subtree is black, we can remove non-gray purple nodes from
  1.1486 +  // purple buffer.
  1.1487 +  for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) {
  1.1488 +    nsIContent* purple = nodesToUnpurple[i];
  1.1489 +    // Can't remove currently handled purple node.
  1.1490 +    if (purple != aNode) {
  1.1491 +      purple->RemovePurple();
  1.1492 +    }
  1.1493 +  }
  1.1494 +  return !NeedsScriptTraverse(aNode);
  1.1495 +}
  1.1496 +
  1.1497 +nsAutoTArray<nsINode*, 1020>* gPurpleRoots = nullptr;
  1.1498 +nsAutoTArray<nsIContent*, 1020>* gNodesToUnbind = nullptr;
  1.1499 +
  1.1500 +void ClearCycleCollectorCleanupData()
  1.1501 +{
  1.1502 +  if (gPurpleRoots) {
  1.1503 +    uint32_t len = gPurpleRoots->Length();
  1.1504 +    for (uint32_t i = 0; i < len; ++i) {
  1.1505 +      nsINode* n = gPurpleRoots->ElementAt(i);
  1.1506 +      n->SetIsPurpleRoot(false);
  1.1507 +    }
  1.1508 +    delete gPurpleRoots;
  1.1509 +    gPurpleRoots = nullptr;
  1.1510 +  }
  1.1511 +  if (gNodesToUnbind) {
  1.1512 +    uint32_t len = gNodesToUnbind->Length();
  1.1513 +    for (uint32_t i = 0; i < len; ++i) {
  1.1514 +      nsIContent* c = gNodesToUnbind->ElementAt(i);
  1.1515 +      c->SetIsPurpleRoot(false);
  1.1516 +      ContentUnbinder::Append(c);
  1.1517 +    }
  1.1518 +    delete gNodesToUnbind;
  1.1519 +    gNodesToUnbind = nullptr;
  1.1520 +  }
  1.1521 +}
  1.1522 +
  1.1523 +static bool
  1.1524 +ShouldClearPurple(nsIContent* aContent)
  1.1525 +{
  1.1526 +  if (aContent && aContent->IsPurple()) {
  1.1527 +    return true;
  1.1528 +  }
  1.1529 +
  1.1530 +  JSObject* o = GetJSObjectChild(aContent);
  1.1531 +  if (o && xpc_IsGrayGCThing(o)) {
  1.1532 +    return true;
  1.1533 +  }
  1.1534 +
  1.1535 +  if (aContent->HasListenerManager()) {
  1.1536 +    return true;
  1.1537 +  }
  1.1538 +
  1.1539 +  return aContent->HasProperties();
  1.1540 +}
  1.1541 +
  1.1542 +// If aNode is not optimizable, but is an element
  1.1543 +// with a frame in a document which has currently active presshell,
  1.1544 +// we can act as if it was optimizable. When the primary frame dies, aNode
  1.1545 +// will end up to the purple buffer because of the refcount change.
  1.1546 +bool
  1.1547 +NodeHasActiveFrame(nsIDocument* aCurrentDoc, nsINode* aNode)
  1.1548 +{
  1.1549 +  return aCurrentDoc->GetShell() && aNode->IsElement() &&
  1.1550 +         aNode->AsElement()->GetPrimaryFrame();
  1.1551 +}
  1.1552 +
  1.1553 +bool
  1.1554 +OwnedByBindingManager(nsIDocument* aCurrentDoc, nsINode* aNode)
  1.1555 +{
  1.1556 +  return aNode->IsElement() && aNode->AsElement()->GetXBLBinding();
  1.1557 +}
  1.1558 +
  1.1559 +// CanSkip checks if aNode is black, and if it is, returns
  1.1560 +// true. If aNode is in a black DOM tree, CanSkip may also remove other objects
  1.1561 +// from purple buffer and unmark event listeners and user data.
  1.1562 +// If the root of the DOM tree is a document, less optimizations are done
  1.1563 +// since checking the blackness of the current document is usually fast and we
  1.1564 +// don't want slow down such common cases.
  1.1565 +bool
  1.1566 +FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed)
  1.1567 +{
  1.1568 +  // Don't try to optimize anything during shutdown.
  1.1569 +  if (nsCCUncollectableMarker::sGeneration == 0) {
  1.1570 +    return false;
  1.1571 +  }
  1.1572 +
  1.1573 +  bool unoptimizable = aNode->UnoptimizableCCNode();
  1.1574 +  nsIDocument* currentDoc = aNode->GetCurrentDoc();
  1.1575 +  if (currentDoc &&
  1.1576 +      nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration()) &&
  1.1577 +      (!unoptimizable || NodeHasActiveFrame(currentDoc, aNode) ||
  1.1578 +       OwnedByBindingManager(currentDoc, aNode))) {
  1.1579 +    MarkNodeChildren(aNode);
  1.1580 +    return true;
  1.1581 +  }
  1.1582 +
  1.1583 +  if (unoptimizable) {
  1.1584 +    return false;
  1.1585 +  }
  1.1586 +
  1.1587 +  nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc) :
  1.1588 +                               FindOptimizableSubtreeRoot(aNode);
  1.1589 +  if (!root) {
  1.1590 +    return false;
  1.1591 +  }
  1.1592 + 
  1.1593 +  // Subtree has been traversed already, and aNode has
  1.1594 +  // been handled in a way that doesn't require revisiting it.
  1.1595 +  if (root->IsPurpleRoot()) {
  1.1596 +    return false;
  1.1597 +  }
  1.1598 +
  1.1599 +  // nodesToClear contains nodes which are either purple or
  1.1600 +  // gray.
  1.1601 +  nsAutoTArray<nsIContent*, 1020> nodesToClear;
  1.1602 +
  1.1603 +  bool foundBlack = root->IsBlack();
  1.1604 +  bool domOnlyCycle = false;
  1.1605 +  if (root != currentDoc) {
  1.1606 +    currentDoc = nullptr;
  1.1607 +    if (!foundBlack) {
  1.1608 +      domOnlyCycle = static_cast<nsIContent*>(root)->OwnedOnlyByTheDOMTree();
  1.1609 +    }
  1.1610 +    if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
  1.1611 +      nodesToClear.AppendElement(static_cast<nsIContent*>(root));
  1.1612 +    }
  1.1613 +  }
  1.1614 +
  1.1615 +  // Traverse the subtree and check if we could know without CC
  1.1616 +  // that it is black.
  1.1617 +  // Note, this traverse is non-virtual and inline, so it should be a lot faster
  1.1618 +  // than CC's generic traverse.
  1.1619 +  for (nsIContent* node = root->GetFirstChild(); node;
  1.1620 +       node = node->GetNextNode(root)) {
  1.1621 +    foundBlack = foundBlack || node->IsBlack();
  1.1622 +    if (foundBlack) {
  1.1623 +      domOnlyCycle = false;
  1.1624 +      if (currentDoc) {
  1.1625 +        // If we can mark the whole document black, no need to optimize
  1.1626 +        // so much, since when the next purple node in the document will be
  1.1627 +        // handled, it is fast to check that the currentDoc is in CCGeneration.
  1.1628 +        break;
  1.1629 +      }
  1.1630 +      // No need to put stuff to the nodesToClear array, if we can clear it
  1.1631 +      // already here.
  1.1632 +      if (node->IsPurple() && (node != aNode || aRemovingAllowed)) {
  1.1633 +        node->RemovePurple();
  1.1634 +      }
  1.1635 +      MarkNodeChildren(node);
  1.1636 +    } else {
  1.1637 +      domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree();
  1.1638 +      if (ShouldClearPurple(node)) {
  1.1639 +        // Collect interesting nodes which we can clear if we find that
  1.1640 +        // they are kept alive in a black tree or are in a DOM-only cycle.
  1.1641 +        nodesToClear.AppendElement(node);
  1.1642 +      }
  1.1643 +    }
  1.1644 +  }
  1.1645 +
  1.1646 +  if (!currentDoc || !foundBlack) { 
  1.1647 +    root->SetIsPurpleRoot(true);
  1.1648 +    if (domOnlyCycle) {
  1.1649 +      if (!gNodesToUnbind) {
  1.1650 +        gNodesToUnbind = new nsAutoTArray<nsIContent*, 1020>();
  1.1651 +      }
  1.1652 +      gNodesToUnbind->AppendElement(static_cast<nsIContent*>(root));
  1.1653 +      for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
  1.1654 +        nsIContent* n = nodesToClear[i];
  1.1655 +        if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
  1.1656 +          n->RemovePurple();
  1.1657 +        }
  1.1658 +      }
  1.1659 +      return true;
  1.1660 +    } else {
  1.1661 +      if (!gPurpleRoots) {
  1.1662 +        gPurpleRoots = new nsAutoTArray<nsINode*, 1020>();
  1.1663 +      }
  1.1664 +      gPurpleRoots->AppendElement(root);
  1.1665 +    }
  1.1666 +  }
  1.1667 +
  1.1668 +  if (!foundBlack) {
  1.1669 +    return false;
  1.1670 +  }
  1.1671 +
  1.1672 +  if (currentDoc) {
  1.1673 +    // Special case documents. If we know the document is black,
  1.1674 +    // we can mark the document to be in CCGeneration.
  1.1675 +    currentDoc->
  1.1676 +      MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
  1.1677 +    MarkNodeChildren(currentDoc);
  1.1678 +  }
  1.1679 +
  1.1680 +  // Subtree is black, so we can remove purple nodes from
  1.1681 +  // purple buffer and mark stuff that to be certainly alive.
  1.1682 +  for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
  1.1683 +    nsIContent* n = nodesToClear[i];
  1.1684 +    MarkNodeChildren(n);
  1.1685 +    // Can't remove currently handled purple node,
  1.1686 +    // unless aRemovingAllowed is true. 
  1.1687 +    if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
  1.1688 +      n->RemovePurple();
  1.1689 +    }
  1.1690 +  }
  1.1691 +  return true;
  1.1692 +}
  1.1693 +
  1.1694 +bool
  1.1695 +FragmentOrElement::CanSkipThis(nsINode* aNode)
  1.1696 +{
  1.1697 +  if (nsCCUncollectableMarker::sGeneration == 0) {
  1.1698 +    return false;
  1.1699 +  }
  1.1700 +  if (aNode->IsBlack()) {
  1.1701 +    return true;
  1.1702 +  }
  1.1703 +  nsIDocument* c = aNode->GetCurrentDoc();
  1.1704 +  return 
  1.1705 +    ((c && nsCCUncollectableMarker::InGeneration(c->GetMarkedCCGeneration())) ||
  1.1706 +     aNode->InCCBlackTree()) && !NeedsScriptTraverse(aNode);
  1.1707 +}
  1.1708 +
  1.1709 +void
  1.1710 +FragmentOrElement::InitCCCallbacks()
  1.1711 +{
  1.1712 +  nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData);
  1.1713 +  nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
  1.1714 +}
  1.1715 +
  1.1716 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement)
  1.1717 +  return FragmentOrElement::CanSkip(tmp, aRemovingAllowed);
  1.1718 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
  1.1719 +
  1.1720 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement)
  1.1721 +  return FragmentOrElement::CanSkipInCC(tmp);
  1.1722 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
  1.1723 +
  1.1724 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement)
  1.1725 +  return FragmentOrElement::CanSkipThis(tmp);
  1.1726 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
  1.1727 +
  1.1728 +static const char* kNSURIs[] = {
  1.1729 +  " ([none])",
  1.1730 +  " (xmlns)",
  1.1731 +  " (xml)",
  1.1732 +  " (xhtml)",
  1.1733 +  " (XLink)",
  1.1734 +  " (XSLT)",
  1.1735 +  " (XBL)",
  1.1736 +  " (MathML)",
  1.1737 +  " (RDF)",
  1.1738 +  " (XUL)",
  1.1739 +  " (SVG)",
  1.1740 +  " (XML Events)"
  1.1741 +};
  1.1742 +
  1.1743 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
  1.1744 +  if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
  1.1745 +    char name[512];
  1.1746 +    uint32_t nsid = tmp->GetNameSpaceID();
  1.1747 +    nsAtomCString localName(tmp->NodeInfo()->NameAtom());
  1.1748 +    nsAutoCString uri;
  1.1749 +    if (tmp->OwnerDoc()->GetDocumentURI()) {
  1.1750 +      tmp->OwnerDoc()->GetDocumentURI()->GetSpec(uri);
  1.1751 +    }
  1.1752 +
  1.1753 +    nsAutoString id;
  1.1754 +    nsIAtom* idAtom = tmp->GetID();
  1.1755 +    if (idAtom) {
  1.1756 +      id.AppendLiteral(" id='");
  1.1757 +      id.Append(nsDependentAtomString(idAtom));
  1.1758 +      id.AppendLiteral("'");
  1.1759 +    }
  1.1760 +
  1.1761 +    nsAutoString classes;
  1.1762 +    const nsAttrValue* classAttrValue = tmp->GetClasses();
  1.1763 +    if (classAttrValue) {
  1.1764 +      classes.AppendLiteral(" class='");
  1.1765 +      nsAutoString classString;
  1.1766 +      classAttrValue->ToString(classString);
  1.1767 +      classString.ReplaceChar(char16_t('\n'), char16_t(' '));
  1.1768 +      classes.Append(classString);
  1.1769 +      classes.AppendLiteral("'");
  1.1770 +    }
  1.1771 +
  1.1772 +    nsAutoCString orphan;
  1.1773 +    if (!tmp->IsInDoc() &&
  1.1774 +        // Ignore xbl:content, which is never in the document and hence always
  1.1775 +        // appears to be orphaned.
  1.1776 +        !tmp->NodeInfo()->Equals(nsGkAtoms::content, kNameSpaceID_XBL)) {
  1.1777 +      orphan.AppendLiteral(" (orphan)");
  1.1778 +    }
  1.1779 +
  1.1780 +    const char* nsuri = nsid < ArrayLength(kNSURIs) ? kNSURIs[nsid] : "";
  1.1781 +    PR_snprintf(name, sizeof(name), "FragmentOrElement%s %s%s%s%s %s",
  1.1782 +                nsuri,
  1.1783 +                localName.get(),
  1.1784 +                NS_ConvertUTF16toUTF8(id).get(),
  1.1785 +                NS_ConvertUTF16toUTF8(classes).get(),
  1.1786 +                orphan.get(),
  1.1787 +                uri.get());
  1.1788 +    cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
  1.1789 +  }
  1.1790 +  else {
  1.1791 +    NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get())
  1.1792 +  }
  1.1793 +
  1.1794 +  // Always need to traverse script objects, so do that before we check
  1.1795 +  // if we're uncollectable.
  1.1796 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
  1.1797 +
  1.1798 +  if (!nsINode::Traverse(tmp, cb)) {
  1.1799 +    return NS_SUCCESS_INTERRUPTED_TRAVERSE;
  1.1800 +  }
  1.1801 +
  1.1802 +  tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb);
  1.1803 +
  1.1804 +  if (tmp->HasProperties()) {
  1.1805 +    if (tmp->IsHTML()) {
  1.1806 +      nsIAtom*** props = nsGenericHTMLElement::PropertiesToTraverseAndUnlink();
  1.1807 +      for (uint32_t i = 0; props[i]; ++i) {
  1.1808 +        nsISupports* property =
  1.1809 +          static_cast<nsISupports*>(tmp->GetProperty(*props[i]));
  1.1810 +        cb.NoteXPCOMChild(property);
  1.1811 +      }
  1.1812 +    }
  1.1813 +  }
  1.1814 +
  1.1815 +  // Traverse attribute names and child content.
  1.1816 +  {
  1.1817 +    uint32_t i;
  1.1818 +    uint32_t attrs = tmp->mAttrsAndChildren.AttrCount();
  1.1819 +    for (i = 0; i < attrs; i++) {
  1.1820 +      const nsAttrName* name = tmp->mAttrsAndChildren.AttrNameAt(i);
  1.1821 +      if (!name->IsAtom()) {
  1.1822 +        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
  1.1823 +                                           "mAttrsAndChildren[i]->NodeInfo()");
  1.1824 +        cb.NoteXPCOMChild(name->NodeInfo());
  1.1825 +      }
  1.1826 +    }
  1.1827 +
  1.1828 +    uint32_t kids = tmp->mAttrsAndChildren.ChildCount();
  1.1829 +    for (i = 0; i < kids; i++) {
  1.1830 +      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrsAndChildren[i]");
  1.1831 +      cb.NoteXPCOMChild(tmp->mAttrsAndChildren.GetSafeChildAt(i));
  1.1832 +    }
  1.1833 +  }
  1.1834 +
  1.1835 +  // Traverse any DOM slots of interest.
  1.1836 +  {
  1.1837 +    nsDOMSlots *slots = tmp->GetExistingDOMSlots();
  1.1838 +    if (slots) {
  1.1839 +      slots->Traverse(cb, tmp->IsXUL());
  1.1840 +    }
  1.1841 +  }
  1.1842 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  1.1843 +
  1.1844 +
  1.1845 +NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
  1.1846 +  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  1.1847 +  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
  1.1848 +  NS_INTERFACE_MAP_ENTRY(Element)
  1.1849 +  NS_INTERFACE_MAP_ENTRY(nsIContent)
  1.1850 +  NS_INTERFACE_MAP_ENTRY(nsINode)
  1.1851 +  NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
  1.1852 +  NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
  1.1853 +  NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
  1.1854 +                                 new nsNodeSupportsWeakRefTearoff(this))
  1.1855 +  NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver,
  1.1856 +                                 new nsNode3Tearoff(this))
  1.1857 +  // DOM bindings depend on the identity pointer being the
  1.1858 +  // same as nsINode (which nsIContent inherits).
  1.1859 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
  1.1860 +NS_INTERFACE_MAP_END
  1.1861 +
  1.1862 +NS_IMPL_CYCLE_COLLECTING_ADDREF(FragmentOrElement)
  1.1863 +NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FragmentOrElement,
  1.1864 +                                                   nsNodeUtils::LastRelease(this))
  1.1865 +
  1.1866 +//----------------------------------------------------------------------
  1.1867 +
  1.1868 +nsresult
  1.1869 +FragmentOrElement::CopyInnerTo(FragmentOrElement* aDst)
  1.1870 +{
  1.1871 +  uint32_t i, count = mAttrsAndChildren.AttrCount();
  1.1872 +  for (i = 0; i < count; ++i) {
  1.1873 +    const nsAttrName* name = mAttrsAndChildren.AttrNameAt(i);
  1.1874 +    const nsAttrValue* value = mAttrsAndChildren.AttrAt(i);
  1.1875 +    nsAutoString valStr;
  1.1876 +    value->ToString(valStr);
  1.1877 +    nsresult rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(),
  1.1878 +                                name->GetPrefix(), valStr, false);
  1.1879 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1880 +  }
  1.1881 +
  1.1882 +  return NS_OK;
  1.1883 +}
  1.1884 +
  1.1885 +const nsTextFragment*
  1.1886 +FragmentOrElement::GetText()
  1.1887 +{
  1.1888 +  return nullptr;
  1.1889 +}
  1.1890 +
  1.1891 +uint32_t
  1.1892 +FragmentOrElement::TextLength() const
  1.1893 +{
  1.1894 +  // We can remove this assertion if it turns out to be useful to be able
  1.1895 +  // to depend on this returning 0
  1.1896 +  NS_NOTREACHED("called FragmentOrElement::TextLength");
  1.1897 +
  1.1898 +  return 0;
  1.1899 +}
  1.1900 +
  1.1901 +nsresult
  1.1902 +FragmentOrElement::SetText(const char16_t* aBuffer, uint32_t aLength,
  1.1903 +                          bool aNotify)
  1.1904 +{
  1.1905 +  NS_ERROR("called FragmentOrElement::SetText");
  1.1906 +
  1.1907 +  return NS_ERROR_FAILURE;
  1.1908 +}
  1.1909 +
  1.1910 +nsresult
  1.1911 +FragmentOrElement::AppendText(const char16_t* aBuffer, uint32_t aLength,
  1.1912 +                             bool aNotify)
  1.1913 +{
  1.1914 +  NS_ERROR("called FragmentOrElement::AppendText");
  1.1915 +
  1.1916 +  return NS_ERROR_FAILURE;
  1.1917 +}
  1.1918 +
  1.1919 +bool
  1.1920 +FragmentOrElement::TextIsOnlyWhitespace()
  1.1921 +{
  1.1922 +  return false;
  1.1923 +}
  1.1924 +
  1.1925 +bool
  1.1926 +FragmentOrElement::HasTextForTranslation()
  1.1927 +{
  1.1928 +  return false;
  1.1929 +}
  1.1930 +
  1.1931 +void
  1.1932 +FragmentOrElement::AppendTextTo(nsAString& aResult)
  1.1933 +{
  1.1934 +  // We can remove this assertion if it turns out to be useful to be able
  1.1935 +  // to depend on this appending nothing.
  1.1936 +  NS_NOTREACHED("called FragmentOrElement::TextLength");
  1.1937 +}
  1.1938 +
  1.1939 +bool
  1.1940 +FragmentOrElement::AppendTextTo(nsAString& aResult, const mozilla::fallible_t&)
  1.1941 +{
  1.1942 +  // We can remove this assertion if it turns out to be useful to be able
  1.1943 +  // to depend on this appending nothing.
  1.1944 +  NS_NOTREACHED("called FragmentOrElement::TextLength");
  1.1945 +
  1.1946 +  return false;
  1.1947 +}
  1.1948 +
  1.1949 +uint32_t
  1.1950 +FragmentOrElement::GetChildCount() const
  1.1951 +{
  1.1952 +  return mAttrsAndChildren.ChildCount();
  1.1953 +}
  1.1954 +
  1.1955 +nsIContent *
  1.1956 +FragmentOrElement::GetChildAt(uint32_t aIndex) const
  1.1957 +{
  1.1958 +  return mAttrsAndChildren.GetSafeChildAt(aIndex);
  1.1959 +}
  1.1960 +
  1.1961 +nsIContent * const *
  1.1962 +FragmentOrElement::GetChildArray(uint32_t* aChildCount) const
  1.1963 +{
  1.1964 +  return mAttrsAndChildren.GetChildArray(aChildCount);
  1.1965 +}
  1.1966 +
  1.1967 +int32_t
  1.1968 +FragmentOrElement::IndexOf(const nsINode* aPossibleChild) const
  1.1969 +{
  1.1970 +  return mAttrsAndChildren.IndexOfChild(aPossibleChild);
  1.1971 +}
  1.1972 +
  1.1973 +// Try to keep the size of StringBuilder close to a jemalloc bucket size.
  1.1974 +#define STRING_BUFFER_UNITS 1020
  1.1975 +
  1.1976 +namespace {
  1.1977 +
  1.1978 +// We put StringBuilder in the anonymous namespace to prevent anything outside
  1.1979 +// this file from accidentally being linked against it.
  1.1980 +
  1.1981 +class StringBuilder
  1.1982 +{
  1.1983 +private:
  1.1984 +  class Unit
  1.1985 +  {
  1.1986 +  public:
  1.1987 +    Unit() : mAtom(nullptr), mType(eUnknown), mLength(0)
  1.1988 +    {
  1.1989 +      MOZ_COUNT_CTOR(StringBuilder::Unit);
  1.1990 +    }
  1.1991 +    ~Unit()
  1.1992 +    {
  1.1993 +      if (mType == eString || mType == eStringWithEncode) {
  1.1994 +        delete mString;
  1.1995 +      }
  1.1996 +      MOZ_COUNT_DTOR(StringBuilder::Unit);
  1.1997 +    }
  1.1998 +
  1.1999 +    enum Type
  1.2000 +    {
  1.2001 +      eUnknown,
  1.2002 +      eAtom,
  1.2003 +      eString,
  1.2004 +      eStringWithEncode,
  1.2005 +      eLiteral,
  1.2006 +      eTextFragment,
  1.2007 +      eTextFragmentWithEncode,
  1.2008 +    };
  1.2009 +
  1.2010 +    union
  1.2011 +    {
  1.2012 +      nsIAtom*              mAtom;
  1.2013 +      const char*           mLiteral;
  1.2014 +      nsAutoString*         mString;
  1.2015 +      const nsTextFragment* mTextFragment;
  1.2016 +    };
  1.2017 +    Type     mType;
  1.2018 +    uint32_t mLength;
  1.2019 +  };
  1.2020 +public:
  1.2021 +  StringBuilder() : mLast(MOZ_THIS_IN_INITIALIZER_LIST()), mLength(0)
  1.2022 +  {
  1.2023 +    MOZ_COUNT_CTOR(StringBuilder);
  1.2024 +  }
  1.2025 +
  1.2026 +  ~StringBuilder()
  1.2027 +  {
  1.2028 +    MOZ_COUNT_DTOR(StringBuilder);
  1.2029 +  }
  1.2030 +
  1.2031 +  void Append(nsIAtom* aAtom)
  1.2032 +  {
  1.2033 +    Unit* u = AddUnit();
  1.2034 +    u->mAtom = aAtom;
  1.2035 +    u->mType = Unit::eAtom;
  1.2036 +    uint32_t len = aAtom->GetLength();
  1.2037 +    u->mLength = len;
  1.2038 +    mLength += len;
  1.2039 +  }
  1.2040 +
  1.2041 +  template<int N>
  1.2042 +  void Append(const char (&aLiteral)[N])
  1.2043 +  {
  1.2044 +    Unit* u = AddUnit();
  1.2045 +    u->mLiteral = aLiteral;
  1.2046 +    u->mType = Unit::eLiteral;
  1.2047 +    uint32_t len = N - 1;
  1.2048 +    u->mLength = len;
  1.2049 +    mLength += len;
  1.2050 +  }
  1.2051 +
  1.2052 +  template<int N>
  1.2053 +  void Append(char (&aLiteral)[N])
  1.2054 +  {
  1.2055 +    Unit* u = AddUnit();
  1.2056 +    u->mLiteral = aLiteral;
  1.2057 +    u->mType = Unit::eLiteral;
  1.2058 +    uint32_t len = N - 1;
  1.2059 +    u->mLength = len;
  1.2060 +    mLength += len;
  1.2061 +  }
  1.2062 +
  1.2063 +  void Append(const nsAString& aString)
  1.2064 +  {
  1.2065 +    Unit* u = AddUnit();
  1.2066 +    u->mString = new nsAutoString(aString);
  1.2067 +    u->mType = Unit::eString;
  1.2068 +    uint32_t len = aString.Length();
  1.2069 +    u->mLength = len;
  1.2070 +    mLength += len;
  1.2071 +  }
  1.2072 +
  1.2073 +  void Append(nsAutoString* aString)
  1.2074 +  {
  1.2075 +    Unit* u = AddUnit();
  1.2076 +    u->mString = aString;
  1.2077 +    u->mType = Unit::eString;
  1.2078 +    uint32_t len = aString->Length();
  1.2079 +    u->mLength = len;
  1.2080 +    mLength += len;
  1.2081 +  }
  1.2082 +
  1.2083 +  void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen)
  1.2084 +  {
  1.2085 +    Unit* u = AddUnit();
  1.2086 +    u->mString = aString;
  1.2087 +    u->mType = Unit::eStringWithEncode;
  1.2088 +    u->mLength = aLen;
  1.2089 +    mLength += aLen;
  1.2090 +  }
  1.2091 +
  1.2092 +  void Append(const nsTextFragment* aTextFragment)
  1.2093 +  {
  1.2094 +    Unit* u = AddUnit();
  1.2095 +    u->mTextFragment = aTextFragment;
  1.2096 +    u->mType = Unit::eTextFragment;
  1.2097 +    uint32_t len = aTextFragment->GetLength();
  1.2098 +    u->mLength = len;
  1.2099 +    mLength += len;
  1.2100 +  }
  1.2101 +
  1.2102 +  void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen)
  1.2103 +  {
  1.2104 +    Unit* u = AddUnit();
  1.2105 +    u->mTextFragment = aTextFragment;
  1.2106 +    u->mType = Unit::eTextFragmentWithEncode;
  1.2107 +    u->mLength = aLen;
  1.2108 +    mLength += aLen;
  1.2109 +  }
  1.2110 +
  1.2111 +  bool ToString(nsAString& aOut)
  1.2112 +  {
  1.2113 +    if (!aOut.SetCapacity(mLength, fallible_t())) {
  1.2114 +      return false;
  1.2115 +    }
  1.2116 +
  1.2117 +    for (StringBuilder* current = this; current; current = current->mNext) {
  1.2118 +      uint32_t len = current->mUnits.Length();
  1.2119 +      for (uint32_t i = 0; i < len; ++i) {
  1.2120 +        Unit& u = current->mUnits[i];
  1.2121 +        switch (u.mType) {
  1.2122 +          case Unit::eAtom:
  1.2123 +            aOut.Append(nsDependentAtomString(u.mAtom));
  1.2124 +            break;
  1.2125 +          case Unit::eString:
  1.2126 +            aOut.Append(*(u.mString));
  1.2127 +            break;
  1.2128 +          case Unit::eStringWithEncode:
  1.2129 +            EncodeAttrString(*(u.mString), aOut);
  1.2130 +            break;
  1.2131 +          case Unit::eLiteral:
  1.2132 +            aOut.AppendASCII(u.mLiteral, u.mLength);
  1.2133 +            break;
  1.2134 +          case Unit::eTextFragment:
  1.2135 +            u.mTextFragment->AppendTo(aOut);
  1.2136 +            break;
  1.2137 +          case Unit::eTextFragmentWithEncode:
  1.2138 +            EncodeTextFragment(u.mTextFragment, aOut);
  1.2139 +            break;
  1.2140 +          default:
  1.2141 +            MOZ_CRASH("Unknown unit type?");
  1.2142 +        }
  1.2143 +      }
  1.2144 +    }
  1.2145 +    return true;
  1.2146 +  }
  1.2147 +private:
  1.2148 +  Unit* AddUnit()
  1.2149 +  {
  1.2150 +    if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
  1.2151 +      new StringBuilder(this);
  1.2152 +    }
  1.2153 +    return mLast->mUnits.AppendElement();
  1.2154 +  }
  1.2155 +
  1.2156 +  StringBuilder(StringBuilder* aFirst)
  1.2157 +  : mLast(nullptr), mLength(0)
  1.2158 +  {
  1.2159 +    MOZ_COUNT_CTOR(StringBuilder);
  1.2160 +    aFirst->mLast->mNext = this;
  1.2161 +    aFirst->mLast = this;
  1.2162 +  }
  1.2163 +
  1.2164 +  void EncodeAttrString(const nsAutoString& aValue, nsAString& aOut)
  1.2165 +  {
  1.2166 +    const char16_t* c = aValue.BeginReading();
  1.2167 +    const char16_t* end = aValue.EndReading();
  1.2168 +    while (c < end) {
  1.2169 +      switch (*c) {
  1.2170 +      case '"':
  1.2171 +        aOut.AppendLiteral("&quot;");
  1.2172 +        break;
  1.2173 +      case '&':
  1.2174 +        aOut.AppendLiteral("&amp;");
  1.2175 +        break;
  1.2176 +      case 0x00A0:
  1.2177 +        aOut.AppendLiteral("&nbsp;");
  1.2178 +        break;
  1.2179 +      default:
  1.2180 +        aOut.Append(*c);
  1.2181 +        break;
  1.2182 +      }
  1.2183 +      ++c;
  1.2184 +    }
  1.2185 +  }
  1.2186 +
  1.2187 +  void EncodeTextFragment(const nsTextFragment* aValue, nsAString& aOut)
  1.2188 +  {
  1.2189 +    uint32_t len = aValue->GetLength();
  1.2190 +    if (aValue->Is2b()) {
  1.2191 +      const char16_t* data = aValue->Get2b();
  1.2192 +      for (uint32_t i = 0; i < len; ++i) {
  1.2193 +        const char16_t c = data[i];
  1.2194 +        switch (c) {
  1.2195 +          case '<':
  1.2196 +            aOut.AppendLiteral("&lt;");
  1.2197 +            break;
  1.2198 +          case '>':
  1.2199 +            aOut.AppendLiteral("&gt;");
  1.2200 +            break;
  1.2201 +          case '&':
  1.2202 +            aOut.AppendLiteral("&amp;");
  1.2203 +            break;
  1.2204 +          case 0x00A0:
  1.2205 +            aOut.AppendLiteral("&nbsp;");
  1.2206 +            break;
  1.2207 +          default:
  1.2208 +            aOut.Append(c);
  1.2209 +            break;
  1.2210 +        }
  1.2211 +      }
  1.2212 +    } else {
  1.2213 +      const char* data = aValue->Get1b();
  1.2214 +      for (uint32_t i = 0; i < len; ++i) {
  1.2215 +        const unsigned char c = data[i];
  1.2216 +        switch (c) {
  1.2217 +          case '<':
  1.2218 +            aOut.AppendLiteral("&lt;");
  1.2219 +            break;
  1.2220 +          case '>':
  1.2221 +            aOut.AppendLiteral("&gt;");
  1.2222 +            break;
  1.2223 +          case '&':
  1.2224 +            aOut.AppendLiteral("&amp;");
  1.2225 +            break;
  1.2226 +          case 0x00A0:
  1.2227 +            aOut.AppendLiteral("&nbsp;");
  1.2228 +            break;
  1.2229 +          default:
  1.2230 +            aOut.Append(c);
  1.2231 +            break;
  1.2232 +        }
  1.2233 +      }
  1.2234 +    }
  1.2235 +  }
  1.2236 +
  1.2237 +  nsAutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
  1.2238 +  nsAutoPtr<StringBuilder>                mNext;
  1.2239 +  StringBuilder*                          mLast;
  1.2240 +  // mLength is used only in the first StringBuilder object in the linked list.
  1.2241 +  uint32_t                                mLength;
  1.2242 +};
  1.2243 +
  1.2244 +} // anonymous namespace
  1.2245 +
  1.2246 +static void
  1.2247 +AppendEncodedCharacters(const nsTextFragment* aText, StringBuilder& aBuilder)
  1.2248 +{
  1.2249 +  uint32_t extraSpaceNeeded = 0;
  1.2250 +  uint32_t len = aText->GetLength();
  1.2251 +  if (aText->Is2b()) {
  1.2252 +    const char16_t* data = aText->Get2b();
  1.2253 +    for (uint32_t i = 0; i < len; ++i) {
  1.2254 +      const char16_t c = data[i];
  1.2255 +      switch (c) {
  1.2256 +        case '<':
  1.2257 +          extraSpaceNeeded += ArrayLength("&lt;") - 2;
  1.2258 +          break;
  1.2259 +        case '>':
  1.2260 +          extraSpaceNeeded += ArrayLength("&gt;") - 2;
  1.2261 +          break;
  1.2262 +        case '&':
  1.2263 +          extraSpaceNeeded += ArrayLength("&amp;") - 2;
  1.2264 +          break;
  1.2265 +        case 0x00A0:
  1.2266 +          extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
  1.2267 +          break;
  1.2268 +        default:
  1.2269 +          break;
  1.2270 +      }
  1.2271 +    }
  1.2272 +  } else {
  1.2273 +    const char* data = aText->Get1b();
  1.2274 +    for (uint32_t i = 0; i < len; ++i) {
  1.2275 +      const unsigned char c = data[i];
  1.2276 +      switch (c) {
  1.2277 +        case '<':
  1.2278 +          extraSpaceNeeded += ArrayLength("&lt;") - 2;
  1.2279 +          break;
  1.2280 +        case '>':
  1.2281 +          extraSpaceNeeded += ArrayLength("&gt;") - 2;
  1.2282 +          break;
  1.2283 +        case '&':
  1.2284 +          extraSpaceNeeded += ArrayLength("&amp;") - 2;
  1.2285 +          break;
  1.2286 +        case 0x00A0:
  1.2287 +          extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
  1.2288 +          break;
  1.2289 +        default:
  1.2290 +          break;
  1.2291 +      }
  1.2292 +    }
  1.2293 +  }
  1.2294 +
  1.2295 +  if (extraSpaceNeeded) {
  1.2296 +    aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
  1.2297 +  } else {
  1.2298 +    aBuilder.Append(aText);
  1.2299 +  }
  1.2300 +}
  1.2301 +
  1.2302 +static void
  1.2303 +AppendEncodedAttributeValue(nsAutoString* aValue, StringBuilder& aBuilder)
  1.2304 +{
  1.2305 +  const char16_t* c = aValue->BeginReading();
  1.2306 +  const char16_t* end = aValue->EndReading();
  1.2307 +
  1.2308 +  uint32_t extraSpaceNeeded = 0;
  1.2309 +  while (c < end) {
  1.2310 +    switch (*c) {
  1.2311 +      case '"':
  1.2312 +        extraSpaceNeeded += ArrayLength("&quot;") - 2;
  1.2313 +        break;
  1.2314 +      case '&':
  1.2315 +        extraSpaceNeeded += ArrayLength("&amp;") - 2;
  1.2316 +        break;
  1.2317 +      case 0x00A0:
  1.2318 +        extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
  1.2319 +        break;
  1.2320 +      default:
  1.2321 +        break;
  1.2322 +    }
  1.2323 +    ++c;
  1.2324 +  }
  1.2325 +
  1.2326 +  if (extraSpaceNeeded) {
  1.2327 +    aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
  1.2328 +  } else {
  1.2329 +    aBuilder.Append(aValue);
  1.2330 +  }
  1.2331 +}
  1.2332 +
  1.2333 +static void
  1.2334 +StartElement(Element* aContent, StringBuilder& aBuilder)
  1.2335 +{
  1.2336 +  nsIAtom* localName = aContent->Tag();
  1.2337 +  int32_t tagNS = aContent->GetNameSpaceID();
  1.2338 +
  1.2339 +  aBuilder.Append("<");
  1.2340 +  if (aContent->IsHTML() || aContent->IsSVG() || aContent->IsMathML()) {
  1.2341 +    aBuilder.Append(localName);
  1.2342 +  } else {
  1.2343 +    aBuilder.Append(aContent->NodeName());
  1.2344 +  }
  1.2345 +
  1.2346 +  int32_t count = aContent->GetAttrCount();
  1.2347 +  for (int32_t i = count; i > 0;) {
  1.2348 +    --i;
  1.2349 +    const nsAttrName* name = aContent->GetAttrNameAt(i);
  1.2350 +    int32_t attNs = name->NamespaceID();
  1.2351 +    nsIAtom* attName = name->LocalName();
  1.2352 +
  1.2353 +    // Filter out any attribute starting with [-|_]moz
  1.2354 +    nsDependentAtomString attrNameStr(attName);
  1.2355 +    if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
  1.2356 +        StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
  1.2357 +      continue;
  1.2358 +    }
  1.2359 +
  1.2360 +    nsAutoString* attValue = new nsAutoString();
  1.2361 +    aContent->GetAttr(attNs, attName, *attValue);
  1.2362 +
  1.2363 +    // Filter out special case of <br type="_moz*"> used by the editor.
  1.2364 +    // Bug 16988.  Yuck.
  1.2365 +    if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
  1.2366 +        attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
  1.2367 +        StringBeginsWith(*attValue, NS_LITERAL_STRING("_moz"))) {
  1.2368 +      delete attValue;
  1.2369 +      continue;
  1.2370 +    }
  1.2371 +    
  1.2372 +    aBuilder.Append(" ");
  1.2373 +
  1.2374 +    if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
  1.2375 +        (attNs == kNameSpaceID_XMLNS &&
  1.2376 +         attName == nsGkAtoms::xmlns)) {
  1.2377 +      // Nothing else required
  1.2378 +    } else if (attNs == kNameSpaceID_XML) {
  1.2379 +      aBuilder.Append("xml:");
  1.2380 +    } else if (attNs == kNameSpaceID_XMLNS) {
  1.2381 +      aBuilder.Append("xmlns:");
  1.2382 +    } else if (attNs == kNameSpaceID_XLink) {
  1.2383 +      aBuilder.Append("xlink:");
  1.2384 +    } else {
  1.2385 +      nsIAtom* prefix = name->GetPrefix();
  1.2386 +      if (prefix) {
  1.2387 +        aBuilder.Append(prefix);
  1.2388 +        aBuilder.Append(":");
  1.2389 +      }
  1.2390 +    }
  1.2391 +
  1.2392 +    aBuilder.Append(attName);
  1.2393 +    aBuilder.Append("=\"");
  1.2394 +    AppendEncodedAttributeValue(attValue, aBuilder);
  1.2395 +    aBuilder.Append("\"");
  1.2396 +  }
  1.2397 +
  1.2398 +  aBuilder.Append(">");
  1.2399 +
  1.2400 +  /*
  1.2401 +  // Per HTML spec we should append one \n if the first child of
  1.2402 +  // pre/textarea/listing is a textnode and starts with a \n.
  1.2403 +  // But because browsers haven't traditionally had that behavior,
  1.2404 +  // we're not changing our behavior either - yet.
  1.2405 +  if (aContent->IsHTML()) {
  1.2406 +    if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
  1.2407 +        localName == nsGkAtoms::listing) {
  1.2408 +      nsIContent* fc = aContent->GetFirstChild();
  1.2409 +      if (fc &&
  1.2410 +          (fc->NodeType() == nsIDOMNode::TEXT_NODE ||
  1.2411 +           fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
  1.2412 +        const nsTextFragment* text = fc->GetText();
  1.2413 +        if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
  1.2414 +          aBuilder.Append("\n");
  1.2415 +        }
  1.2416 +      }
  1.2417 +    }
  1.2418 +  }*/
  1.2419 +}
  1.2420 +
  1.2421 +static inline bool
  1.2422 +ShouldEscape(nsIContent* aParent)
  1.2423 +{
  1.2424 +  if (!aParent || !aParent->IsHTML()) {
  1.2425 +    return true;
  1.2426 +  }
  1.2427 +
  1.2428 +  static const nsIAtom* nonEscapingElements[] = {
  1.2429 +    nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
  1.2430 +    nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
  1.2431 +    nsGkAtoms::plaintext, 
  1.2432 +    // Per the current spec noscript should be escaped in case
  1.2433 +    // scripts are disabled or if document doesn't have
  1.2434 +    // browsing context. However the latter seems to be a spec bug
  1.2435 +    // and Gecko hasn't traditionally done the former.
  1.2436 +    nsGkAtoms::noscript    
  1.2437 +  };
  1.2438 +  static mozilla::BloomFilter<12, nsIAtom> sFilter;
  1.2439 +  static bool sInitialized = false;
  1.2440 +  if (!sInitialized) {
  1.2441 +    sInitialized = true;
  1.2442 +    for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
  1.2443 +      sFilter.add(nonEscapingElements[i]);
  1.2444 +    }
  1.2445 +  }
  1.2446 +
  1.2447 +  nsIAtom* tag = aParent->Tag();
  1.2448 +  if (sFilter.mightContain(tag)) {
  1.2449 +    for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
  1.2450 +      if (tag == nonEscapingElements[i]) {
  1.2451 +        return false;
  1.2452 +      }
  1.2453 +    }
  1.2454 +  }
  1.2455 +  return true;
  1.2456 +}
  1.2457 +
  1.2458 +static inline bool
  1.2459 +IsVoidTag(Element* aElement)
  1.2460 +{
  1.2461 +  if (!aElement->IsHTML()) {
  1.2462 +    return false;
  1.2463 +  }
  1.2464 +
  1.2465 +  static const nsIAtom* voidElements[] = {
  1.2466 +    nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
  1.2467 +    nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
  1.2468 +    nsGkAtoms::command, nsGkAtoms::embed, nsGkAtoms::frame,
  1.2469 +    nsGkAtoms::hr, nsGkAtoms::img, nsGkAtoms::input,
  1.2470 +    nsGkAtoms::keygen, nsGkAtoms::link, nsGkAtoms::meta,
  1.2471 +    nsGkAtoms::param, nsGkAtoms::source, nsGkAtoms::track,
  1.2472 +    nsGkAtoms::wbr
  1.2473 +  };
  1.2474 +
  1.2475 +  static mozilla::BloomFilter<12, nsIAtom> sFilter;
  1.2476 +  static bool sInitialized = false;
  1.2477 +  if (!sInitialized) {
  1.2478 +    sInitialized = true;
  1.2479 +    for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
  1.2480 +      sFilter.add(voidElements[i]);
  1.2481 +    }
  1.2482 +  }
  1.2483 +  
  1.2484 +  nsIAtom* tag = aElement->Tag();
  1.2485 +  if (sFilter.mightContain(tag)) {
  1.2486 +    for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
  1.2487 +      if (tag == voidElements[i]) {
  1.2488 +        return true;
  1.2489 +      }
  1.2490 +    }
  1.2491 +  }
  1.2492 +  return false;
  1.2493 +}
  1.2494 +
  1.2495 +static bool
  1.2496 +Serialize(FragmentOrElement* aRoot, bool aDescendentsOnly, nsAString& aOut)
  1.2497 +{
  1.2498 +  nsINode* current = aDescendentsOnly ?
  1.2499 +    nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
  1.2500 +
  1.2501 +  if (!current) {
  1.2502 +    return true;
  1.2503 +  }
  1.2504 +
  1.2505 +  StringBuilder builder;
  1.2506 +  nsIContent* next;
  1.2507 +  while (true) {
  1.2508 +    bool isVoid = false;
  1.2509 +    switch (current->NodeType()) {
  1.2510 +      case nsIDOMNode::ELEMENT_NODE: {
  1.2511 +        Element* elem = current->AsElement();
  1.2512 +        StartElement(elem, builder);
  1.2513 +        isVoid = IsVoidTag(elem);
  1.2514 +        if (!isVoid &&
  1.2515 +            (next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
  1.2516 +          current = next;
  1.2517 +          continue;
  1.2518 +        }
  1.2519 +        break;
  1.2520 +      }
  1.2521 +
  1.2522 +      case nsIDOMNode::TEXT_NODE:
  1.2523 +      case nsIDOMNode::CDATA_SECTION_NODE: {
  1.2524 +        const nsTextFragment* text = static_cast<nsIContent*>(current)->GetText();
  1.2525 +        nsIContent* parent = current->GetParent();
  1.2526 +        if (ShouldEscape(parent)) {
  1.2527 +          AppendEncodedCharacters(text, builder);
  1.2528 +        } else {
  1.2529 +          builder.Append(text);
  1.2530 +        }
  1.2531 +        break;
  1.2532 +      }
  1.2533 +
  1.2534 +      case nsIDOMNode::COMMENT_NODE: {
  1.2535 +        builder.Append("<!--");
  1.2536 +        builder.Append(static_cast<nsIContent*>(current)->GetText());
  1.2537 +        builder.Append("-->");
  1.2538 +        break;
  1.2539 +      }
  1.2540 +
  1.2541 +      case nsIDOMNode::DOCUMENT_TYPE_NODE: {
  1.2542 +        builder.Append("<!DOCTYPE ");
  1.2543 +        builder.Append(current->NodeName());
  1.2544 +        builder.Append(">");
  1.2545 +        break;
  1.2546 +      }
  1.2547 +
  1.2548 +      case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: {
  1.2549 +        builder.Append("<?");
  1.2550 +        builder.Append(current->NodeName());
  1.2551 +        builder.Append(" ");
  1.2552 +        builder.Append(static_cast<nsIContent*>(current)->GetText());
  1.2553 +        builder.Append(">");
  1.2554 +        break;
  1.2555 +      }
  1.2556 +    }
  1.2557 +
  1.2558 +    while (true) {
  1.2559 +      if (!isVoid && current->NodeType() == nsIDOMNode::ELEMENT_NODE) {
  1.2560 +        builder.Append("</");
  1.2561 +        nsIContent* elem = static_cast<nsIContent*>(current);
  1.2562 +        if (elem->IsHTML() || elem->IsSVG() || elem->IsMathML()) {
  1.2563 +          builder.Append(elem->Tag());
  1.2564 +        } else {
  1.2565 +          builder.Append(current->NodeName());
  1.2566 +        }
  1.2567 +        builder.Append(">");
  1.2568 +      }
  1.2569 +      isVoid = false;
  1.2570 +
  1.2571 +      if (current == aRoot) {
  1.2572 +        return builder.ToString(aOut);
  1.2573 +      }
  1.2574 +
  1.2575 +      if ((next = current->GetNextSibling())) {
  1.2576 +        current = next;
  1.2577 +        break;
  1.2578 +      }
  1.2579 +
  1.2580 +      current = current->GetParentNode();
  1.2581 +
  1.2582 +      // Template case, if we are in a template's content, then the parent
  1.2583 +      // should be the host template element.
  1.2584 +      if (current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
  1.2585 +        DocumentFragment* frag = static_cast<DocumentFragment*>(current);
  1.2586 +        nsIContent* fragHost = frag->GetHost();
  1.2587 +        if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
  1.2588 +          current = fragHost;
  1.2589 +        }
  1.2590 +      }
  1.2591 +
  1.2592 +      if (aDescendentsOnly && current == aRoot) {
  1.2593 +        return builder.ToString(aOut);
  1.2594 +      }
  1.2595 +    }
  1.2596 +  }
  1.2597 +}
  1.2598 +
  1.2599 +void
  1.2600 +FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
  1.2601 +{
  1.2602 +  aMarkup.Truncate();
  1.2603 +
  1.2604 +  nsIDocument* doc = OwnerDoc();
  1.2605 +  if (IsInHTMLDocument()) {
  1.2606 +    Serialize(this, !aIncludeSelf, aMarkup);
  1.2607 +    return;
  1.2608 +  }
  1.2609 +
  1.2610 +  nsAutoString contentType;
  1.2611 +  doc->GetContentType(contentType);
  1.2612 +  bool tryToCacheEncoder = !aIncludeSelf;
  1.2613 +
  1.2614 +  nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
  1.2615 +  if (!docEncoder) {
  1.2616 +    docEncoder =
  1.2617 +      do_CreateInstance(PromiseFlatCString(
  1.2618 +        nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
  1.2619 +        NS_ConvertUTF16toUTF8(contentType)
  1.2620 +      ).get());
  1.2621 +  }
  1.2622 +  if (!docEncoder) {
  1.2623 +    // This could be some type for which we create a synthetic document.  Try
  1.2624 +    // again as XML
  1.2625 +    contentType.AssignLiteral("application/xml");
  1.2626 +    docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
  1.2627 +    // Don't try to cache the encoder since it would point to a different
  1.2628 +    // contentType once it has been reinitialized.
  1.2629 +    tryToCacheEncoder = false;
  1.2630 +  }
  1.2631 +
  1.2632 +  NS_ENSURE_TRUE_VOID(docEncoder);
  1.2633 +
  1.2634 +  uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
  1.2635 +                   // Output DOM-standard newlines
  1.2636 +                   nsIDocumentEncoder::OutputLFLineBreak |
  1.2637 +                   // Don't do linebreaking that's not present in
  1.2638 +                   // the source
  1.2639 +                   nsIDocumentEncoder::OutputRaw |
  1.2640 +                   // Only check for mozdirty when necessary (bug 599983)
  1.2641 +                   nsIDocumentEncoder::OutputIgnoreMozDirty;
  1.2642 +
  1.2643 +  if (IsEditable()) {
  1.2644 +    nsCOMPtr<Element> elem = do_QueryInterface(this);
  1.2645 +    nsIEditor* editor = elem ? elem->GetEditorInternal() : nullptr;
  1.2646 +    if (editor && editor->OutputsMozDirty()) {
  1.2647 +      flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
  1.2648 +    }
  1.2649 +  }
  1.2650 +
  1.2651 +  DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
  1.2652 +  MOZ_ASSERT(NS_SUCCEEDED(rv));
  1.2653 +
  1.2654 +  if (aIncludeSelf) {
  1.2655 +    docEncoder->SetNativeNode(this);
  1.2656 +  } else {
  1.2657 +    docEncoder->SetNativeContainerNode(this);
  1.2658 +  }
  1.2659 +  rv = docEncoder->EncodeToString(aMarkup);
  1.2660 +  MOZ_ASSERT(NS_SUCCEEDED(rv));
  1.2661 +  if (tryToCacheEncoder) {
  1.2662 +    doc->SetCachedEncoder(docEncoder.forget());
  1.2663 +  }
  1.2664 +}
  1.2665 +
  1.2666 +static bool
  1.2667 +ContainsMarkup(const nsAString& aStr)
  1.2668 +{
  1.2669 +  // Note: we can't use FindCharInSet because null is one of the characters we
  1.2670 +  // want to search for.
  1.2671 +  const char16_t* start = aStr.BeginReading();
  1.2672 +  const char16_t* end = aStr.EndReading();
  1.2673 +
  1.2674 +  while (start != end) {
  1.2675 +    char16_t c = *start;
  1.2676 +    if (c == char16_t('<') ||
  1.2677 +        c == char16_t('&') ||
  1.2678 +        c == char16_t('\r') ||
  1.2679 +        c == char16_t('\0')) {
  1.2680 +      return true;
  1.2681 +    }
  1.2682 +    ++start;
  1.2683 +  }
  1.2684 +
  1.2685 +  return false;
  1.2686 +}
  1.2687 +
  1.2688 +void
  1.2689 +FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError)
  1.2690 +{
  1.2691 +  FragmentOrElement* target = this;
  1.2692 +  // Handle template case.
  1.2693 +  if (nsNodeUtils::IsTemplateElement(target)) {
  1.2694 +    DocumentFragment* frag =
  1.2695 +      static_cast<HTMLTemplateElement*>(target)->Content();
  1.2696 +    MOZ_ASSERT(frag);
  1.2697 +    target = frag;
  1.2698 +  }
  1.2699 +
  1.2700 +  // Fast-path for strings with no markup. Limit this to short strings, to
  1.2701 +  // avoid ContainsMarkup taking too long. The choice for 100 is based on
  1.2702 +  // gut feeling.
  1.2703 +  //
  1.2704 +  // Don't do this for elements with a weird parser insertion mode, for
  1.2705 +  // instance setting innerHTML = "" on a <html> element should add the
  1.2706 +  // optional <head> and <body> elements.
  1.2707 +  if (!target->HasWeirdParserInsertionMode() &&
  1.2708 +      aInnerHTML.Length() < 100 && !ContainsMarkup(aInnerHTML)) {
  1.2709 +    aError = nsContentUtils::SetNodeTextContent(target, aInnerHTML, false);
  1.2710 +    return;
  1.2711 +  }
  1.2712 +
  1.2713 +  nsIDocument* doc = target->OwnerDoc();
  1.2714 +
  1.2715 +  // Batch possible DOMSubtreeModified events.
  1.2716 +  mozAutoSubtreeModified subtree(doc, nullptr);
  1.2717 +
  1.2718 +  target->FireNodeRemovedForChildren();
  1.2719 +
  1.2720 +  // Needed when innerHTML is used in combination with contenteditable
  1.2721 +  mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
  1.2722 +
  1.2723 +  // Remove childnodes.
  1.2724 +  uint32_t childCount = target->GetChildCount();
  1.2725 +  nsAutoMutationBatch mb(target, true, false);
  1.2726 +  for (uint32_t i = 0; i < childCount; ++i) {
  1.2727 +    target->RemoveChildAt(0, true);
  1.2728 +  }
  1.2729 +  mb.RemovalDone();
  1.2730 +
  1.2731 +  nsAutoScriptLoaderDisabler sld(doc);
  1.2732 +
  1.2733 +  nsIAtom* contextLocalName = Tag();
  1.2734 +  int32_t contextNameSpaceID = GetNameSpaceID();
  1.2735 +
  1.2736 +  ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
  1.2737 +  if (shadowRoot) {
  1.2738 +    // Fix up the context to be the host of the ShadowRoot.
  1.2739 +    contextLocalName = shadowRoot->GetHost()->Tag();
  1.2740 +    contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
  1.2741 +  }
  1.2742 +
  1.2743 +  if (doc->IsHTML()) {
  1.2744 +    int32_t oldChildCount = target->GetChildCount();
  1.2745 +    aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
  1.2746 +                                               target,
  1.2747 +                                               contextLocalName,
  1.2748 +                                               contextNameSpaceID,
  1.2749 +                                               doc->GetCompatibilityMode() ==
  1.2750 +                                                 eCompatibility_NavQuirks,
  1.2751 +                                               true);
  1.2752 +    mb.NodesAdded();
  1.2753 +    // HTML5 parser has notified, but not fired mutation events.
  1.2754 +    nsContentUtils::FireMutationEventsForDirectParsing(doc, target,
  1.2755 +                                                       oldChildCount);
  1.2756 +  } else {
  1.2757 +    nsRefPtr<DocumentFragment> df =
  1.2758 +      nsContentUtils::CreateContextualFragment(target, aInnerHTML, true, aError);
  1.2759 +    if (!aError.Failed()) {
  1.2760 +      // Suppress assertion about node removal mutation events that can't have
  1.2761 +      // listeners anyway, because no one has had the chance to register mutation
  1.2762 +      // listeners on the fragment that comes from the parser.
  1.2763 +      nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
  1.2764 +
  1.2765 +      static_cast<nsINode*>(target)->AppendChild(*df, aError);
  1.2766 +      mb.NodesAdded();
  1.2767 +    }
  1.2768 +  }
  1.2769 +}
  1.2770 +
  1.2771 +nsINode::nsSlots*
  1.2772 +FragmentOrElement::CreateSlots()
  1.2773 +{
  1.2774 +  return new nsDOMSlots();
  1.2775 +}
  1.2776 +
  1.2777 +void
  1.2778 +FragmentOrElement::FireNodeRemovedForChildren()
  1.2779 +{
  1.2780 +  nsIDocument* doc = OwnerDoc();
  1.2781 +  // Optimize the common case
  1.2782 +  if (!nsContentUtils::
  1.2783 +        HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
  1.2784 +    return;
  1.2785 +  }
  1.2786 +
  1.2787 +  nsCOMPtr<nsIDocument> owningDoc = doc;
  1.2788 +
  1.2789 +  nsCOMPtr<nsINode> child;
  1.2790 +  for (child = GetFirstChild();
  1.2791 +       child && child->GetParentNode() == this;
  1.2792 +       child = child->GetNextSibling()) {
  1.2793 +    nsContentUtils::MaybeFireNodeRemoved(child, this, doc);
  1.2794 +  }
  1.2795 +}
  1.2796 +
  1.2797 +size_t
  1.2798 +FragmentOrElement::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  1.2799 +{
  1.2800 +  size_t n = 0;
  1.2801 +  n += nsIContent::SizeOfExcludingThis(aMallocSizeOf);
  1.2802 +  n += mAttrsAndChildren.SizeOfExcludingThis(aMallocSizeOf);
  1.2803 +
  1.2804 +  nsDOMSlots* slots = GetExistingDOMSlots();
  1.2805 +  if (slots) {
  1.2806 +    n += slots->SizeOfIncludingThis(aMallocSizeOf);
  1.2807 +  }
  1.2808 +
  1.2809 +  return n;
  1.2810 +}

mercurial