content/base/src/FragmentOrElement.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 sw=2 et tw=79: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * Base class for all element classes; this provides an implementation
michael@0 9 * of DOM Core's nsIDOMElement, implements nsIContent, provides
michael@0 10 * utility methods for subclasses, and so forth.
michael@0 11 */
michael@0 12
michael@0 13 #include "mozilla/ArrayUtils.h"
michael@0 14 #include "mozilla/Likely.h"
michael@0 15 #include "mozilla/MemoryReporting.h"
michael@0 16 #include "mozilla/StaticPtr.h"
michael@0 17
michael@0 18 #include "mozilla/dom/FragmentOrElement.h"
michael@0 19
michael@0 20 #include "mozilla/AsyncEventDispatcher.h"
michael@0 21 #include "mozilla/EventDispatcher.h"
michael@0 22 #include "mozilla/EventListenerManager.h"
michael@0 23 #include "mozilla/EventStates.h"
michael@0 24 #include "mozilla/dom/Attr.h"
michael@0 25 #include "nsDOMAttributeMap.h"
michael@0 26 #include "nsIAtom.h"
michael@0 27 #include "nsINodeInfo.h"
michael@0 28 #include "nsIDocumentInlines.h"
michael@0 29 #include "nsIDocumentEncoder.h"
michael@0 30 #include "nsIDOMNodeList.h"
michael@0 31 #include "nsIContentIterator.h"
michael@0 32 #include "nsFocusManager.h"
michael@0 33 #include "nsILinkHandler.h"
michael@0 34 #include "nsIScriptGlobalObject.h"
michael@0 35 #include "nsIURL.h"
michael@0 36 #include "nsNetUtil.h"
michael@0 37 #include "nsIFrame.h"
michael@0 38 #include "nsIAnonymousContentCreator.h"
michael@0 39 #include "nsIPresShell.h"
michael@0 40 #include "nsPresContext.h"
michael@0 41 #include "nsStyleConsts.h"
michael@0 42 #include "nsString.h"
michael@0 43 #include "nsUnicharUtils.h"
michael@0 44 #include "nsIDOMEvent.h"
michael@0 45 #include "nsDOMCID.h"
michael@0 46 #include "nsIServiceManager.h"
michael@0 47 #include "nsIDOMCSSStyleDeclaration.h"
michael@0 48 #include "nsDOMCSSAttrDeclaration.h"
michael@0 49 #include "nsNameSpaceManager.h"
michael@0 50 #include "nsContentList.h"
michael@0 51 #include "nsDOMTokenList.h"
michael@0 52 #include "nsXBLPrototypeBinding.h"
michael@0 53 #include "nsError.h"
michael@0 54 #include "nsDOMString.h"
michael@0 55 #include "nsIScriptSecurityManager.h"
michael@0 56 #include "nsIDOMMutationEvent.h"
michael@0 57 #include "mozilla/InternalMutationEvent.h"
michael@0 58 #include "mozilla/MouseEvents.h"
michael@0 59 #include "nsNodeUtils.h"
michael@0 60 #include "nsDocument.h"
michael@0 61 #include "nsAttrValueOrString.h"
michael@0 62 #ifdef MOZ_XUL
michael@0 63 #include "nsXULElement.h"
michael@0 64 #endif /* MOZ_XUL */
michael@0 65 #include "nsFrameManager.h"
michael@0 66 #include "nsFrameSelection.h"
michael@0 67 #ifdef DEBUG
michael@0 68 #include "nsRange.h"
michael@0 69 #endif
michael@0 70
michael@0 71 #include "nsBindingManager.h"
michael@0 72 #include "nsXBLBinding.h"
michael@0 73 #include "nsPIDOMWindow.h"
michael@0 74 #include "nsPIBoxObject.h"
michael@0 75 #include "nsSVGUtils.h"
michael@0 76 #include "nsLayoutUtils.h"
michael@0 77 #include "nsGkAtoms.h"
michael@0 78 #include "nsContentUtils.h"
michael@0 79 #include "nsTextFragment.h"
michael@0 80 #include "nsContentCID.h"
michael@0 81
michael@0 82 #include "nsIDOMEventListener.h"
michael@0 83 #include "nsIWebNavigation.h"
michael@0 84 #include "nsIBaseWindow.h"
michael@0 85 #include "nsIWidget.h"
michael@0 86
michael@0 87 #include "js/GCAPI.h"
michael@0 88
michael@0 89 #include "nsNodeInfoManager.h"
michael@0 90 #include "nsICategoryManager.h"
michael@0 91 #include "nsIDOMUserDataHandler.h"
michael@0 92 #include "nsGenericHTMLElement.h"
michael@0 93 #include "nsIEditor.h"
michael@0 94 #include "nsIEditorIMESupport.h"
michael@0 95 #include "nsContentCreatorFunctions.h"
michael@0 96 #include "nsIControllers.h"
michael@0 97 #include "nsView.h"
michael@0 98 #include "nsViewManager.h"
michael@0 99 #include "nsIScrollableFrame.h"
michael@0 100 #include "ChildIterator.h"
michael@0 101 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
michael@0 102 #include "nsRuleProcessorData.h"
michael@0 103 #include "nsTextNode.h"
michael@0 104 #include "mozilla/dom/NodeListBinding.h"
michael@0 105 #include "mozilla/dom/UndoManager.h"
michael@0 106
michael@0 107 #ifdef MOZ_XUL
michael@0 108 #include "nsIXULDocument.h"
michael@0 109 #endif /* MOZ_XUL */
michael@0 110
michael@0 111 #include "nsCCUncollectableMarker.h"
michael@0 112
michael@0 113 #include "mozAutoDocUpdate.h"
michael@0 114
michael@0 115 #include "prprf.h"
michael@0 116 #include "nsDOMMutationObserver.h"
michael@0 117 #include "nsWrapperCacheInlines.h"
michael@0 118 #include "nsCycleCollector.h"
michael@0 119 #include "xpcpublic.h"
michael@0 120 #include "nsIScriptError.h"
michael@0 121 #include "mozilla/Telemetry.h"
michael@0 122
michael@0 123 #include "mozilla/CORSMode.h"
michael@0 124
michael@0 125 #include "mozilla/dom/ShadowRoot.h"
michael@0 126 #include "mozilla/dom/HTMLTemplateElement.h"
michael@0 127
michael@0 128 #include "nsStyledElement.h"
michael@0 129 #include "nsIContentInlines.h"
michael@0 130
michael@0 131 using namespace mozilla;
michael@0 132 using namespace mozilla::dom;
michael@0 133
michael@0 134 int32_t nsIContent::sTabFocusModel = eTabFocus_any;
michael@0 135 bool nsIContent::sTabFocusModelAppliesToXUL = false;
michael@0 136 uint32_t nsMutationGuard::sMutationCount = 0;
michael@0 137
michael@0 138 nsIContent*
michael@0 139 nsIContent::FindFirstNonChromeOnlyAccessContent() const
michael@0 140 {
michael@0 141 // This handles also nested native anonymous content.
michael@0 142 for (const nsIContent *content = this; content;
michael@0 143 content = content->GetBindingParent()) {
michael@0 144 if (!content->ChromeOnlyAccess()) {
michael@0 145 // Oops, this function signature allows casting const to
michael@0 146 // non-const. (Then again, so does GetChildAt(0)->GetParent().)
michael@0 147 return const_cast<nsIContent*>(content);
michael@0 148 }
michael@0 149 }
michael@0 150 return nullptr;
michael@0 151 }
michael@0 152
michael@0 153 nsIContent*
michael@0 154 nsIContent::GetFlattenedTreeParent() const
michael@0 155 {
michael@0 156 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
michael@0 157 nsIContent* parent = GetXBLInsertionParent();
michael@0 158 if (parent) {
michael@0 159 return parent;
michael@0 160 }
michael@0 161 }
michael@0 162
michael@0 163 return GetParent();
michael@0 164 }
michael@0 165
michael@0 166 nsIContent::IMEState
michael@0 167 nsIContent::GetDesiredIMEState()
michael@0 168 {
michael@0 169 if (!IsEditableInternal()) {
michael@0 170 // Check for the special case where we're dealing with elements which don't
michael@0 171 // have the editable flag set, but are readwrite (such as text controls).
michael@0 172 if (!IsElement() ||
michael@0 173 !AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
michael@0 174 return IMEState(IMEState::DISABLED);
michael@0 175 }
michael@0 176 }
michael@0 177 // NOTE: The content for independent editors (e.g., input[type=text],
michael@0 178 // textarea) must override this method, so, we don't need to worry about
michael@0 179 // that here.
michael@0 180 nsIContent *editableAncestor = GetEditingHost();
michael@0 181
michael@0 182 // This is in another editable content, use the result of it.
michael@0 183 if (editableAncestor && editableAncestor != this) {
michael@0 184 return editableAncestor->GetDesiredIMEState();
michael@0 185 }
michael@0 186 nsIDocument* doc = GetCurrentDoc();
michael@0 187 if (!doc) {
michael@0 188 return IMEState(IMEState::DISABLED);
michael@0 189 }
michael@0 190 nsIPresShell* ps = doc->GetShell();
michael@0 191 if (!ps) {
michael@0 192 return IMEState(IMEState::DISABLED);
michael@0 193 }
michael@0 194 nsPresContext* pc = ps->GetPresContext();
michael@0 195 if (!pc) {
michael@0 196 return IMEState(IMEState::DISABLED);
michael@0 197 }
michael@0 198 nsIEditor* editor = nsContentUtils::GetHTMLEditor(pc);
michael@0 199 nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(editor);
michael@0 200 if (!imeEditor) {
michael@0 201 return IMEState(IMEState::DISABLED);
michael@0 202 }
michael@0 203 IMEState state;
michael@0 204 imeEditor->GetPreferredIMEState(&state);
michael@0 205 return state;
michael@0 206 }
michael@0 207
michael@0 208 bool
michael@0 209 nsIContent::HasIndependentSelection()
michael@0 210 {
michael@0 211 nsIFrame* frame = GetPrimaryFrame();
michael@0 212 return (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
michael@0 213 }
michael@0 214
michael@0 215 dom::Element*
michael@0 216 nsIContent::GetEditingHost()
michael@0 217 {
michael@0 218 // If this isn't editable, return nullptr.
michael@0 219 NS_ENSURE_TRUE(IsEditableInternal(), nullptr);
michael@0 220
michael@0 221 nsIDocument* doc = GetCurrentDoc();
michael@0 222 NS_ENSURE_TRUE(doc, nullptr);
michael@0 223 // If this is in designMode, we should return <body>
michael@0 224 if (doc->HasFlag(NODE_IS_EDITABLE)) {
michael@0 225 return doc->GetBodyElement();
michael@0 226 }
michael@0 227
michael@0 228 nsIContent* content = this;
michael@0 229 for (dom::Element* parent = GetParentElement();
michael@0 230 parent && parent->HasFlag(NODE_IS_EDITABLE);
michael@0 231 parent = content->GetParentElement()) {
michael@0 232 content = parent;
michael@0 233 }
michael@0 234 return content->AsElement();
michael@0 235 }
michael@0 236
michael@0 237 nsresult
michael@0 238 nsIContent::LookupNamespaceURIInternal(const nsAString& aNamespacePrefix,
michael@0 239 nsAString& aNamespaceURI) const
michael@0 240 {
michael@0 241 if (aNamespacePrefix.EqualsLiteral("xml")) {
michael@0 242 // Special-case for xml prefix
michael@0 243 aNamespaceURI.AssignLiteral("http://www.w3.org/XML/1998/namespace");
michael@0 244 return NS_OK;
michael@0 245 }
michael@0 246
michael@0 247 if (aNamespacePrefix.EqualsLiteral("xmlns")) {
michael@0 248 // Special-case for xmlns prefix
michael@0 249 aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/");
michael@0 250 return NS_OK;
michael@0 251 }
michael@0 252
michael@0 253 nsCOMPtr<nsIAtom> name;
michael@0 254 if (!aNamespacePrefix.IsEmpty()) {
michael@0 255 name = do_GetAtom(aNamespacePrefix);
michael@0 256 NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
michael@0 257 }
michael@0 258 else {
michael@0 259 name = nsGkAtoms::xmlns;
michael@0 260 }
michael@0 261 // Trace up the content parent chain looking for the namespace
michael@0 262 // declaration that declares aNamespacePrefix.
michael@0 263 const nsIContent* content = this;
michael@0 264 do {
michael@0 265 if (content->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI))
michael@0 266 return NS_OK;
michael@0 267 } while ((content = content->GetParent()));
michael@0 268 return NS_ERROR_FAILURE;
michael@0 269 }
michael@0 270
michael@0 271 already_AddRefed<nsIURI>
michael@0 272 nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const
michael@0 273 {
michael@0 274 nsIDocument* doc = OwnerDoc();
michael@0 275 // Start with document base
michael@0 276 nsCOMPtr<nsIURI> base = doc->GetBaseURI(aTryUseXHRDocBaseURI);
michael@0 277
michael@0 278 // Collect array of xml:base attribute values up the parent chain. This
michael@0 279 // is slightly slower for the case when there are xml:base attributes, but
michael@0 280 // faster for the far more common case of there not being any such
michael@0 281 // attributes.
michael@0 282 // Also check for SVG elements which require special handling
michael@0 283 nsAutoTArray<nsString, 5> baseAttrs;
michael@0 284 nsString attr;
michael@0 285 const nsIContent *elem = this;
michael@0 286 do {
michael@0 287 // First check for SVG specialness (why is this SVG specific?)
michael@0 288 if (elem->IsSVG()) {
michael@0 289 nsIContent* bindingParent = elem->GetBindingParent();
michael@0 290 if (bindingParent) {
michael@0 291 nsXBLBinding* binding = bindingParent->GetXBLBinding();
michael@0 292 if (binding) {
michael@0 293 // XXX sXBL/XBL2 issue
michael@0 294 // If this is an anonymous XBL element use the binding
michael@0 295 // document for the base URI.
michael@0 296 // XXX Will fail with xml:base
michael@0 297 base = binding->PrototypeBinding()->DocURI();
michael@0 298 break;
michael@0 299 }
michael@0 300 }
michael@0 301 }
michael@0 302
michael@0 303 nsIURI* explicitBaseURI = elem->GetExplicitBaseURI();
michael@0 304 if (explicitBaseURI) {
michael@0 305 base = explicitBaseURI;
michael@0 306 break;
michael@0 307 }
michael@0 308
michael@0 309 // Otherwise check for xml:base attribute
michael@0 310 elem->GetAttr(kNameSpaceID_XML, nsGkAtoms::base, attr);
michael@0 311 if (!attr.IsEmpty()) {
michael@0 312 baseAttrs.AppendElement(attr);
michael@0 313 }
michael@0 314 elem = elem->GetParent();
michael@0 315 } while(elem);
michael@0 316
michael@0 317 // Now resolve against all xml:base attrs
michael@0 318 for (uint32_t i = baseAttrs.Length() - 1; i != uint32_t(-1); --i) {
michael@0 319 nsCOMPtr<nsIURI> newBase;
michael@0 320 nsresult rv = NS_NewURI(getter_AddRefs(newBase), baseAttrs[i],
michael@0 321 doc->GetDocumentCharacterSet().get(), base);
michael@0 322 // Do a security check, almost the same as nsDocument::SetBaseURL()
michael@0 323 // Only need to do this on the final uri
michael@0 324 if (NS_SUCCEEDED(rv) && i == 0) {
michael@0 325 rv = nsContentUtils::GetSecurityManager()->
michael@0 326 CheckLoadURIWithPrincipal(NodePrincipal(), newBase,
michael@0 327 nsIScriptSecurityManager::STANDARD);
michael@0 328 }
michael@0 329 if (NS_SUCCEEDED(rv)) {
michael@0 330 base.swap(newBase);
michael@0 331 }
michael@0 332 }
michael@0 333
michael@0 334 return base.forget();
michael@0 335 }
michael@0 336
michael@0 337 //----------------------------------------------------------------------
michael@0 338
michael@0 339 static inline JSObject*
michael@0 340 GetJSObjectChild(nsWrapperCache* aCache)
michael@0 341 {
michael@0 342 return aCache->PreservingWrapper() ? aCache->GetWrapperPreserveColor() : nullptr;
michael@0 343 }
michael@0 344
michael@0 345 static bool
michael@0 346 NeedsScriptTraverse(nsINode* aNode)
michael@0 347 {
michael@0 348 return aNode->PreservingWrapper() && aNode->GetWrapperPreserveColor() &&
michael@0 349 !aNode->IsBlackAndDoesNotNeedTracing(aNode);
michael@0 350 }
michael@0 351
michael@0 352 //----------------------------------------------------------------------
michael@0 353
michael@0 354 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsChildContentList)
michael@0 355 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsChildContentList)
michael@0 356
michael@0 357 // If nsChildContentList is changed so that any additional fields are
michael@0 358 // traversed by the cycle collector, then CAN_SKIP must be updated to
michael@0 359 // check that the additional fields are null.
michael@0 360 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsChildContentList)
michael@0 361
michael@0 362 // nsChildContentList only ever has a single child, its wrapper, so if
michael@0 363 // the wrapper is black, the list can't be part of a garbage cycle.
michael@0 364 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsChildContentList)
michael@0 365 return tmp->IsBlack();
michael@0 366 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
michael@0 367
michael@0 368 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsChildContentList)
michael@0 369 return tmp->IsBlackAndDoesNotNeedTracing(tmp);
michael@0 370 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
michael@0 371
michael@0 372 // CanSkipThis returns false to avoid problems with incomplete unlinking.
michael@0 373 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsChildContentList)
michael@0 374 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
michael@0 375
michael@0 376 NS_INTERFACE_TABLE_HEAD(nsChildContentList)
michael@0 377 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
michael@0 378 NS_INTERFACE_TABLE(nsChildContentList, nsINodeList, nsIDOMNodeList)
michael@0 379 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsChildContentList)
michael@0 380 NS_INTERFACE_MAP_END
michael@0 381
michael@0 382 JSObject*
michael@0 383 nsChildContentList::WrapObject(JSContext *cx)
michael@0 384 {
michael@0 385 return NodeListBinding::Wrap(cx, this);
michael@0 386 }
michael@0 387
michael@0 388 NS_IMETHODIMP
michael@0 389 nsChildContentList::GetLength(uint32_t* aLength)
michael@0 390 {
michael@0 391 *aLength = mNode ? mNode->GetChildCount() : 0;
michael@0 392
michael@0 393 return NS_OK;
michael@0 394 }
michael@0 395
michael@0 396 NS_IMETHODIMP
michael@0 397 nsChildContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
michael@0 398 {
michael@0 399 nsINode* node = Item(aIndex);
michael@0 400 if (!node) {
michael@0 401 *aReturn = nullptr;
michael@0 402
michael@0 403 return NS_OK;
michael@0 404 }
michael@0 405
michael@0 406 return CallQueryInterface(node, aReturn);
michael@0 407 }
michael@0 408
michael@0 409 nsIContent*
michael@0 410 nsChildContentList::Item(uint32_t aIndex)
michael@0 411 {
michael@0 412 if (mNode) {
michael@0 413 return mNode->GetChildAt(aIndex);
michael@0 414 }
michael@0 415
michael@0 416 return nullptr;
michael@0 417 }
michael@0 418
michael@0 419 int32_t
michael@0 420 nsChildContentList::IndexOf(nsIContent* aContent)
michael@0 421 {
michael@0 422 if (mNode) {
michael@0 423 return mNode->IndexOf(aContent);
michael@0 424 }
michael@0 425
michael@0 426 return -1;
michael@0 427 }
michael@0 428
michael@0 429 //----------------------------------------------------------------------
michael@0 430
michael@0 431 NS_IMPL_CYCLE_COLLECTION(nsNode3Tearoff, mNode)
michael@0 432
michael@0 433 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNode3Tearoff)
michael@0 434 NS_INTERFACE_MAP_ENTRY(nsIDOMXPathNSResolver)
michael@0 435 NS_INTERFACE_MAP_END_AGGREGATED(mNode)
michael@0 436
michael@0 437 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNode3Tearoff)
michael@0 438 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNode3Tearoff)
michael@0 439
michael@0 440 NS_IMETHODIMP
michael@0 441 nsNode3Tearoff::LookupNamespaceURI(const nsAString& aNamespacePrefix,
michael@0 442 nsAString& aNamespaceURI)
michael@0 443 {
michael@0 444 mNode->LookupNamespaceURI(aNamespacePrefix, aNamespaceURI);
michael@0 445 return NS_OK;
michael@0 446 }
michael@0 447
michael@0 448 nsIHTMLCollection*
michael@0 449 FragmentOrElement::Children()
michael@0 450 {
michael@0 451 FragmentOrElement::nsDOMSlots *slots = DOMSlots();
michael@0 452
michael@0 453 if (!slots->mChildrenList) {
michael@0 454 slots->mChildrenList = new nsContentList(this, kNameSpaceID_Wildcard,
michael@0 455 nsGkAtoms::_asterix, nsGkAtoms::_asterix,
michael@0 456 false);
michael@0 457 }
michael@0 458
michael@0 459 return slots->mChildrenList;
michael@0 460 }
michael@0 461
michael@0 462
michael@0 463 //----------------------------------------------------------------------
michael@0 464
michael@0 465
michael@0 466 NS_IMPL_ISUPPORTS(nsNodeWeakReference,
michael@0 467 nsIWeakReference)
michael@0 468
michael@0 469 nsNodeWeakReference::~nsNodeWeakReference()
michael@0 470 {
michael@0 471 if (mNode) {
michael@0 472 NS_ASSERTION(mNode->Slots()->mWeakReference == this,
michael@0 473 "Weak reference has wrong value");
michael@0 474 mNode->Slots()->mWeakReference = nullptr;
michael@0 475 }
michael@0 476 }
michael@0 477
michael@0 478 NS_IMETHODIMP
michael@0 479 nsNodeWeakReference::QueryReferent(const nsIID& aIID, void** aInstancePtr)
michael@0 480 {
michael@0 481 return mNode ? mNode->QueryInterface(aIID, aInstancePtr) :
michael@0 482 NS_ERROR_NULL_POINTER;
michael@0 483 }
michael@0 484
michael@0 485
michael@0 486 NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff, mNode)
michael@0 487
michael@0 488 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff)
michael@0 489 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
michael@0 490 NS_INTERFACE_MAP_END_AGGREGATED(mNode)
michael@0 491
michael@0 492 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff)
michael@0 493 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff)
michael@0 494
michael@0 495 NS_IMETHODIMP
michael@0 496 nsNodeSupportsWeakRefTearoff::GetWeakReference(nsIWeakReference** aInstancePtr)
michael@0 497 {
michael@0 498 nsINode::nsSlots* slots = mNode->Slots();
michael@0 499 if (!slots->mWeakReference) {
michael@0 500 slots->mWeakReference = new nsNodeWeakReference(mNode);
michael@0 501 NS_ENSURE_TRUE(slots->mWeakReference, NS_ERROR_OUT_OF_MEMORY);
michael@0 502 }
michael@0 503
michael@0 504 NS_ADDREF(*aInstancePtr = slots->mWeakReference);
michael@0 505
michael@0 506 return NS_OK;
michael@0 507 }
michael@0 508
michael@0 509 //----------------------------------------------------------------------
michael@0 510 FragmentOrElement::nsDOMSlots::nsDOMSlots()
michael@0 511 : nsINode::nsSlots(),
michael@0 512 mDataset(nullptr),
michael@0 513 mUndoManager(nullptr),
michael@0 514 mBindingParent(nullptr)
michael@0 515 {
michael@0 516 }
michael@0 517
michael@0 518 FragmentOrElement::nsDOMSlots::~nsDOMSlots()
michael@0 519 {
michael@0 520 if (mAttributeMap) {
michael@0 521 mAttributeMap->DropReference();
michael@0 522 }
michael@0 523 }
michael@0 524
michael@0 525 void
michael@0 526 FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb, bool aIsXUL)
michael@0 527 {
michael@0 528 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mStyle");
michael@0 529 cb.NoteXPCOMChild(mStyle.get());
michael@0 530
michael@0 531 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mSMILOverrideStyle");
michael@0 532 cb.NoteXPCOMChild(mSMILOverrideStyle.get());
michael@0 533
michael@0 534 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mAttributeMap");
michael@0 535 cb.NoteXPCOMChild(mAttributeMap.get());
michael@0 536
michael@0 537 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mUndoManager");
michael@0 538 cb.NoteXPCOMChild(mUndoManager.get());
michael@0 539
michael@0 540 if (aIsXUL) {
michael@0 541 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mControllers");
michael@0 542 cb.NoteXPCOMChild(mControllers);
michael@0 543 }
michael@0 544
michael@0 545 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLBinding");
michael@0 546 cb.NoteNativeChild(mXBLBinding, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
michael@0 547
michael@0 548 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
michael@0 549 cb.NoteXPCOMChild(mXBLInsertionParent.get());
michael@0 550
michael@0 551 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mShadowRoot");
michael@0 552 cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
michael@0 553
michael@0 554 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
michael@0 555 cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
michael@0 556
michael@0 557 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
michael@0 558 cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
michael@0 559
michael@0 560 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
michael@0 561 cb.NoteXPCOMChild(mClassList.get());
michael@0 562
michael@0 563 if (mCustomElementData) {
michael@0 564 for (uint32_t i = 0; i < mCustomElementData->mCallbackQueue.Length(); i++) {
michael@0 565 mCustomElementData->mCallbackQueue[i]->Traverse(cb);
michael@0 566 }
michael@0 567 }
michael@0 568 }
michael@0 569
michael@0 570 void
michael@0 571 FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL)
michael@0 572 {
michael@0 573 mStyle = nullptr;
michael@0 574 mSMILOverrideStyle = nullptr;
michael@0 575 if (mAttributeMap) {
michael@0 576 mAttributeMap->DropReference();
michael@0 577 mAttributeMap = nullptr;
michael@0 578 }
michael@0 579 if (aIsXUL)
michael@0 580 NS_IF_RELEASE(mControllers);
michael@0 581 mXBLBinding = nullptr;
michael@0 582 mXBLInsertionParent = nullptr;
michael@0 583 mShadowRoot = nullptr;
michael@0 584 mContainingShadow = nullptr;
michael@0 585 mChildrenList = nullptr;
michael@0 586 mUndoManager = nullptr;
michael@0 587 mCustomElementData = nullptr;
michael@0 588 mClassList = nullptr;
michael@0 589 }
michael@0 590
michael@0 591 size_t
michael@0 592 FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 593 {
michael@0 594 size_t n = aMallocSizeOf(this);
michael@0 595
michael@0 596 if (mAttributeMap) {
michael@0 597 n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf);
michael@0 598 }
michael@0 599
michael@0 600 // Measurement of the following members may be added later if DMD finds it is
michael@0 601 // worthwhile:
michael@0 602 // - Superclass members (nsINode::nsSlots)
michael@0 603 // - mStyle
michael@0 604 // - mDataSet
michael@0 605 // - mSMILOverrideStyle
michael@0 606 // - mSMILOverrideStyleRule
michael@0 607 // - mChildrenList
michael@0 608 // - mClassList
michael@0 609
michael@0 610 // The following members are not measured:
michael@0 611 // - mBindingParent / mControllers: because they're non-owning
michael@0 612 return n;
michael@0 613 }
michael@0 614
michael@0 615 FragmentOrElement::FragmentOrElement(already_AddRefed<nsINodeInfo>& aNodeInfo)
michael@0 616 : nsIContent(aNodeInfo)
michael@0 617 {
michael@0 618 }
michael@0 619
michael@0 620 FragmentOrElement::FragmentOrElement(already_AddRefed<nsINodeInfo>&& aNodeInfo)
michael@0 621 : nsIContent(aNodeInfo)
michael@0 622 {
michael@0 623 }
michael@0 624
michael@0 625 FragmentOrElement::~FragmentOrElement()
michael@0 626 {
michael@0 627 NS_PRECONDITION(!IsInDoc(),
michael@0 628 "Please remove this from the document properly");
michael@0 629 if (GetParent()) {
michael@0 630 NS_RELEASE(mParent);
michael@0 631 }
michael@0 632 }
michael@0 633
michael@0 634 already_AddRefed<nsINodeList>
michael@0 635 FragmentOrElement::GetChildren(uint32_t aFilter)
michael@0 636 {
michael@0 637 nsRefPtr<nsSimpleContentList> list = new nsSimpleContentList(this);
michael@0 638 if (!list) {
michael@0 639 return nullptr;
michael@0 640 }
michael@0 641
michael@0 642 nsIFrame *frame = GetPrimaryFrame();
michael@0 643
michael@0 644 // Append :before generated content.
michael@0 645 if (frame) {
michael@0 646 nsIFrame *beforeFrame = nsLayoutUtils::GetBeforeFrame(frame);
michael@0 647 if (beforeFrame) {
michael@0 648 list->AppendElement(beforeFrame->GetContent());
michael@0 649 }
michael@0 650 }
michael@0 651
michael@0 652 // If XBL is bound to this node then append XBL anonymous content including
michael@0 653 // explict content altered by insertion point if we were requested for XBL
michael@0 654 // anonymous content, otherwise append explicit content with respect to
michael@0 655 // insertion point if any.
michael@0 656 if (!(aFilter & eAllButXBL)) {
michael@0 657 FlattenedChildIterator iter(this);
michael@0 658 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
michael@0 659 list->AppendElement(child);
michael@0 660 }
michael@0 661 } else {
michael@0 662 ExplicitChildIterator iter(this);
michael@0 663 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
michael@0 664 list->AppendElement(child);
michael@0 665 }
michael@0 666 }
michael@0 667
michael@0 668 if (frame) {
michael@0 669 // Append native anonymous content to the end.
michael@0 670 nsIAnonymousContentCreator* creator = do_QueryFrame(frame);
michael@0 671 if (creator) {
michael@0 672 creator->AppendAnonymousContentTo(*list, aFilter);
michael@0 673 }
michael@0 674
michael@0 675 // Append :after generated content.
michael@0 676 nsIFrame *afterFrame = nsLayoutUtils::GetAfterFrame(frame);
michael@0 677 if (afterFrame) {
michael@0 678 list->AppendElement(afterFrame->GetContent());
michael@0 679 }
michael@0 680 }
michael@0 681
michael@0 682 return list.forget();
michael@0 683 }
michael@0 684
michael@0 685 static nsIContent*
michael@0 686 FindChromeAccessOnlySubtreeOwner(nsIContent* aContent)
michael@0 687 {
michael@0 688 if (aContent->ChromeOnlyAccess()) {
michael@0 689 bool chromeAccessOnly = false;
michael@0 690 while (aContent && !chromeAccessOnly) {
michael@0 691 chromeAccessOnly = aContent->IsRootOfChromeAccessOnlySubtree();
michael@0 692 aContent = aContent->GetParent();
michael@0 693 }
michael@0 694 }
michael@0 695 return aContent;
michael@0 696 }
michael@0 697
michael@0 698 nsresult
michael@0 699 nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor)
michael@0 700 {
michael@0 701 //FIXME! Document how this event retargeting works, Bug 329124.
michael@0 702 aVisitor.mCanHandle = true;
michael@0 703 aVisitor.mMayHaveListenerManager = HasListenerManager();
michael@0 704
michael@0 705 // Don't propagate mouseover and mouseout events when mouse is moving
michael@0 706 // inside chrome access only content.
michael@0 707 bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
michael@0 708 if ((aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH ||
michael@0 709 aVisitor.mEvent->message == NS_MOUSE_EXIT_SYNTH ||
michael@0 710 aVisitor.mEvent->message == NS_POINTER_OVER ||
michael@0 711 aVisitor.mEvent->message == NS_POINTER_OUT) &&
michael@0 712 // Check if we should stop event propagation when event has just been
michael@0 713 // dispatched or when we're about to propagate from
michael@0 714 // chrome access only subtree.
michael@0 715 ((this == aVisitor.mEvent->originalTarget &&
michael@0 716 !ChromeOnlyAccess()) || isAnonForEvents)) {
michael@0 717 nsCOMPtr<nsIContent> relatedTarget =
michael@0 718 do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->relatedTarget);
michael@0 719 if (relatedTarget &&
michael@0 720 relatedTarget->OwnerDoc() == OwnerDoc()) {
michael@0 721
michael@0 722 // If current target is anonymous for events or we know that related
michael@0 723 // target is descendant of an element which is anonymous for events,
michael@0 724 // we may want to stop event propagation.
michael@0 725 // If this is the original target, aVisitor.mRelatedTargetIsInAnon
michael@0 726 // must be updated.
michael@0 727 if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
michael@0 728 (aVisitor.mEvent->originalTarget == this &&
michael@0 729 (aVisitor.mRelatedTargetIsInAnon =
michael@0 730 relatedTarget->ChromeOnlyAccess()))) {
michael@0 731 nsIContent* anonOwner = FindChromeAccessOnlySubtreeOwner(this);
michael@0 732 if (anonOwner) {
michael@0 733 nsIContent* anonOwnerRelated =
michael@0 734 FindChromeAccessOnlySubtreeOwner(relatedTarget);
michael@0 735 if (anonOwnerRelated) {
michael@0 736 // Note, anonOwnerRelated may still be inside some other
michael@0 737 // native anonymous subtree. The case where anonOwner is still
michael@0 738 // inside native anonymous subtree will be handled when event
michael@0 739 // propagates up in the DOM tree.
michael@0 740 while (anonOwner != anonOwnerRelated &&
michael@0 741 anonOwnerRelated->ChromeOnlyAccess()) {
michael@0 742 anonOwnerRelated = FindChromeAccessOnlySubtreeOwner(anonOwnerRelated);
michael@0 743 }
michael@0 744 if (anonOwner == anonOwnerRelated) {
michael@0 745 #ifdef DEBUG_smaug
michael@0 746 nsCOMPtr<nsIContent> originalTarget =
michael@0 747 do_QueryInterface(aVisitor.mEvent->originalTarget);
michael@0 748 nsAutoString ot, ct, rt;
michael@0 749 if (originalTarget) {
michael@0 750 originalTarget->Tag()->ToString(ot);
michael@0 751 }
michael@0 752 Tag()->ToString(ct);
michael@0 753 relatedTarget->Tag()->ToString(rt);
michael@0 754 printf("Stopping %s propagation:"
michael@0 755 "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s"
michael@0 756 "\n\trelatedTarget=%s %s \n%s",
michael@0 757 (aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH)
michael@0 758 ? "mouseover" : "mouseout",
michael@0 759 NS_ConvertUTF16toUTF8(ot).get(),
michael@0 760 NS_ConvertUTF16toUTF8(ct).get(),
michael@0 761 isAnonForEvents
michael@0 762 ? "(is native anonymous)"
michael@0 763 : (ChromeOnlyAccess()
michael@0 764 ? "(is in native anonymous subtree)" : ""),
michael@0 765 NS_ConvertUTF16toUTF8(rt).get(),
michael@0 766 relatedTarget->ChromeOnlyAccess()
michael@0 767 ? "(is in native anonymous subtree)" : "",
michael@0 768 (originalTarget &&
michael@0 769 relatedTarget->FindFirstNonChromeOnlyAccessContent() ==
michael@0 770 originalTarget->FindFirstNonChromeOnlyAccessContent())
michael@0 771 ? "" : "Wrong event propagation!?!\n");
michael@0 772 #endif
michael@0 773 aVisitor.mParentTarget = nullptr;
michael@0 774 // Event should not propagate to non-anon content.
michael@0 775 aVisitor.mCanHandle = isAnonForEvents;
michael@0 776 return NS_OK;
michael@0 777 }
michael@0 778 }
michael@0 779 }
michael@0 780 }
michael@0 781 }
michael@0 782 }
michael@0 783
michael@0 784 nsIContent* parent = GetParent();
michael@0 785 // Event may need to be retargeted if this is the root of a native
michael@0 786 // anonymous content subtree or event is dispatched somewhere inside XBL.
michael@0 787 if (isAnonForEvents) {
michael@0 788 #ifdef DEBUG
michael@0 789 // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
michael@0 790 // all the events are allowed even in the native anonymous content..
michael@0 791 nsCOMPtr<nsIContent> t = do_QueryInterface(aVisitor.mEvent->originalTarget);
michael@0 792 NS_ASSERTION(!t || !t->ChromeOnlyAccess() ||
michael@0 793 aVisitor.mEvent->eventStructType != NS_MUTATION_EVENT ||
michael@0 794 aVisitor.mDOMEvent,
michael@0 795 "Mutation event dispatched in native anonymous content!?!");
michael@0 796 #endif
michael@0 797 aVisitor.mEventTargetAtParent = parent;
michael@0 798 } else if (parent && aVisitor.mOriginalTargetIsInAnon) {
michael@0 799 nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->target));
michael@0 800 if (content && content->GetBindingParent() == parent) {
michael@0 801 aVisitor.mEventTargetAtParent = parent;
michael@0 802 }
michael@0 803 }
michael@0 804
michael@0 805 // check for an anonymous parent
michael@0 806 // XXX XBL2/sXBL issue
michael@0 807 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
michael@0 808 nsIContent* insertionParent = GetXBLInsertionParent();
michael@0 809 NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent &&
michael@0 810 aVisitor.mEventTargetAtParent != insertionParent),
michael@0 811 "Retargeting and having insertion parent!");
michael@0 812 if (insertionParent) {
michael@0 813 parent = insertionParent;
michael@0 814 }
michael@0 815 }
michael@0 816
michael@0 817 if (parent) {
michael@0 818 aVisitor.mParentTarget = parent;
michael@0 819 } else {
michael@0 820 aVisitor.mParentTarget = GetCurrentDoc();
michael@0 821 }
michael@0 822 return NS_OK;
michael@0 823 }
michael@0 824
michael@0 825 bool
michael@0 826 nsIContent::GetAttr(int32_t aNameSpaceID, nsIAtom* aName,
michael@0 827 nsAString& aResult) const
michael@0 828 {
michael@0 829 if (IsElement()) {
michael@0 830 return AsElement()->GetAttr(aNameSpaceID, aName, aResult);
michael@0 831 }
michael@0 832 aResult.Truncate();
michael@0 833 return false;
michael@0 834 }
michael@0 835
michael@0 836 bool
michael@0 837 nsIContent::HasAttr(int32_t aNameSpaceID, nsIAtom* aName) const
michael@0 838 {
michael@0 839 return IsElement() && AsElement()->HasAttr(aNameSpaceID, aName);
michael@0 840 }
michael@0 841
michael@0 842 bool
michael@0 843 nsIContent::AttrValueIs(int32_t aNameSpaceID,
michael@0 844 nsIAtom* aName,
michael@0 845 const nsAString& aValue,
michael@0 846 nsCaseTreatment aCaseSensitive) const
michael@0 847 {
michael@0 848 return IsElement() &&
michael@0 849 AsElement()->AttrValueIs(aNameSpaceID, aName, aValue, aCaseSensitive);
michael@0 850 }
michael@0 851
michael@0 852 bool
michael@0 853 nsIContent::AttrValueIs(int32_t aNameSpaceID,
michael@0 854 nsIAtom* aName,
michael@0 855 nsIAtom* aValue,
michael@0 856 nsCaseTreatment aCaseSensitive) const
michael@0 857 {
michael@0 858 return IsElement() &&
michael@0 859 AsElement()->AttrValueIs(aNameSpaceID, aName, aValue, aCaseSensitive);
michael@0 860 }
michael@0 861
michael@0 862 bool
michael@0 863 nsIContent::IsFocusable(int32_t* aTabIndex, bool aWithMouse)
michael@0 864 {
michael@0 865 bool focusable = IsFocusableInternal(aTabIndex, aWithMouse);
michael@0 866 // Ensure that the return value and aTabIndex are consistent in the case
michael@0 867 // we're in userfocusignored context.
michael@0 868 if (focusable || (aTabIndex && *aTabIndex != -1)) {
michael@0 869 if (nsContentUtils::IsUserFocusIgnored(this)) {
michael@0 870 if (aTabIndex) {
michael@0 871 *aTabIndex = -1;
michael@0 872 }
michael@0 873 return false;
michael@0 874 }
michael@0 875 return focusable;
michael@0 876 }
michael@0 877 return false;
michael@0 878 }
michael@0 879
michael@0 880 bool
michael@0 881 nsIContent::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse)
michael@0 882 {
michael@0 883 if (aTabIndex) {
michael@0 884 *aTabIndex = -1; // Default, not tabbable
michael@0 885 }
michael@0 886 return false;
michael@0 887 }
michael@0 888
michael@0 889 const nsAttrValue*
michael@0 890 FragmentOrElement::DoGetClasses() const
michael@0 891 {
michael@0 892 NS_NOTREACHED("Shouldn't ever be called");
michael@0 893 return nullptr;
michael@0 894 }
michael@0 895
michael@0 896 NS_IMETHODIMP
michael@0 897 FragmentOrElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
michael@0 898 {
michael@0 899 return NS_OK;
michael@0 900 }
michael@0 901
michael@0 902 bool
michael@0 903 FragmentOrElement::IsLink(nsIURI** aURI) const
michael@0 904 {
michael@0 905 *aURI = nullptr;
michael@0 906 return false;
michael@0 907 }
michael@0 908
michael@0 909 nsIContent*
michael@0 910 FragmentOrElement::GetBindingParent() const
michael@0 911 {
michael@0 912 nsDOMSlots *slots = GetExistingDOMSlots();
michael@0 913
michael@0 914 if (slots) {
michael@0 915 return slots->mBindingParent;
michael@0 916 }
michael@0 917 return nullptr;
michael@0 918 }
michael@0 919
michael@0 920 nsXBLBinding*
michael@0 921 FragmentOrElement::GetXBLBinding() const
michael@0 922 {
michael@0 923 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
michael@0 924 nsDOMSlots *slots = GetExistingDOMSlots();
michael@0 925 if (slots) {
michael@0 926 return slots->mXBLBinding;
michael@0 927 }
michael@0 928 }
michael@0 929
michael@0 930 return nullptr;
michael@0 931 }
michael@0 932
michael@0 933 void
michael@0 934 FragmentOrElement::SetXBLBinding(nsXBLBinding* aBinding,
michael@0 935 nsBindingManager* aOldBindingManager)
michael@0 936 {
michael@0 937 nsBindingManager* bindingManager;
michael@0 938 if (aOldBindingManager) {
michael@0 939 MOZ_ASSERT(!aBinding, "aOldBindingManager should only be provided "
michael@0 940 "when removing a binding.");
michael@0 941 bindingManager = aOldBindingManager;
michael@0 942 } else {
michael@0 943 bindingManager = OwnerDoc()->BindingManager();
michael@0 944 }
michael@0 945
michael@0 946 // After this point, aBinding will be the most-derived binding for aContent.
michael@0 947 // If we already have a binding for aContent, make sure to
michael@0 948 // remove it from the attached stack. Otherwise we might end up firing its
michael@0 949 // constructor twice (if aBinding inherits from it) or firing its constructor
michael@0 950 // after aContent has been deleted (if aBinding is null and the content node
michael@0 951 // dies before we process mAttachedStack).
michael@0 952 nsRefPtr<nsXBLBinding> oldBinding = GetXBLBinding();
michael@0 953 if (oldBinding) {
michael@0 954 bindingManager->RemoveFromAttachedQueue(oldBinding);
michael@0 955 }
michael@0 956
michael@0 957 nsDOMSlots *slots = DOMSlots();
michael@0 958 if (aBinding) {
michael@0 959 SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
michael@0 960 slots->mXBLBinding = aBinding;
michael@0 961 bindingManager->AddBoundContent(this);
michael@0 962 } else {
michael@0 963 slots->mXBLBinding = nullptr;
michael@0 964 bindingManager->RemoveBoundContent(this);
michael@0 965 if (oldBinding) {
michael@0 966 oldBinding->SetBoundElement(nullptr);
michael@0 967 }
michael@0 968 }
michael@0 969 }
michael@0 970
michael@0 971 nsIContent*
michael@0 972 FragmentOrElement::GetXBLInsertionParent() const
michael@0 973 {
michael@0 974 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
michael@0 975 nsDOMSlots *slots = GetExistingDOMSlots();
michael@0 976 if (slots) {
michael@0 977 return slots->mXBLInsertionParent;
michael@0 978 }
michael@0 979 }
michael@0 980
michael@0 981 return nullptr;
michael@0 982 }
michael@0 983
michael@0 984 ShadowRoot*
michael@0 985 FragmentOrElement::GetShadowRoot() const
michael@0 986 {
michael@0 987 nsDOMSlots *slots = GetExistingDOMSlots();
michael@0 988 if (slots) {
michael@0 989 return slots->mShadowRoot;
michael@0 990 }
michael@0 991 return nullptr;
michael@0 992 }
michael@0 993
michael@0 994 ShadowRoot*
michael@0 995 FragmentOrElement::GetContainingShadow() const
michael@0 996 {
michael@0 997 nsDOMSlots *slots = GetExistingDOMSlots();
michael@0 998 if (slots) {
michael@0 999 return slots->mContainingShadow;
michael@0 1000 }
michael@0 1001 return nullptr;
michael@0 1002 }
michael@0 1003
michael@0 1004 void
michael@0 1005 FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot)
michael@0 1006 {
michael@0 1007 nsDOMSlots *slots = DOMSlots();
michael@0 1008 slots->mShadowRoot = aShadowRoot;
michael@0 1009 }
michael@0 1010
michael@0 1011 void
michael@0 1012 FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
michael@0 1013 {
michael@0 1014 nsDOMSlots *slots = DOMSlots();
michael@0 1015 if (aContent) {
michael@0 1016 SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
michael@0 1017 }
michael@0 1018 slots->mXBLInsertionParent = aContent;
michael@0 1019 }
michael@0 1020
michael@0 1021 CustomElementData*
michael@0 1022 FragmentOrElement::GetCustomElementData() const
michael@0 1023 {
michael@0 1024 nsDOMSlots *slots = GetExistingDOMSlots();
michael@0 1025 if (slots) {
michael@0 1026 return slots->mCustomElementData;
michael@0 1027 }
michael@0 1028 return nullptr;
michael@0 1029 }
michael@0 1030
michael@0 1031 void
michael@0 1032 FragmentOrElement::SetCustomElementData(CustomElementData* aData)
michael@0 1033 {
michael@0 1034 nsDOMSlots *slots = DOMSlots();
michael@0 1035 MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
michael@0 1036 slots->mCustomElementData = aData;
michael@0 1037 }
michael@0 1038
michael@0 1039 nsresult
michael@0 1040 FragmentOrElement::InsertChildAt(nsIContent* aKid,
michael@0 1041 uint32_t aIndex,
michael@0 1042 bool aNotify)
michael@0 1043 {
michael@0 1044 NS_PRECONDITION(aKid, "null ptr");
michael@0 1045
michael@0 1046 return doInsertChildAt(aKid, aIndex, aNotify, mAttrsAndChildren);
michael@0 1047 }
michael@0 1048
michael@0 1049 void
michael@0 1050 FragmentOrElement::RemoveChildAt(uint32_t aIndex, bool aNotify)
michael@0 1051 {
michael@0 1052 nsCOMPtr<nsIContent> oldKid = mAttrsAndChildren.GetSafeChildAt(aIndex);
michael@0 1053 NS_ASSERTION(oldKid == GetChildAt(aIndex), "Unexpected child in RemoveChildAt");
michael@0 1054
michael@0 1055 if (oldKid) {
michael@0 1056 doRemoveChildAt(aIndex, aNotify, oldKid, mAttrsAndChildren);
michael@0 1057 }
michael@0 1058 }
michael@0 1059
michael@0 1060 void
michael@0 1061 FragmentOrElement::GetTextContentInternal(nsAString& aTextContent)
michael@0 1062 {
michael@0 1063 if(!nsContentUtils::GetNodeTextContent(this, true, aTextContent))
michael@0 1064 NS_RUNTIMEABORT("OOM");
michael@0 1065 }
michael@0 1066
michael@0 1067 void
michael@0 1068 FragmentOrElement::SetTextContentInternal(const nsAString& aTextContent,
michael@0 1069 ErrorResult& aError)
michael@0 1070 {
michael@0 1071 aError = nsContentUtils::SetNodeTextContent(this, aTextContent, false);
michael@0 1072 }
michael@0 1073
michael@0 1074 void
michael@0 1075 FragmentOrElement::DestroyContent()
michael@0 1076 {
michael@0 1077 nsIDocument *document = OwnerDoc();
michael@0 1078 document->BindingManager()->RemovedFromDocument(this, document);
michael@0 1079 document->ClearBoxObjectFor(this);
michael@0 1080
michael@0 1081 // XXX We really should let cycle collection do this, but that currently still
michael@0 1082 // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
michael@0 1083 ReleaseWrapper(this);
michael@0 1084
michael@0 1085 uint32_t i, count = mAttrsAndChildren.ChildCount();
michael@0 1086 for (i = 0; i < count; ++i) {
michael@0 1087 // The child can remove itself from the parent in BindToTree.
michael@0 1088 mAttrsAndChildren.ChildAt(i)->DestroyContent();
michael@0 1089 }
michael@0 1090 }
michael@0 1091
michael@0 1092 void
michael@0 1093 FragmentOrElement::SaveSubtreeState()
michael@0 1094 {
michael@0 1095 uint32_t i, count = mAttrsAndChildren.ChildCount();
michael@0 1096 for (i = 0; i < count; ++i) {
michael@0 1097 mAttrsAndChildren.ChildAt(i)->SaveSubtreeState();
michael@0 1098 }
michael@0 1099 }
michael@0 1100
michael@0 1101 //----------------------------------------------------------------------
michael@0 1102
michael@0 1103 // Generic DOMNode implementations
michael@0 1104
michael@0 1105 void
michael@0 1106 FragmentOrElement::FireNodeInserted(nsIDocument* aDoc,
michael@0 1107 nsINode* aParent,
michael@0 1108 nsTArray<nsCOMPtr<nsIContent> >& aNodes)
michael@0 1109 {
michael@0 1110 uint32_t count = aNodes.Length();
michael@0 1111 for (uint32_t i = 0; i < count; ++i) {
michael@0 1112 nsIContent* childContent = aNodes[i];
michael@0 1113
michael@0 1114 if (nsContentUtils::HasMutationListeners(childContent,
michael@0 1115 NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) {
michael@0 1116 InternalMutationEvent mutation(true, NS_MUTATION_NODEINSERTED);
michael@0 1117 mutation.mRelatedNode = do_QueryInterface(aParent);
michael@0 1118
michael@0 1119 mozAutoSubtreeModified subtree(aDoc, aParent);
michael@0 1120 (new AsyncEventDispatcher(childContent, mutation))->RunDOMEventWhenSafe();
michael@0 1121 }
michael@0 1122 }
michael@0 1123 }
michael@0 1124
michael@0 1125 //----------------------------------------------------------------------
michael@0 1126
michael@0 1127 // nsISupports implementation
michael@0 1128
michael@0 1129 #define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
michael@0 1130
michael@0 1131 class ContentUnbinder : public nsRunnable
michael@0 1132 {
michael@0 1133 public:
michael@0 1134 ContentUnbinder()
michael@0 1135 {
michael@0 1136 mLast = this;
michael@0 1137 }
michael@0 1138
michael@0 1139 ~ContentUnbinder()
michael@0 1140 {
michael@0 1141 Run();
michael@0 1142 }
michael@0 1143
michael@0 1144 void UnbindSubtree(nsIContent* aNode)
michael@0 1145 {
michael@0 1146 if (aNode->NodeType() != nsIDOMNode::ELEMENT_NODE &&
michael@0 1147 aNode->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
michael@0 1148 return;
michael@0 1149 }
michael@0 1150 FragmentOrElement* container = static_cast<FragmentOrElement*>(aNode);
michael@0 1151 uint32_t childCount = container->mAttrsAndChildren.ChildCount();
michael@0 1152 if (childCount) {
michael@0 1153 while (childCount-- > 0) {
michael@0 1154 // Hold a strong ref to the node when we remove it, because we may be
michael@0 1155 // the last reference to it. We need to call TakeChildAt() and
michael@0 1156 // update mFirstChild before calling UnbindFromTree, since this last
michael@0 1157 // can notify various observers and they should really see consistent
michael@0 1158 // tree state.
michael@0 1159 nsCOMPtr<nsIContent> child =
michael@0 1160 container->mAttrsAndChildren.TakeChildAt(childCount);
michael@0 1161 if (childCount == 0) {
michael@0 1162 container->mFirstChild = nullptr;
michael@0 1163 }
michael@0 1164 UnbindSubtree(child);
michael@0 1165 child->UnbindFromTree();
michael@0 1166 }
michael@0 1167 }
michael@0 1168 }
michael@0 1169
michael@0 1170 NS_IMETHOD Run()
michael@0 1171 {
michael@0 1172 nsAutoScriptBlocker scriptBlocker;
michael@0 1173 uint32_t len = mSubtreeRoots.Length();
michael@0 1174 if (len) {
michael@0 1175 PRTime start = PR_Now();
michael@0 1176 for (uint32_t i = 0; i < len; ++i) {
michael@0 1177 UnbindSubtree(mSubtreeRoots[i]);
michael@0 1178 }
michael@0 1179 mSubtreeRoots.Clear();
michael@0 1180 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND,
michael@0 1181 uint32_t(PR_Now() - start) / PR_USEC_PER_MSEC);
michael@0 1182 }
michael@0 1183 nsCycleCollector_dispatchDeferredDeletion();
michael@0 1184 if (this == sContentUnbinder) {
michael@0 1185 sContentUnbinder = nullptr;
michael@0 1186 if (mNext) {
michael@0 1187 nsRefPtr<ContentUnbinder> next;
michael@0 1188 next.swap(mNext);
michael@0 1189 sContentUnbinder = next;
michael@0 1190 next->mLast = mLast;
michael@0 1191 mLast = nullptr;
michael@0 1192 NS_DispatchToMainThread(next);
michael@0 1193 }
michael@0 1194 }
michael@0 1195 return NS_OK;
michael@0 1196 }
michael@0 1197
michael@0 1198 static void UnbindAll()
michael@0 1199 {
michael@0 1200 nsRefPtr<ContentUnbinder> ub = sContentUnbinder;
michael@0 1201 sContentUnbinder = nullptr;
michael@0 1202 while (ub) {
michael@0 1203 ub->Run();
michael@0 1204 ub = ub->mNext;
michael@0 1205 }
michael@0 1206 }
michael@0 1207
michael@0 1208 static void Append(nsIContent* aSubtreeRoot)
michael@0 1209 {
michael@0 1210 if (!sContentUnbinder) {
michael@0 1211 sContentUnbinder = new ContentUnbinder();
michael@0 1212 nsCOMPtr<nsIRunnable> e = sContentUnbinder;
michael@0 1213 NS_DispatchToMainThread(e);
michael@0 1214 }
michael@0 1215
michael@0 1216 if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
michael@0 1217 SUBTREE_UNBINDINGS_PER_RUNNABLE) {
michael@0 1218 sContentUnbinder->mLast->mNext = new ContentUnbinder();
michael@0 1219 sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
michael@0 1220 }
michael@0 1221 sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
michael@0 1222 }
michael@0 1223
michael@0 1224 private:
michael@0 1225 nsAutoTArray<nsCOMPtr<nsIContent>,
michael@0 1226 SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots;
michael@0 1227 nsRefPtr<ContentUnbinder> mNext;
michael@0 1228 ContentUnbinder* mLast;
michael@0 1229 static ContentUnbinder* sContentUnbinder;
michael@0 1230 };
michael@0 1231
michael@0 1232 ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr;
michael@0 1233
michael@0 1234 void
michael@0 1235 FragmentOrElement::ClearContentUnbinder()
michael@0 1236 {
michael@0 1237 ContentUnbinder::UnbindAll();
michael@0 1238 }
michael@0 1239
michael@0 1240 NS_IMPL_CYCLE_COLLECTION_CLASS(FragmentOrElement)
michael@0 1241
michael@0 1242 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
michael@0 1243 nsINode::Unlink(tmp);
michael@0 1244
michael@0 1245 if (tmp->HasProperties()) {
michael@0 1246 if (tmp->IsHTML()) {
michael@0 1247 nsIAtom*** props = nsGenericHTMLElement::PropertiesToTraverseAndUnlink();
michael@0 1248 for (uint32_t i = 0; props[i]; ++i) {
michael@0 1249 tmp->DeleteProperty(*props[i]);
michael@0 1250 }
michael@0 1251 }
michael@0 1252 }
michael@0 1253
michael@0 1254 // Unlink child content (and unbind our subtree).
michael@0 1255 if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) {
michael@0 1256 uint32_t childCount = tmp->mAttrsAndChildren.ChildCount();
michael@0 1257 if (childCount) {
michael@0 1258 // Don't allow script to run while we're unbinding everything.
michael@0 1259 nsAutoScriptBlocker scriptBlocker;
michael@0 1260 while (childCount-- > 0) {
michael@0 1261 // Hold a strong ref to the node when we remove it, because we may be
michael@0 1262 // the last reference to it. We need to call TakeChildAt() and
michael@0 1263 // update mFirstChild before calling UnbindFromTree, since this last
michael@0 1264 // can notify various observers and they should really see consistent
michael@0 1265 // tree state.
michael@0 1266 nsCOMPtr<nsIContent> child = tmp->mAttrsAndChildren.TakeChildAt(childCount);
michael@0 1267 if (childCount == 0) {
michael@0 1268 tmp->mFirstChild = nullptr;
michael@0 1269 }
michael@0 1270 child->UnbindFromTree();
michael@0 1271 }
michael@0 1272 }
michael@0 1273 } else if (!tmp->GetParent() && tmp->mAttrsAndChildren.ChildCount()) {
michael@0 1274 ContentUnbinder::Append(tmp);
michael@0 1275 } /* else {
michael@0 1276 The subtree root will end up to a ContentUnbinder, and that will
michael@0 1277 unbind the child nodes.
michael@0 1278 } */
michael@0 1279
michael@0 1280 // Unlink any DOM slots of interest.
michael@0 1281 {
michael@0 1282 nsDOMSlots *slots = tmp->GetExistingDOMSlots();
michael@0 1283 if (slots) {
michael@0 1284 slots->Unlink(tmp->IsXUL());
michael@0 1285 }
michael@0 1286 }
michael@0 1287
michael@0 1288 {
michael@0 1289 nsIDocument *doc;
michael@0 1290 if (!tmp->GetParentNode() && (doc = tmp->OwnerDoc())) {
michael@0 1291 doc->BindingManager()->RemovedFromDocument(tmp, doc);
michael@0 1292 }
michael@0 1293 }
michael@0 1294 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 1295
michael@0 1296 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)
michael@0 1297
michael@0 1298 void
michael@0 1299 FragmentOrElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
michael@0 1300 void* aData)
michael@0 1301 {
michael@0 1302 uint32_t* gen = static_cast<uint32_t*>(aData);
michael@0 1303 xpc_MarkInCCGeneration(static_cast<nsISupports*>(aChild), *gen);
michael@0 1304 }
michael@0 1305
michael@0 1306 void
michael@0 1307 FragmentOrElement::MarkUserDataHandler(void* aObject, nsIAtom* aKey,
michael@0 1308 void* aChild, void* aData)
michael@0 1309 {
michael@0 1310 xpc_TryUnmarkWrappedGrayObject(static_cast<nsISupports*>(aChild));
michael@0 1311 }
michael@0 1312
michael@0 1313 void
michael@0 1314 FragmentOrElement::MarkNodeChildren(nsINode* aNode)
michael@0 1315 {
michael@0 1316 JSObject* o = GetJSObjectChild(aNode);
michael@0 1317 if (o) {
michael@0 1318 JS::ExposeObjectToActiveJS(o);
michael@0 1319 }
michael@0 1320
michael@0 1321 EventListenerManager* elm = aNode->GetExistingListenerManager();
michael@0 1322 if (elm) {
michael@0 1323 elm->MarkForCC();
michael@0 1324 }
michael@0 1325
michael@0 1326 if (aNode->HasProperties()) {
michael@0 1327 nsIDocument* ownerDoc = aNode->OwnerDoc();
michael@0 1328 ownerDoc->PropertyTable(DOM_USER_DATA)->
michael@0 1329 Enumerate(aNode, FragmentOrElement::MarkUserData,
michael@0 1330 &nsCCUncollectableMarker::sGeneration);
michael@0 1331 ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->
michael@0 1332 Enumerate(aNode, FragmentOrElement::MarkUserDataHandler,
michael@0 1333 &nsCCUncollectableMarker::sGeneration);
michael@0 1334 }
michael@0 1335 }
michael@0 1336
michael@0 1337 nsINode*
michael@0 1338 FindOptimizableSubtreeRoot(nsINode* aNode)
michael@0 1339 {
michael@0 1340 nsINode* p;
michael@0 1341 while ((p = aNode->GetParentNode())) {
michael@0 1342 if (aNode->UnoptimizableCCNode()) {
michael@0 1343 return nullptr;
michael@0 1344 }
michael@0 1345 aNode = p;
michael@0 1346 }
michael@0 1347
michael@0 1348 if (aNode->UnoptimizableCCNode()) {
michael@0 1349 return nullptr;
michael@0 1350 }
michael@0 1351 return aNode;
michael@0 1352 }
michael@0 1353
michael@0 1354 StaticAutoPtr<nsTHashtable<nsPtrHashKey<nsINode>>> gCCBlackMarkedNodes;
michael@0 1355
michael@0 1356 static PLDHashOperator
michael@0 1357 VisitBlackMarkedNode(nsPtrHashKey<nsINode>* aEntry, void*)
michael@0 1358 {
michael@0 1359 nsINode* n = aEntry->GetKey();
michael@0 1360 n->SetCCMarkedRoot(false);
michael@0 1361 n->SetInCCBlackTree(false);
michael@0 1362 return PL_DHASH_NEXT;
michael@0 1363 }
michael@0 1364
michael@0 1365 static void
michael@0 1366 ClearBlackMarkedNodes()
michael@0 1367 {
michael@0 1368 if (!gCCBlackMarkedNodes) {
michael@0 1369 return;
michael@0 1370 }
michael@0 1371 gCCBlackMarkedNodes->EnumerateEntries(VisitBlackMarkedNode, nullptr);
michael@0 1372 gCCBlackMarkedNodes = nullptr;
michael@0 1373 }
michael@0 1374
michael@0 1375 // static
michael@0 1376 void
michael@0 1377 FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode)
michael@0 1378 {
michael@0 1379 if (!gCCBlackMarkedNodes) {
michael@0 1380 return;
michael@0 1381 }
michael@0 1382 gCCBlackMarkedNodes->RemoveEntry(aNode);
michael@0 1383 }
michael@0 1384
michael@0 1385 // static
michael@0 1386 bool
michael@0 1387 FragmentOrElement::CanSkipInCC(nsINode* aNode)
michael@0 1388 {
michael@0 1389 // Don't try to optimize anything during shutdown.
michael@0 1390 if (nsCCUncollectableMarker::sGeneration == 0) {
michael@0 1391 return false;
michael@0 1392 }
michael@0 1393
michael@0 1394 nsIDocument* currentDoc = aNode->GetCurrentDoc();
michael@0 1395 if (currentDoc &&
michael@0 1396 nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
michael@0 1397 return !NeedsScriptTraverse(aNode);
michael@0 1398 }
michael@0 1399
michael@0 1400 // Bail out early if aNode is somewhere in anonymous content,
michael@0 1401 // or otherwise unusual.
michael@0 1402 if (aNode->UnoptimizableCCNode()) {
michael@0 1403 return false;
michael@0 1404 }
michael@0 1405
michael@0 1406 nsINode* root =
michael@0 1407 currentDoc ? static_cast<nsINode*>(currentDoc) :
michael@0 1408 FindOptimizableSubtreeRoot(aNode);
michael@0 1409 if (!root) {
michael@0 1410 return false;
michael@0 1411 }
michael@0 1412
michael@0 1413 // Subtree has been traversed already.
michael@0 1414 if (root->CCMarkedRoot()) {
michael@0 1415 return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
michael@0 1416 }
michael@0 1417
michael@0 1418 if (!gCCBlackMarkedNodes) {
michael@0 1419 gCCBlackMarkedNodes = new nsTHashtable<nsPtrHashKey<nsINode> >(1020);
michael@0 1420 }
michael@0 1421
michael@0 1422 // nodesToUnpurple contains nodes which will be removed
michael@0 1423 // from the purple buffer if the DOM tree is black.
michael@0 1424 nsAutoTArray<nsIContent*, 1020> nodesToUnpurple;
michael@0 1425 // grayNodes need script traverse, so they aren't removed from
michael@0 1426 // the purple buffer, but are marked to be in black subtree so that
michael@0 1427 // traverse is faster.
michael@0 1428 nsAutoTArray<nsINode*, 1020> grayNodes;
michael@0 1429
michael@0 1430 bool foundBlack = root->IsBlack();
michael@0 1431 if (root != currentDoc) {
michael@0 1432 currentDoc = nullptr;
michael@0 1433 if (NeedsScriptTraverse(root)) {
michael@0 1434 grayNodes.AppendElement(root);
michael@0 1435 } else if (static_cast<nsIContent*>(root)->IsPurple()) {
michael@0 1436 nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root));
michael@0 1437 }
michael@0 1438 }
michael@0 1439
michael@0 1440 // Traverse the subtree and check if we could know without CC
michael@0 1441 // that it is black.
michael@0 1442 // Note, this traverse is non-virtual and inline, so it should be a lot faster
michael@0 1443 // than CC's generic traverse.
michael@0 1444 for (nsIContent* node = root->GetFirstChild(); node;
michael@0 1445 node = node->GetNextNode(root)) {
michael@0 1446 foundBlack = foundBlack || node->IsBlack();
michael@0 1447 if (foundBlack && currentDoc) {
michael@0 1448 // If we can mark the whole document black, no need to optimize
michael@0 1449 // so much, since when the next purple node in the document will be
michael@0 1450 // handled, it is fast to check that currentDoc is in CCGeneration.
michael@0 1451 break;
michael@0 1452 }
michael@0 1453 if (NeedsScriptTraverse(node)) {
michael@0 1454 // Gray nodes need real CC traverse.
michael@0 1455 grayNodes.AppendElement(node);
michael@0 1456 } else if (node->IsPurple()) {
michael@0 1457 nodesToUnpurple.AppendElement(node);
michael@0 1458 }
michael@0 1459 }
michael@0 1460
michael@0 1461 root->SetCCMarkedRoot(true);
michael@0 1462 root->SetInCCBlackTree(foundBlack);
michael@0 1463 gCCBlackMarkedNodes->PutEntry(root);
michael@0 1464
michael@0 1465 if (!foundBlack) {
michael@0 1466 return false;
michael@0 1467 }
michael@0 1468
michael@0 1469 if (currentDoc) {
michael@0 1470 // Special case documents. If we know the document is black,
michael@0 1471 // we can mark the document to be in CCGeneration.
michael@0 1472 currentDoc->
michael@0 1473 MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
michael@0 1474 } else {
michael@0 1475 for (uint32_t i = 0; i < grayNodes.Length(); ++i) {
michael@0 1476 nsINode* node = grayNodes[i];
michael@0 1477 node->SetInCCBlackTree(true);
michael@0 1478 gCCBlackMarkedNodes->PutEntry(node);
michael@0 1479 }
michael@0 1480 }
michael@0 1481
michael@0 1482 // Subtree is black, we can remove non-gray purple nodes from
michael@0 1483 // purple buffer.
michael@0 1484 for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) {
michael@0 1485 nsIContent* purple = nodesToUnpurple[i];
michael@0 1486 // Can't remove currently handled purple node.
michael@0 1487 if (purple != aNode) {
michael@0 1488 purple->RemovePurple();
michael@0 1489 }
michael@0 1490 }
michael@0 1491 return !NeedsScriptTraverse(aNode);
michael@0 1492 }
michael@0 1493
michael@0 1494 nsAutoTArray<nsINode*, 1020>* gPurpleRoots = nullptr;
michael@0 1495 nsAutoTArray<nsIContent*, 1020>* gNodesToUnbind = nullptr;
michael@0 1496
michael@0 1497 void ClearCycleCollectorCleanupData()
michael@0 1498 {
michael@0 1499 if (gPurpleRoots) {
michael@0 1500 uint32_t len = gPurpleRoots->Length();
michael@0 1501 for (uint32_t i = 0; i < len; ++i) {
michael@0 1502 nsINode* n = gPurpleRoots->ElementAt(i);
michael@0 1503 n->SetIsPurpleRoot(false);
michael@0 1504 }
michael@0 1505 delete gPurpleRoots;
michael@0 1506 gPurpleRoots = nullptr;
michael@0 1507 }
michael@0 1508 if (gNodesToUnbind) {
michael@0 1509 uint32_t len = gNodesToUnbind->Length();
michael@0 1510 for (uint32_t i = 0; i < len; ++i) {
michael@0 1511 nsIContent* c = gNodesToUnbind->ElementAt(i);
michael@0 1512 c->SetIsPurpleRoot(false);
michael@0 1513 ContentUnbinder::Append(c);
michael@0 1514 }
michael@0 1515 delete gNodesToUnbind;
michael@0 1516 gNodesToUnbind = nullptr;
michael@0 1517 }
michael@0 1518 }
michael@0 1519
michael@0 1520 static bool
michael@0 1521 ShouldClearPurple(nsIContent* aContent)
michael@0 1522 {
michael@0 1523 if (aContent && aContent->IsPurple()) {
michael@0 1524 return true;
michael@0 1525 }
michael@0 1526
michael@0 1527 JSObject* o = GetJSObjectChild(aContent);
michael@0 1528 if (o && xpc_IsGrayGCThing(o)) {
michael@0 1529 return true;
michael@0 1530 }
michael@0 1531
michael@0 1532 if (aContent->HasListenerManager()) {
michael@0 1533 return true;
michael@0 1534 }
michael@0 1535
michael@0 1536 return aContent->HasProperties();
michael@0 1537 }
michael@0 1538
michael@0 1539 // If aNode is not optimizable, but is an element
michael@0 1540 // with a frame in a document which has currently active presshell,
michael@0 1541 // we can act as if it was optimizable. When the primary frame dies, aNode
michael@0 1542 // will end up to the purple buffer because of the refcount change.
michael@0 1543 bool
michael@0 1544 NodeHasActiveFrame(nsIDocument* aCurrentDoc, nsINode* aNode)
michael@0 1545 {
michael@0 1546 return aCurrentDoc->GetShell() && aNode->IsElement() &&
michael@0 1547 aNode->AsElement()->GetPrimaryFrame();
michael@0 1548 }
michael@0 1549
michael@0 1550 bool
michael@0 1551 OwnedByBindingManager(nsIDocument* aCurrentDoc, nsINode* aNode)
michael@0 1552 {
michael@0 1553 return aNode->IsElement() && aNode->AsElement()->GetXBLBinding();
michael@0 1554 }
michael@0 1555
michael@0 1556 // CanSkip checks if aNode is black, and if it is, returns
michael@0 1557 // true. If aNode is in a black DOM tree, CanSkip may also remove other objects
michael@0 1558 // from purple buffer and unmark event listeners and user data.
michael@0 1559 // If the root of the DOM tree is a document, less optimizations are done
michael@0 1560 // since checking the blackness of the current document is usually fast and we
michael@0 1561 // don't want slow down such common cases.
michael@0 1562 bool
michael@0 1563 FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed)
michael@0 1564 {
michael@0 1565 // Don't try to optimize anything during shutdown.
michael@0 1566 if (nsCCUncollectableMarker::sGeneration == 0) {
michael@0 1567 return false;
michael@0 1568 }
michael@0 1569
michael@0 1570 bool unoptimizable = aNode->UnoptimizableCCNode();
michael@0 1571 nsIDocument* currentDoc = aNode->GetCurrentDoc();
michael@0 1572 if (currentDoc &&
michael@0 1573 nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration()) &&
michael@0 1574 (!unoptimizable || NodeHasActiveFrame(currentDoc, aNode) ||
michael@0 1575 OwnedByBindingManager(currentDoc, aNode))) {
michael@0 1576 MarkNodeChildren(aNode);
michael@0 1577 return true;
michael@0 1578 }
michael@0 1579
michael@0 1580 if (unoptimizable) {
michael@0 1581 return false;
michael@0 1582 }
michael@0 1583
michael@0 1584 nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc) :
michael@0 1585 FindOptimizableSubtreeRoot(aNode);
michael@0 1586 if (!root) {
michael@0 1587 return false;
michael@0 1588 }
michael@0 1589
michael@0 1590 // Subtree has been traversed already, and aNode has
michael@0 1591 // been handled in a way that doesn't require revisiting it.
michael@0 1592 if (root->IsPurpleRoot()) {
michael@0 1593 return false;
michael@0 1594 }
michael@0 1595
michael@0 1596 // nodesToClear contains nodes which are either purple or
michael@0 1597 // gray.
michael@0 1598 nsAutoTArray<nsIContent*, 1020> nodesToClear;
michael@0 1599
michael@0 1600 bool foundBlack = root->IsBlack();
michael@0 1601 bool domOnlyCycle = false;
michael@0 1602 if (root != currentDoc) {
michael@0 1603 currentDoc = nullptr;
michael@0 1604 if (!foundBlack) {
michael@0 1605 domOnlyCycle = static_cast<nsIContent*>(root)->OwnedOnlyByTheDOMTree();
michael@0 1606 }
michael@0 1607 if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
michael@0 1608 nodesToClear.AppendElement(static_cast<nsIContent*>(root));
michael@0 1609 }
michael@0 1610 }
michael@0 1611
michael@0 1612 // Traverse the subtree and check if we could know without CC
michael@0 1613 // that it is black.
michael@0 1614 // Note, this traverse is non-virtual and inline, so it should be a lot faster
michael@0 1615 // than CC's generic traverse.
michael@0 1616 for (nsIContent* node = root->GetFirstChild(); node;
michael@0 1617 node = node->GetNextNode(root)) {
michael@0 1618 foundBlack = foundBlack || node->IsBlack();
michael@0 1619 if (foundBlack) {
michael@0 1620 domOnlyCycle = false;
michael@0 1621 if (currentDoc) {
michael@0 1622 // If we can mark the whole document black, no need to optimize
michael@0 1623 // so much, since when the next purple node in the document will be
michael@0 1624 // handled, it is fast to check that the currentDoc is in CCGeneration.
michael@0 1625 break;
michael@0 1626 }
michael@0 1627 // No need to put stuff to the nodesToClear array, if we can clear it
michael@0 1628 // already here.
michael@0 1629 if (node->IsPurple() && (node != aNode || aRemovingAllowed)) {
michael@0 1630 node->RemovePurple();
michael@0 1631 }
michael@0 1632 MarkNodeChildren(node);
michael@0 1633 } else {
michael@0 1634 domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree();
michael@0 1635 if (ShouldClearPurple(node)) {
michael@0 1636 // Collect interesting nodes which we can clear if we find that
michael@0 1637 // they are kept alive in a black tree or are in a DOM-only cycle.
michael@0 1638 nodesToClear.AppendElement(node);
michael@0 1639 }
michael@0 1640 }
michael@0 1641 }
michael@0 1642
michael@0 1643 if (!currentDoc || !foundBlack) {
michael@0 1644 root->SetIsPurpleRoot(true);
michael@0 1645 if (domOnlyCycle) {
michael@0 1646 if (!gNodesToUnbind) {
michael@0 1647 gNodesToUnbind = new nsAutoTArray<nsIContent*, 1020>();
michael@0 1648 }
michael@0 1649 gNodesToUnbind->AppendElement(static_cast<nsIContent*>(root));
michael@0 1650 for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
michael@0 1651 nsIContent* n = nodesToClear[i];
michael@0 1652 if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
michael@0 1653 n->RemovePurple();
michael@0 1654 }
michael@0 1655 }
michael@0 1656 return true;
michael@0 1657 } else {
michael@0 1658 if (!gPurpleRoots) {
michael@0 1659 gPurpleRoots = new nsAutoTArray<nsINode*, 1020>();
michael@0 1660 }
michael@0 1661 gPurpleRoots->AppendElement(root);
michael@0 1662 }
michael@0 1663 }
michael@0 1664
michael@0 1665 if (!foundBlack) {
michael@0 1666 return false;
michael@0 1667 }
michael@0 1668
michael@0 1669 if (currentDoc) {
michael@0 1670 // Special case documents. If we know the document is black,
michael@0 1671 // we can mark the document to be in CCGeneration.
michael@0 1672 currentDoc->
michael@0 1673 MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
michael@0 1674 MarkNodeChildren(currentDoc);
michael@0 1675 }
michael@0 1676
michael@0 1677 // Subtree is black, so we can remove purple nodes from
michael@0 1678 // purple buffer and mark stuff that to be certainly alive.
michael@0 1679 for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
michael@0 1680 nsIContent* n = nodesToClear[i];
michael@0 1681 MarkNodeChildren(n);
michael@0 1682 // Can't remove currently handled purple node,
michael@0 1683 // unless aRemovingAllowed is true.
michael@0 1684 if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
michael@0 1685 n->RemovePurple();
michael@0 1686 }
michael@0 1687 }
michael@0 1688 return true;
michael@0 1689 }
michael@0 1690
michael@0 1691 bool
michael@0 1692 FragmentOrElement::CanSkipThis(nsINode* aNode)
michael@0 1693 {
michael@0 1694 if (nsCCUncollectableMarker::sGeneration == 0) {
michael@0 1695 return false;
michael@0 1696 }
michael@0 1697 if (aNode->IsBlack()) {
michael@0 1698 return true;
michael@0 1699 }
michael@0 1700 nsIDocument* c = aNode->GetCurrentDoc();
michael@0 1701 return
michael@0 1702 ((c && nsCCUncollectableMarker::InGeneration(c->GetMarkedCCGeneration())) ||
michael@0 1703 aNode->InCCBlackTree()) && !NeedsScriptTraverse(aNode);
michael@0 1704 }
michael@0 1705
michael@0 1706 void
michael@0 1707 FragmentOrElement::InitCCCallbacks()
michael@0 1708 {
michael@0 1709 nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData);
michael@0 1710 nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
michael@0 1711 }
michael@0 1712
michael@0 1713 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement)
michael@0 1714 return FragmentOrElement::CanSkip(tmp, aRemovingAllowed);
michael@0 1715 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
michael@0 1716
michael@0 1717 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement)
michael@0 1718 return FragmentOrElement::CanSkipInCC(tmp);
michael@0 1719 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
michael@0 1720
michael@0 1721 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement)
michael@0 1722 return FragmentOrElement::CanSkipThis(tmp);
michael@0 1723 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
michael@0 1724
michael@0 1725 static const char* kNSURIs[] = {
michael@0 1726 " ([none])",
michael@0 1727 " (xmlns)",
michael@0 1728 " (xml)",
michael@0 1729 " (xhtml)",
michael@0 1730 " (XLink)",
michael@0 1731 " (XSLT)",
michael@0 1732 " (XBL)",
michael@0 1733 " (MathML)",
michael@0 1734 " (RDF)",
michael@0 1735 " (XUL)",
michael@0 1736 " (SVG)",
michael@0 1737 " (XML Events)"
michael@0 1738 };
michael@0 1739
michael@0 1740 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
michael@0 1741 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
michael@0 1742 char name[512];
michael@0 1743 uint32_t nsid = tmp->GetNameSpaceID();
michael@0 1744 nsAtomCString localName(tmp->NodeInfo()->NameAtom());
michael@0 1745 nsAutoCString uri;
michael@0 1746 if (tmp->OwnerDoc()->GetDocumentURI()) {
michael@0 1747 tmp->OwnerDoc()->GetDocumentURI()->GetSpec(uri);
michael@0 1748 }
michael@0 1749
michael@0 1750 nsAutoString id;
michael@0 1751 nsIAtom* idAtom = tmp->GetID();
michael@0 1752 if (idAtom) {
michael@0 1753 id.AppendLiteral(" id='");
michael@0 1754 id.Append(nsDependentAtomString(idAtom));
michael@0 1755 id.AppendLiteral("'");
michael@0 1756 }
michael@0 1757
michael@0 1758 nsAutoString classes;
michael@0 1759 const nsAttrValue* classAttrValue = tmp->GetClasses();
michael@0 1760 if (classAttrValue) {
michael@0 1761 classes.AppendLiteral(" class='");
michael@0 1762 nsAutoString classString;
michael@0 1763 classAttrValue->ToString(classString);
michael@0 1764 classString.ReplaceChar(char16_t('\n'), char16_t(' '));
michael@0 1765 classes.Append(classString);
michael@0 1766 classes.AppendLiteral("'");
michael@0 1767 }
michael@0 1768
michael@0 1769 nsAutoCString orphan;
michael@0 1770 if (!tmp->IsInDoc() &&
michael@0 1771 // Ignore xbl:content, which is never in the document and hence always
michael@0 1772 // appears to be orphaned.
michael@0 1773 !tmp->NodeInfo()->Equals(nsGkAtoms::content, kNameSpaceID_XBL)) {
michael@0 1774 orphan.AppendLiteral(" (orphan)");
michael@0 1775 }
michael@0 1776
michael@0 1777 const char* nsuri = nsid < ArrayLength(kNSURIs) ? kNSURIs[nsid] : "";
michael@0 1778 PR_snprintf(name, sizeof(name), "FragmentOrElement%s %s%s%s%s %s",
michael@0 1779 nsuri,
michael@0 1780 localName.get(),
michael@0 1781 NS_ConvertUTF16toUTF8(id).get(),
michael@0 1782 NS_ConvertUTF16toUTF8(classes).get(),
michael@0 1783 orphan.get(),
michael@0 1784 uri.get());
michael@0 1785 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
michael@0 1786 }
michael@0 1787 else {
michael@0 1788 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get())
michael@0 1789 }
michael@0 1790
michael@0 1791 // Always need to traverse script objects, so do that before we check
michael@0 1792 // if we're uncollectable.
michael@0 1793 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
michael@0 1794
michael@0 1795 if (!nsINode::Traverse(tmp, cb)) {
michael@0 1796 return NS_SUCCESS_INTERRUPTED_TRAVERSE;
michael@0 1797 }
michael@0 1798
michael@0 1799 tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb);
michael@0 1800
michael@0 1801 if (tmp->HasProperties()) {
michael@0 1802 if (tmp->IsHTML()) {
michael@0 1803 nsIAtom*** props = nsGenericHTMLElement::PropertiesToTraverseAndUnlink();
michael@0 1804 for (uint32_t i = 0; props[i]; ++i) {
michael@0 1805 nsISupports* property =
michael@0 1806 static_cast<nsISupports*>(tmp->GetProperty(*props[i]));
michael@0 1807 cb.NoteXPCOMChild(property);
michael@0 1808 }
michael@0 1809 }
michael@0 1810 }
michael@0 1811
michael@0 1812 // Traverse attribute names and child content.
michael@0 1813 {
michael@0 1814 uint32_t i;
michael@0 1815 uint32_t attrs = tmp->mAttrsAndChildren.AttrCount();
michael@0 1816 for (i = 0; i < attrs; i++) {
michael@0 1817 const nsAttrName* name = tmp->mAttrsAndChildren.AttrNameAt(i);
michael@0 1818 if (!name->IsAtom()) {
michael@0 1819 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
michael@0 1820 "mAttrsAndChildren[i]->NodeInfo()");
michael@0 1821 cb.NoteXPCOMChild(name->NodeInfo());
michael@0 1822 }
michael@0 1823 }
michael@0 1824
michael@0 1825 uint32_t kids = tmp->mAttrsAndChildren.ChildCount();
michael@0 1826 for (i = 0; i < kids; i++) {
michael@0 1827 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrsAndChildren[i]");
michael@0 1828 cb.NoteXPCOMChild(tmp->mAttrsAndChildren.GetSafeChildAt(i));
michael@0 1829 }
michael@0 1830 }
michael@0 1831
michael@0 1832 // Traverse any DOM slots of interest.
michael@0 1833 {
michael@0 1834 nsDOMSlots *slots = tmp->GetExistingDOMSlots();
michael@0 1835 if (slots) {
michael@0 1836 slots->Traverse(cb, tmp->IsXUL());
michael@0 1837 }
michael@0 1838 }
michael@0 1839 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 1840
michael@0 1841
michael@0 1842 NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
michael@0 1843 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
michael@0 1844 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
michael@0 1845 NS_INTERFACE_MAP_ENTRY(Element)
michael@0 1846 NS_INTERFACE_MAP_ENTRY(nsIContent)
michael@0 1847 NS_INTERFACE_MAP_ENTRY(nsINode)
michael@0 1848 NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
michael@0 1849 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
michael@0 1850 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
michael@0 1851 new nsNodeSupportsWeakRefTearoff(this))
michael@0 1852 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver,
michael@0 1853 new nsNode3Tearoff(this))
michael@0 1854 // DOM bindings depend on the identity pointer being the
michael@0 1855 // same as nsINode (which nsIContent inherits).
michael@0 1856 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
michael@0 1857 NS_INTERFACE_MAP_END
michael@0 1858
michael@0 1859 NS_IMPL_CYCLE_COLLECTING_ADDREF(FragmentOrElement)
michael@0 1860 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FragmentOrElement,
michael@0 1861 nsNodeUtils::LastRelease(this))
michael@0 1862
michael@0 1863 //----------------------------------------------------------------------
michael@0 1864
michael@0 1865 nsresult
michael@0 1866 FragmentOrElement::CopyInnerTo(FragmentOrElement* aDst)
michael@0 1867 {
michael@0 1868 uint32_t i, count = mAttrsAndChildren.AttrCount();
michael@0 1869 for (i = 0; i < count; ++i) {
michael@0 1870 const nsAttrName* name = mAttrsAndChildren.AttrNameAt(i);
michael@0 1871 const nsAttrValue* value = mAttrsAndChildren.AttrAt(i);
michael@0 1872 nsAutoString valStr;
michael@0 1873 value->ToString(valStr);
michael@0 1874 nsresult rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(),
michael@0 1875 name->GetPrefix(), valStr, false);
michael@0 1876 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1877 }
michael@0 1878
michael@0 1879 return NS_OK;
michael@0 1880 }
michael@0 1881
michael@0 1882 const nsTextFragment*
michael@0 1883 FragmentOrElement::GetText()
michael@0 1884 {
michael@0 1885 return nullptr;
michael@0 1886 }
michael@0 1887
michael@0 1888 uint32_t
michael@0 1889 FragmentOrElement::TextLength() const
michael@0 1890 {
michael@0 1891 // We can remove this assertion if it turns out to be useful to be able
michael@0 1892 // to depend on this returning 0
michael@0 1893 NS_NOTREACHED("called FragmentOrElement::TextLength");
michael@0 1894
michael@0 1895 return 0;
michael@0 1896 }
michael@0 1897
michael@0 1898 nsresult
michael@0 1899 FragmentOrElement::SetText(const char16_t* aBuffer, uint32_t aLength,
michael@0 1900 bool aNotify)
michael@0 1901 {
michael@0 1902 NS_ERROR("called FragmentOrElement::SetText");
michael@0 1903
michael@0 1904 return NS_ERROR_FAILURE;
michael@0 1905 }
michael@0 1906
michael@0 1907 nsresult
michael@0 1908 FragmentOrElement::AppendText(const char16_t* aBuffer, uint32_t aLength,
michael@0 1909 bool aNotify)
michael@0 1910 {
michael@0 1911 NS_ERROR("called FragmentOrElement::AppendText");
michael@0 1912
michael@0 1913 return NS_ERROR_FAILURE;
michael@0 1914 }
michael@0 1915
michael@0 1916 bool
michael@0 1917 FragmentOrElement::TextIsOnlyWhitespace()
michael@0 1918 {
michael@0 1919 return false;
michael@0 1920 }
michael@0 1921
michael@0 1922 bool
michael@0 1923 FragmentOrElement::HasTextForTranslation()
michael@0 1924 {
michael@0 1925 return false;
michael@0 1926 }
michael@0 1927
michael@0 1928 void
michael@0 1929 FragmentOrElement::AppendTextTo(nsAString& aResult)
michael@0 1930 {
michael@0 1931 // We can remove this assertion if it turns out to be useful to be able
michael@0 1932 // to depend on this appending nothing.
michael@0 1933 NS_NOTREACHED("called FragmentOrElement::TextLength");
michael@0 1934 }
michael@0 1935
michael@0 1936 bool
michael@0 1937 FragmentOrElement::AppendTextTo(nsAString& aResult, const mozilla::fallible_t&)
michael@0 1938 {
michael@0 1939 // We can remove this assertion if it turns out to be useful to be able
michael@0 1940 // to depend on this appending nothing.
michael@0 1941 NS_NOTREACHED("called FragmentOrElement::TextLength");
michael@0 1942
michael@0 1943 return false;
michael@0 1944 }
michael@0 1945
michael@0 1946 uint32_t
michael@0 1947 FragmentOrElement::GetChildCount() const
michael@0 1948 {
michael@0 1949 return mAttrsAndChildren.ChildCount();
michael@0 1950 }
michael@0 1951
michael@0 1952 nsIContent *
michael@0 1953 FragmentOrElement::GetChildAt(uint32_t aIndex) const
michael@0 1954 {
michael@0 1955 return mAttrsAndChildren.GetSafeChildAt(aIndex);
michael@0 1956 }
michael@0 1957
michael@0 1958 nsIContent * const *
michael@0 1959 FragmentOrElement::GetChildArray(uint32_t* aChildCount) const
michael@0 1960 {
michael@0 1961 return mAttrsAndChildren.GetChildArray(aChildCount);
michael@0 1962 }
michael@0 1963
michael@0 1964 int32_t
michael@0 1965 FragmentOrElement::IndexOf(const nsINode* aPossibleChild) const
michael@0 1966 {
michael@0 1967 return mAttrsAndChildren.IndexOfChild(aPossibleChild);
michael@0 1968 }
michael@0 1969
michael@0 1970 // Try to keep the size of StringBuilder close to a jemalloc bucket size.
michael@0 1971 #define STRING_BUFFER_UNITS 1020
michael@0 1972
michael@0 1973 namespace {
michael@0 1974
michael@0 1975 // We put StringBuilder in the anonymous namespace to prevent anything outside
michael@0 1976 // this file from accidentally being linked against it.
michael@0 1977
michael@0 1978 class StringBuilder
michael@0 1979 {
michael@0 1980 private:
michael@0 1981 class Unit
michael@0 1982 {
michael@0 1983 public:
michael@0 1984 Unit() : mAtom(nullptr), mType(eUnknown), mLength(0)
michael@0 1985 {
michael@0 1986 MOZ_COUNT_CTOR(StringBuilder::Unit);
michael@0 1987 }
michael@0 1988 ~Unit()
michael@0 1989 {
michael@0 1990 if (mType == eString || mType == eStringWithEncode) {
michael@0 1991 delete mString;
michael@0 1992 }
michael@0 1993 MOZ_COUNT_DTOR(StringBuilder::Unit);
michael@0 1994 }
michael@0 1995
michael@0 1996 enum Type
michael@0 1997 {
michael@0 1998 eUnknown,
michael@0 1999 eAtom,
michael@0 2000 eString,
michael@0 2001 eStringWithEncode,
michael@0 2002 eLiteral,
michael@0 2003 eTextFragment,
michael@0 2004 eTextFragmentWithEncode,
michael@0 2005 };
michael@0 2006
michael@0 2007 union
michael@0 2008 {
michael@0 2009 nsIAtom* mAtom;
michael@0 2010 const char* mLiteral;
michael@0 2011 nsAutoString* mString;
michael@0 2012 const nsTextFragment* mTextFragment;
michael@0 2013 };
michael@0 2014 Type mType;
michael@0 2015 uint32_t mLength;
michael@0 2016 };
michael@0 2017 public:
michael@0 2018 StringBuilder() : mLast(MOZ_THIS_IN_INITIALIZER_LIST()), mLength(0)
michael@0 2019 {
michael@0 2020 MOZ_COUNT_CTOR(StringBuilder);
michael@0 2021 }
michael@0 2022
michael@0 2023 ~StringBuilder()
michael@0 2024 {
michael@0 2025 MOZ_COUNT_DTOR(StringBuilder);
michael@0 2026 }
michael@0 2027
michael@0 2028 void Append(nsIAtom* aAtom)
michael@0 2029 {
michael@0 2030 Unit* u = AddUnit();
michael@0 2031 u->mAtom = aAtom;
michael@0 2032 u->mType = Unit::eAtom;
michael@0 2033 uint32_t len = aAtom->GetLength();
michael@0 2034 u->mLength = len;
michael@0 2035 mLength += len;
michael@0 2036 }
michael@0 2037
michael@0 2038 template<int N>
michael@0 2039 void Append(const char (&aLiteral)[N])
michael@0 2040 {
michael@0 2041 Unit* u = AddUnit();
michael@0 2042 u->mLiteral = aLiteral;
michael@0 2043 u->mType = Unit::eLiteral;
michael@0 2044 uint32_t len = N - 1;
michael@0 2045 u->mLength = len;
michael@0 2046 mLength += len;
michael@0 2047 }
michael@0 2048
michael@0 2049 template<int N>
michael@0 2050 void Append(char (&aLiteral)[N])
michael@0 2051 {
michael@0 2052 Unit* u = AddUnit();
michael@0 2053 u->mLiteral = aLiteral;
michael@0 2054 u->mType = Unit::eLiteral;
michael@0 2055 uint32_t len = N - 1;
michael@0 2056 u->mLength = len;
michael@0 2057 mLength += len;
michael@0 2058 }
michael@0 2059
michael@0 2060 void Append(const nsAString& aString)
michael@0 2061 {
michael@0 2062 Unit* u = AddUnit();
michael@0 2063 u->mString = new nsAutoString(aString);
michael@0 2064 u->mType = Unit::eString;
michael@0 2065 uint32_t len = aString.Length();
michael@0 2066 u->mLength = len;
michael@0 2067 mLength += len;
michael@0 2068 }
michael@0 2069
michael@0 2070 void Append(nsAutoString* aString)
michael@0 2071 {
michael@0 2072 Unit* u = AddUnit();
michael@0 2073 u->mString = aString;
michael@0 2074 u->mType = Unit::eString;
michael@0 2075 uint32_t len = aString->Length();
michael@0 2076 u->mLength = len;
michael@0 2077 mLength += len;
michael@0 2078 }
michael@0 2079
michael@0 2080 void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen)
michael@0 2081 {
michael@0 2082 Unit* u = AddUnit();
michael@0 2083 u->mString = aString;
michael@0 2084 u->mType = Unit::eStringWithEncode;
michael@0 2085 u->mLength = aLen;
michael@0 2086 mLength += aLen;
michael@0 2087 }
michael@0 2088
michael@0 2089 void Append(const nsTextFragment* aTextFragment)
michael@0 2090 {
michael@0 2091 Unit* u = AddUnit();
michael@0 2092 u->mTextFragment = aTextFragment;
michael@0 2093 u->mType = Unit::eTextFragment;
michael@0 2094 uint32_t len = aTextFragment->GetLength();
michael@0 2095 u->mLength = len;
michael@0 2096 mLength += len;
michael@0 2097 }
michael@0 2098
michael@0 2099 void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen)
michael@0 2100 {
michael@0 2101 Unit* u = AddUnit();
michael@0 2102 u->mTextFragment = aTextFragment;
michael@0 2103 u->mType = Unit::eTextFragmentWithEncode;
michael@0 2104 u->mLength = aLen;
michael@0 2105 mLength += aLen;
michael@0 2106 }
michael@0 2107
michael@0 2108 bool ToString(nsAString& aOut)
michael@0 2109 {
michael@0 2110 if (!aOut.SetCapacity(mLength, fallible_t())) {
michael@0 2111 return false;
michael@0 2112 }
michael@0 2113
michael@0 2114 for (StringBuilder* current = this; current; current = current->mNext) {
michael@0 2115 uint32_t len = current->mUnits.Length();
michael@0 2116 for (uint32_t i = 0; i < len; ++i) {
michael@0 2117 Unit& u = current->mUnits[i];
michael@0 2118 switch (u.mType) {
michael@0 2119 case Unit::eAtom:
michael@0 2120 aOut.Append(nsDependentAtomString(u.mAtom));
michael@0 2121 break;
michael@0 2122 case Unit::eString:
michael@0 2123 aOut.Append(*(u.mString));
michael@0 2124 break;
michael@0 2125 case Unit::eStringWithEncode:
michael@0 2126 EncodeAttrString(*(u.mString), aOut);
michael@0 2127 break;
michael@0 2128 case Unit::eLiteral:
michael@0 2129 aOut.AppendASCII(u.mLiteral, u.mLength);
michael@0 2130 break;
michael@0 2131 case Unit::eTextFragment:
michael@0 2132 u.mTextFragment->AppendTo(aOut);
michael@0 2133 break;
michael@0 2134 case Unit::eTextFragmentWithEncode:
michael@0 2135 EncodeTextFragment(u.mTextFragment, aOut);
michael@0 2136 break;
michael@0 2137 default:
michael@0 2138 MOZ_CRASH("Unknown unit type?");
michael@0 2139 }
michael@0 2140 }
michael@0 2141 }
michael@0 2142 return true;
michael@0 2143 }
michael@0 2144 private:
michael@0 2145 Unit* AddUnit()
michael@0 2146 {
michael@0 2147 if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
michael@0 2148 new StringBuilder(this);
michael@0 2149 }
michael@0 2150 return mLast->mUnits.AppendElement();
michael@0 2151 }
michael@0 2152
michael@0 2153 StringBuilder(StringBuilder* aFirst)
michael@0 2154 : mLast(nullptr), mLength(0)
michael@0 2155 {
michael@0 2156 MOZ_COUNT_CTOR(StringBuilder);
michael@0 2157 aFirst->mLast->mNext = this;
michael@0 2158 aFirst->mLast = this;
michael@0 2159 }
michael@0 2160
michael@0 2161 void EncodeAttrString(const nsAutoString& aValue, nsAString& aOut)
michael@0 2162 {
michael@0 2163 const char16_t* c = aValue.BeginReading();
michael@0 2164 const char16_t* end = aValue.EndReading();
michael@0 2165 while (c < end) {
michael@0 2166 switch (*c) {
michael@0 2167 case '"':
michael@0 2168 aOut.AppendLiteral("&quot;");
michael@0 2169 break;
michael@0 2170 case '&':
michael@0 2171 aOut.AppendLiteral("&amp;");
michael@0 2172 break;
michael@0 2173 case 0x00A0:
michael@0 2174 aOut.AppendLiteral("&nbsp;");
michael@0 2175 break;
michael@0 2176 default:
michael@0 2177 aOut.Append(*c);
michael@0 2178 break;
michael@0 2179 }
michael@0 2180 ++c;
michael@0 2181 }
michael@0 2182 }
michael@0 2183
michael@0 2184 void EncodeTextFragment(const nsTextFragment* aValue, nsAString& aOut)
michael@0 2185 {
michael@0 2186 uint32_t len = aValue->GetLength();
michael@0 2187 if (aValue->Is2b()) {
michael@0 2188 const char16_t* data = aValue->Get2b();
michael@0 2189 for (uint32_t i = 0; i < len; ++i) {
michael@0 2190 const char16_t c = data[i];
michael@0 2191 switch (c) {
michael@0 2192 case '<':
michael@0 2193 aOut.AppendLiteral("&lt;");
michael@0 2194 break;
michael@0 2195 case '>':
michael@0 2196 aOut.AppendLiteral("&gt;");
michael@0 2197 break;
michael@0 2198 case '&':
michael@0 2199 aOut.AppendLiteral("&amp;");
michael@0 2200 break;
michael@0 2201 case 0x00A0:
michael@0 2202 aOut.AppendLiteral("&nbsp;");
michael@0 2203 break;
michael@0 2204 default:
michael@0 2205 aOut.Append(c);
michael@0 2206 break;
michael@0 2207 }
michael@0 2208 }
michael@0 2209 } else {
michael@0 2210 const char* data = aValue->Get1b();
michael@0 2211 for (uint32_t i = 0; i < len; ++i) {
michael@0 2212 const unsigned char c = data[i];
michael@0 2213 switch (c) {
michael@0 2214 case '<':
michael@0 2215 aOut.AppendLiteral("&lt;");
michael@0 2216 break;
michael@0 2217 case '>':
michael@0 2218 aOut.AppendLiteral("&gt;");
michael@0 2219 break;
michael@0 2220 case '&':
michael@0 2221 aOut.AppendLiteral("&amp;");
michael@0 2222 break;
michael@0 2223 case 0x00A0:
michael@0 2224 aOut.AppendLiteral("&nbsp;");
michael@0 2225 break;
michael@0 2226 default:
michael@0 2227 aOut.Append(c);
michael@0 2228 break;
michael@0 2229 }
michael@0 2230 }
michael@0 2231 }
michael@0 2232 }
michael@0 2233
michael@0 2234 nsAutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
michael@0 2235 nsAutoPtr<StringBuilder> mNext;
michael@0 2236 StringBuilder* mLast;
michael@0 2237 // mLength is used only in the first StringBuilder object in the linked list.
michael@0 2238 uint32_t mLength;
michael@0 2239 };
michael@0 2240
michael@0 2241 } // anonymous namespace
michael@0 2242
michael@0 2243 static void
michael@0 2244 AppendEncodedCharacters(const nsTextFragment* aText, StringBuilder& aBuilder)
michael@0 2245 {
michael@0 2246 uint32_t extraSpaceNeeded = 0;
michael@0 2247 uint32_t len = aText->GetLength();
michael@0 2248 if (aText->Is2b()) {
michael@0 2249 const char16_t* data = aText->Get2b();
michael@0 2250 for (uint32_t i = 0; i < len; ++i) {
michael@0 2251 const char16_t c = data[i];
michael@0 2252 switch (c) {
michael@0 2253 case '<':
michael@0 2254 extraSpaceNeeded += ArrayLength("&lt;") - 2;
michael@0 2255 break;
michael@0 2256 case '>':
michael@0 2257 extraSpaceNeeded += ArrayLength("&gt;") - 2;
michael@0 2258 break;
michael@0 2259 case '&':
michael@0 2260 extraSpaceNeeded += ArrayLength("&amp;") - 2;
michael@0 2261 break;
michael@0 2262 case 0x00A0:
michael@0 2263 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
michael@0 2264 break;
michael@0 2265 default:
michael@0 2266 break;
michael@0 2267 }
michael@0 2268 }
michael@0 2269 } else {
michael@0 2270 const char* data = aText->Get1b();
michael@0 2271 for (uint32_t i = 0; i < len; ++i) {
michael@0 2272 const unsigned char c = data[i];
michael@0 2273 switch (c) {
michael@0 2274 case '<':
michael@0 2275 extraSpaceNeeded += ArrayLength("&lt;") - 2;
michael@0 2276 break;
michael@0 2277 case '>':
michael@0 2278 extraSpaceNeeded += ArrayLength("&gt;") - 2;
michael@0 2279 break;
michael@0 2280 case '&':
michael@0 2281 extraSpaceNeeded += ArrayLength("&amp;") - 2;
michael@0 2282 break;
michael@0 2283 case 0x00A0:
michael@0 2284 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
michael@0 2285 break;
michael@0 2286 default:
michael@0 2287 break;
michael@0 2288 }
michael@0 2289 }
michael@0 2290 }
michael@0 2291
michael@0 2292 if (extraSpaceNeeded) {
michael@0 2293 aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
michael@0 2294 } else {
michael@0 2295 aBuilder.Append(aText);
michael@0 2296 }
michael@0 2297 }
michael@0 2298
michael@0 2299 static void
michael@0 2300 AppendEncodedAttributeValue(nsAutoString* aValue, StringBuilder& aBuilder)
michael@0 2301 {
michael@0 2302 const char16_t* c = aValue->BeginReading();
michael@0 2303 const char16_t* end = aValue->EndReading();
michael@0 2304
michael@0 2305 uint32_t extraSpaceNeeded = 0;
michael@0 2306 while (c < end) {
michael@0 2307 switch (*c) {
michael@0 2308 case '"':
michael@0 2309 extraSpaceNeeded += ArrayLength("&quot;") - 2;
michael@0 2310 break;
michael@0 2311 case '&':
michael@0 2312 extraSpaceNeeded += ArrayLength("&amp;") - 2;
michael@0 2313 break;
michael@0 2314 case 0x00A0:
michael@0 2315 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
michael@0 2316 break;
michael@0 2317 default:
michael@0 2318 break;
michael@0 2319 }
michael@0 2320 ++c;
michael@0 2321 }
michael@0 2322
michael@0 2323 if (extraSpaceNeeded) {
michael@0 2324 aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
michael@0 2325 } else {
michael@0 2326 aBuilder.Append(aValue);
michael@0 2327 }
michael@0 2328 }
michael@0 2329
michael@0 2330 static void
michael@0 2331 StartElement(Element* aContent, StringBuilder& aBuilder)
michael@0 2332 {
michael@0 2333 nsIAtom* localName = aContent->Tag();
michael@0 2334 int32_t tagNS = aContent->GetNameSpaceID();
michael@0 2335
michael@0 2336 aBuilder.Append("<");
michael@0 2337 if (aContent->IsHTML() || aContent->IsSVG() || aContent->IsMathML()) {
michael@0 2338 aBuilder.Append(localName);
michael@0 2339 } else {
michael@0 2340 aBuilder.Append(aContent->NodeName());
michael@0 2341 }
michael@0 2342
michael@0 2343 int32_t count = aContent->GetAttrCount();
michael@0 2344 for (int32_t i = count; i > 0;) {
michael@0 2345 --i;
michael@0 2346 const nsAttrName* name = aContent->GetAttrNameAt(i);
michael@0 2347 int32_t attNs = name->NamespaceID();
michael@0 2348 nsIAtom* attName = name->LocalName();
michael@0 2349
michael@0 2350 // Filter out any attribute starting with [-|_]moz
michael@0 2351 nsDependentAtomString attrNameStr(attName);
michael@0 2352 if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
michael@0 2353 StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
michael@0 2354 continue;
michael@0 2355 }
michael@0 2356
michael@0 2357 nsAutoString* attValue = new nsAutoString();
michael@0 2358 aContent->GetAttr(attNs, attName, *attValue);
michael@0 2359
michael@0 2360 // Filter out special case of <br type="_moz*"> used by the editor.
michael@0 2361 // Bug 16988. Yuck.
michael@0 2362 if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
michael@0 2363 attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
michael@0 2364 StringBeginsWith(*attValue, NS_LITERAL_STRING("_moz"))) {
michael@0 2365 delete attValue;
michael@0 2366 continue;
michael@0 2367 }
michael@0 2368
michael@0 2369 aBuilder.Append(" ");
michael@0 2370
michael@0 2371 if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
michael@0 2372 (attNs == kNameSpaceID_XMLNS &&
michael@0 2373 attName == nsGkAtoms::xmlns)) {
michael@0 2374 // Nothing else required
michael@0 2375 } else if (attNs == kNameSpaceID_XML) {
michael@0 2376 aBuilder.Append("xml:");
michael@0 2377 } else if (attNs == kNameSpaceID_XMLNS) {
michael@0 2378 aBuilder.Append("xmlns:");
michael@0 2379 } else if (attNs == kNameSpaceID_XLink) {
michael@0 2380 aBuilder.Append("xlink:");
michael@0 2381 } else {
michael@0 2382 nsIAtom* prefix = name->GetPrefix();
michael@0 2383 if (prefix) {
michael@0 2384 aBuilder.Append(prefix);
michael@0 2385 aBuilder.Append(":");
michael@0 2386 }
michael@0 2387 }
michael@0 2388
michael@0 2389 aBuilder.Append(attName);
michael@0 2390 aBuilder.Append("=\"");
michael@0 2391 AppendEncodedAttributeValue(attValue, aBuilder);
michael@0 2392 aBuilder.Append("\"");
michael@0 2393 }
michael@0 2394
michael@0 2395 aBuilder.Append(">");
michael@0 2396
michael@0 2397 /*
michael@0 2398 // Per HTML spec we should append one \n if the first child of
michael@0 2399 // pre/textarea/listing is a textnode and starts with a \n.
michael@0 2400 // But because browsers haven't traditionally had that behavior,
michael@0 2401 // we're not changing our behavior either - yet.
michael@0 2402 if (aContent->IsHTML()) {
michael@0 2403 if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
michael@0 2404 localName == nsGkAtoms::listing) {
michael@0 2405 nsIContent* fc = aContent->GetFirstChild();
michael@0 2406 if (fc &&
michael@0 2407 (fc->NodeType() == nsIDOMNode::TEXT_NODE ||
michael@0 2408 fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
michael@0 2409 const nsTextFragment* text = fc->GetText();
michael@0 2410 if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
michael@0 2411 aBuilder.Append("\n");
michael@0 2412 }
michael@0 2413 }
michael@0 2414 }
michael@0 2415 }*/
michael@0 2416 }
michael@0 2417
michael@0 2418 static inline bool
michael@0 2419 ShouldEscape(nsIContent* aParent)
michael@0 2420 {
michael@0 2421 if (!aParent || !aParent->IsHTML()) {
michael@0 2422 return true;
michael@0 2423 }
michael@0 2424
michael@0 2425 static const nsIAtom* nonEscapingElements[] = {
michael@0 2426 nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
michael@0 2427 nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
michael@0 2428 nsGkAtoms::plaintext,
michael@0 2429 // Per the current spec noscript should be escaped in case
michael@0 2430 // scripts are disabled or if document doesn't have
michael@0 2431 // browsing context. However the latter seems to be a spec bug
michael@0 2432 // and Gecko hasn't traditionally done the former.
michael@0 2433 nsGkAtoms::noscript
michael@0 2434 };
michael@0 2435 static mozilla::BloomFilter<12, nsIAtom> sFilter;
michael@0 2436 static bool sInitialized = false;
michael@0 2437 if (!sInitialized) {
michael@0 2438 sInitialized = true;
michael@0 2439 for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
michael@0 2440 sFilter.add(nonEscapingElements[i]);
michael@0 2441 }
michael@0 2442 }
michael@0 2443
michael@0 2444 nsIAtom* tag = aParent->Tag();
michael@0 2445 if (sFilter.mightContain(tag)) {
michael@0 2446 for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
michael@0 2447 if (tag == nonEscapingElements[i]) {
michael@0 2448 return false;
michael@0 2449 }
michael@0 2450 }
michael@0 2451 }
michael@0 2452 return true;
michael@0 2453 }
michael@0 2454
michael@0 2455 static inline bool
michael@0 2456 IsVoidTag(Element* aElement)
michael@0 2457 {
michael@0 2458 if (!aElement->IsHTML()) {
michael@0 2459 return false;
michael@0 2460 }
michael@0 2461
michael@0 2462 static const nsIAtom* voidElements[] = {
michael@0 2463 nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
michael@0 2464 nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
michael@0 2465 nsGkAtoms::command, nsGkAtoms::embed, nsGkAtoms::frame,
michael@0 2466 nsGkAtoms::hr, nsGkAtoms::img, nsGkAtoms::input,
michael@0 2467 nsGkAtoms::keygen, nsGkAtoms::link, nsGkAtoms::meta,
michael@0 2468 nsGkAtoms::param, nsGkAtoms::source, nsGkAtoms::track,
michael@0 2469 nsGkAtoms::wbr
michael@0 2470 };
michael@0 2471
michael@0 2472 static mozilla::BloomFilter<12, nsIAtom> sFilter;
michael@0 2473 static bool sInitialized = false;
michael@0 2474 if (!sInitialized) {
michael@0 2475 sInitialized = true;
michael@0 2476 for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
michael@0 2477 sFilter.add(voidElements[i]);
michael@0 2478 }
michael@0 2479 }
michael@0 2480
michael@0 2481 nsIAtom* tag = aElement->Tag();
michael@0 2482 if (sFilter.mightContain(tag)) {
michael@0 2483 for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
michael@0 2484 if (tag == voidElements[i]) {
michael@0 2485 return true;
michael@0 2486 }
michael@0 2487 }
michael@0 2488 }
michael@0 2489 return false;
michael@0 2490 }
michael@0 2491
michael@0 2492 static bool
michael@0 2493 Serialize(FragmentOrElement* aRoot, bool aDescendentsOnly, nsAString& aOut)
michael@0 2494 {
michael@0 2495 nsINode* current = aDescendentsOnly ?
michael@0 2496 nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
michael@0 2497
michael@0 2498 if (!current) {
michael@0 2499 return true;
michael@0 2500 }
michael@0 2501
michael@0 2502 StringBuilder builder;
michael@0 2503 nsIContent* next;
michael@0 2504 while (true) {
michael@0 2505 bool isVoid = false;
michael@0 2506 switch (current->NodeType()) {
michael@0 2507 case nsIDOMNode::ELEMENT_NODE: {
michael@0 2508 Element* elem = current->AsElement();
michael@0 2509 StartElement(elem, builder);
michael@0 2510 isVoid = IsVoidTag(elem);
michael@0 2511 if (!isVoid &&
michael@0 2512 (next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
michael@0 2513 current = next;
michael@0 2514 continue;
michael@0 2515 }
michael@0 2516 break;
michael@0 2517 }
michael@0 2518
michael@0 2519 case nsIDOMNode::TEXT_NODE:
michael@0 2520 case nsIDOMNode::CDATA_SECTION_NODE: {
michael@0 2521 const nsTextFragment* text = static_cast<nsIContent*>(current)->GetText();
michael@0 2522 nsIContent* parent = current->GetParent();
michael@0 2523 if (ShouldEscape(parent)) {
michael@0 2524 AppendEncodedCharacters(text, builder);
michael@0 2525 } else {
michael@0 2526 builder.Append(text);
michael@0 2527 }
michael@0 2528 break;
michael@0 2529 }
michael@0 2530
michael@0 2531 case nsIDOMNode::COMMENT_NODE: {
michael@0 2532 builder.Append("<!--");
michael@0 2533 builder.Append(static_cast<nsIContent*>(current)->GetText());
michael@0 2534 builder.Append("-->");
michael@0 2535 break;
michael@0 2536 }
michael@0 2537
michael@0 2538 case nsIDOMNode::DOCUMENT_TYPE_NODE: {
michael@0 2539 builder.Append("<!DOCTYPE ");
michael@0 2540 builder.Append(current->NodeName());
michael@0 2541 builder.Append(">");
michael@0 2542 break;
michael@0 2543 }
michael@0 2544
michael@0 2545 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: {
michael@0 2546 builder.Append("<?");
michael@0 2547 builder.Append(current->NodeName());
michael@0 2548 builder.Append(" ");
michael@0 2549 builder.Append(static_cast<nsIContent*>(current)->GetText());
michael@0 2550 builder.Append(">");
michael@0 2551 break;
michael@0 2552 }
michael@0 2553 }
michael@0 2554
michael@0 2555 while (true) {
michael@0 2556 if (!isVoid && current->NodeType() == nsIDOMNode::ELEMENT_NODE) {
michael@0 2557 builder.Append("</");
michael@0 2558 nsIContent* elem = static_cast<nsIContent*>(current);
michael@0 2559 if (elem->IsHTML() || elem->IsSVG() || elem->IsMathML()) {
michael@0 2560 builder.Append(elem->Tag());
michael@0 2561 } else {
michael@0 2562 builder.Append(current->NodeName());
michael@0 2563 }
michael@0 2564 builder.Append(">");
michael@0 2565 }
michael@0 2566 isVoid = false;
michael@0 2567
michael@0 2568 if (current == aRoot) {
michael@0 2569 return builder.ToString(aOut);
michael@0 2570 }
michael@0 2571
michael@0 2572 if ((next = current->GetNextSibling())) {
michael@0 2573 current = next;
michael@0 2574 break;
michael@0 2575 }
michael@0 2576
michael@0 2577 current = current->GetParentNode();
michael@0 2578
michael@0 2579 // Template case, if we are in a template's content, then the parent
michael@0 2580 // should be the host template element.
michael@0 2581 if (current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
michael@0 2582 DocumentFragment* frag = static_cast<DocumentFragment*>(current);
michael@0 2583 nsIContent* fragHost = frag->GetHost();
michael@0 2584 if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
michael@0 2585 current = fragHost;
michael@0 2586 }
michael@0 2587 }
michael@0 2588
michael@0 2589 if (aDescendentsOnly && current == aRoot) {
michael@0 2590 return builder.ToString(aOut);
michael@0 2591 }
michael@0 2592 }
michael@0 2593 }
michael@0 2594 }
michael@0 2595
michael@0 2596 void
michael@0 2597 FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
michael@0 2598 {
michael@0 2599 aMarkup.Truncate();
michael@0 2600
michael@0 2601 nsIDocument* doc = OwnerDoc();
michael@0 2602 if (IsInHTMLDocument()) {
michael@0 2603 Serialize(this, !aIncludeSelf, aMarkup);
michael@0 2604 return;
michael@0 2605 }
michael@0 2606
michael@0 2607 nsAutoString contentType;
michael@0 2608 doc->GetContentType(contentType);
michael@0 2609 bool tryToCacheEncoder = !aIncludeSelf;
michael@0 2610
michael@0 2611 nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
michael@0 2612 if (!docEncoder) {
michael@0 2613 docEncoder =
michael@0 2614 do_CreateInstance(PromiseFlatCString(
michael@0 2615 nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
michael@0 2616 NS_ConvertUTF16toUTF8(contentType)
michael@0 2617 ).get());
michael@0 2618 }
michael@0 2619 if (!docEncoder) {
michael@0 2620 // This could be some type for which we create a synthetic document. Try
michael@0 2621 // again as XML
michael@0 2622 contentType.AssignLiteral("application/xml");
michael@0 2623 docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
michael@0 2624 // Don't try to cache the encoder since it would point to a different
michael@0 2625 // contentType once it has been reinitialized.
michael@0 2626 tryToCacheEncoder = false;
michael@0 2627 }
michael@0 2628
michael@0 2629 NS_ENSURE_TRUE_VOID(docEncoder);
michael@0 2630
michael@0 2631 uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
michael@0 2632 // Output DOM-standard newlines
michael@0 2633 nsIDocumentEncoder::OutputLFLineBreak |
michael@0 2634 // Don't do linebreaking that's not present in
michael@0 2635 // the source
michael@0 2636 nsIDocumentEncoder::OutputRaw |
michael@0 2637 // Only check for mozdirty when necessary (bug 599983)
michael@0 2638 nsIDocumentEncoder::OutputIgnoreMozDirty;
michael@0 2639
michael@0 2640 if (IsEditable()) {
michael@0 2641 nsCOMPtr<Element> elem = do_QueryInterface(this);
michael@0 2642 nsIEditor* editor = elem ? elem->GetEditorInternal() : nullptr;
michael@0 2643 if (editor && editor->OutputsMozDirty()) {
michael@0 2644 flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
michael@0 2645 }
michael@0 2646 }
michael@0 2647
michael@0 2648 DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
michael@0 2649 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 2650
michael@0 2651 if (aIncludeSelf) {
michael@0 2652 docEncoder->SetNativeNode(this);
michael@0 2653 } else {
michael@0 2654 docEncoder->SetNativeContainerNode(this);
michael@0 2655 }
michael@0 2656 rv = docEncoder->EncodeToString(aMarkup);
michael@0 2657 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 2658 if (tryToCacheEncoder) {
michael@0 2659 doc->SetCachedEncoder(docEncoder.forget());
michael@0 2660 }
michael@0 2661 }
michael@0 2662
michael@0 2663 static bool
michael@0 2664 ContainsMarkup(const nsAString& aStr)
michael@0 2665 {
michael@0 2666 // Note: we can't use FindCharInSet because null is one of the characters we
michael@0 2667 // want to search for.
michael@0 2668 const char16_t* start = aStr.BeginReading();
michael@0 2669 const char16_t* end = aStr.EndReading();
michael@0 2670
michael@0 2671 while (start != end) {
michael@0 2672 char16_t c = *start;
michael@0 2673 if (c == char16_t('<') ||
michael@0 2674 c == char16_t('&') ||
michael@0 2675 c == char16_t('\r') ||
michael@0 2676 c == char16_t('\0')) {
michael@0 2677 return true;
michael@0 2678 }
michael@0 2679 ++start;
michael@0 2680 }
michael@0 2681
michael@0 2682 return false;
michael@0 2683 }
michael@0 2684
michael@0 2685 void
michael@0 2686 FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError)
michael@0 2687 {
michael@0 2688 FragmentOrElement* target = this;
michael@0 2689 // Handle template case.
michael@0 2690 if (nsNodeUtils::IsTemplateElement(target)) {
michael@0 2691 DocumentFragment* frag =
michael@0 2692 static_cast<HTMLTemplateElement*>(target)->Content();
michael@0 2693 MOZ_ASSERT(frag);
michael@0 2694 target = frag;
michael@0 2695 }
michael@0 2696
michael@0 2697 // Fast-path for strings with no markup. Limit this to short strings, to
michael@0 2698 // avoid ContainsMarkup taking too long. The choice for 100 is based on
michael@0 2699 // gut feeling.
michael@0 2700 //
michael@0 2701 // Don't do this for elements with a weird parser insertion mode, for
michael@0 2702 // instance setting innerHTML = "" on a <html> element should add the
michael@0 2703 // optional <head> and <body> elements.
michael@0 2704 if (!target->HasWeirdParserInsertionMode() &&
michael@0 2705 aInnerHTML.Length() < 100 && !ContainsMarkup(aInnerHTML)) {
michael@0 2706 aError = nsContentUtils::SetNodeTextContent(target, aInnerHTML, false);
michael@0 2707 return;
michael@0 2708 }
michael@0 2709
michael@0 2710 nsIDocument* doc = target->OwnerDoc();
michael@0 2711
michael@0 2712 // Batch possible DOMSubtreeModified events.
michael@0 2713 mozAutoSubtreeModified subtree(doc, nullptr);
michael@0 2714
michael@0 2715 target->FireNodeRemovedForChildren();
michael@0 2716
michael@0 2717 // Needed when innerHTML is used in combination with contenteditable
michael@0 2718 mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
michael@0 2719
michael@0 2720 // Remove childnodes.
michael@0 2721 uint32_t childCount = target->GetChildCount();
michael@0 2722 nsAutoMutationBatch mb(target, true, false);
michael@0 2723 for (uint32_t i = 0; i < childCount; ++i) {
michael@0 2724 target->RemoveChildAt(0, true);
michael@0 2725 }
michael@0 2726 mb.RemovalDone();
michael@0 2727
michael@0 2728 nsAutoScriptLoaderDisabler sld(doc);
michael@0 2729
michael@0 2730 nsIAtom* contextLocalName = Tag();
michael@0 2731 int32_t contextNameSpaceID = GetNameSpaceID();
michael@0 2732
michael@0 2733 ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
michael@0 2734 if (shadowRoot) {
michael@0 2735 // Fix up the context to be the host of the ShadowRoot.
michael@0 2736 contextLocalName = shadowRoot->GetHost()->Tag();
michael@0 2737 contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
michael@0 2738 }
michael@0 2739
michael@0 2740 if (doc->IsHTML()) {
michael@0 2741 int32_t oldChildCount = target->GetChildCount();
michael@0 2742 aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
michael@0 2743 target,
michael@0 2744 contextLocalName,
michael@0 2745 contextNameSpaceID,
michael@0 2746 doc->GetCompatibilityMode() ==
michael@0 2747 eCompatibility_NavQuirks,
michael@0 2748 true);
michael@0 2749 mb.NodesAdded();
michael@0 2750 // HTML5 parser has notified, but not fired mutation events.
michael@0 2751 nsContentUtils::FireMutationEventsForDirectParsing(doc, target,
michael@0 2752 oldChildCount);
michael@0 2753 } else {
michael@0 2754 nsRefPtr<DocumentFragment> df =
michael@0 2755 nsContentUtils::CreateContextualFragment(target, aInnerHTML, true, aError);
michael@0 2756 if (!aError.Failed()) {
michael@0 2757 // Suppress assertion about node removal mutation events that can't have
michael@0 2758 // listeners anyway, because no one has had the chance to register mutation
michael@0 2759 // listeners on the fragment that comes from the parser.
michael@0 2760 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
michael@0 2761
michael@0 2762 static_cast<nsINode*>(target)->AppendChild(*df, aError);
michael@0 2763 mb.NodesAdded();
michael@0 2764 }
michael@0 2765 }
michael@0 2766 }
michael@0 2767
michael@0 2768 nsINode::nsSlots*
michael@0 2769 FragmentOrElement::CreateSlots()
michael@0 2770 {
michael@0 2771 return new nsDOMSlots();
michael@0 2772 }
michael@0 2773
michael@0 2774 void
michael@0 2775 FragmentOrElement::FireNodeRemovedForChildren()
michael@0 2776 {
michael@0 2777 nsIDocument* doc = OwnerDoc();
michael@0 2778 // Optimize the common case
michael@0 2779 if (!nsContentUtils::
michael@0 2780 HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
michael@0 2781 return;
michael@0 2782 }
michael@0 2783
michael@0 2784 nsCOMPtr<nsIDocument> owningDoc = doc;
michael@0 2785
michael@0 2786 nsCOMPtr<nsINode> child;
michael@0 2787 for (child = GetFirstChild();
michael@0 2788 child && child->GetParentNode() == this;
michael@0 2789 child = child->GetNextSibling()) {
michael@0 2790 nsContentUtils::MaybeFireNodeRemoved(child, this, doc);
michael@0 2791 }
michael@0 2792 }
michael@0 2793
michael@0 2794 size_t
michael@0 2795 FragmentOrElement::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 2796 {
michael@0 2797 size_t n = 0;
michael@0 2798 n += nsIContent::SizeOfExcludingThis(aMallocSizeOf);
michael@0 2799 n += mAttrsAndChildren.SizeOfExcludingThis(aMallocSizeOf);
michael@0 2800
michael@0 2801 nsDOMSlots* slots = GetExistingDOMSlots();
michael@0 2802 if (slots) {
michael@0 2803 n += slots->SizeOfIncludingThis(aMallocSizeOf);
michael@0 2804 }
michael@0 2805
michael@0 2806 return n;
michael@0 2807 }

mercurial