Wed, 31 Dec 2014 06:09:35 +0100
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("""); |
michael@0 | 2169 | break; |
michael@0 | 2170 | case '&': |
michael@0 | 2171 | aOut.AppendLiteral("&"); |
michael@0 | 2172 | break; |
michael@0 | 2173 | case 0x00A0: |
michael@0 | 2174 | aOut.AppendLiteral(" "); |
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("<"); |
michael@0 | 2194 | break; |
michael@0 | 2195 | case '>': |
michael@0 | 2196 | aOut.AppendLiteral(">"); |
michael@0 | 2197 | break; |
michael@0 | 2198 | case '&': |
michael@0 | 2199 | aOut.AppendLiteral("&"); |
michael@0 | 2200 | break; |
michael@0 | 2201 | case 0x00A0: |
michael@0 | 2202 | aOut.AppendLiteral(" "); |
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("<"); |
michael@0 | 2216 | break; |
michael@0 | 2217 | case '>': |
michael@0 | 2218 | aOut.AppendLiteral(">"); |
michael@0 | 2219 | break; |
michael@0 | 2220 | case '&': |
michael@0 | 2221 | aOut.AppendLiteral("&"); |
michael@0 | 2222 | break; |
michael@0 | 2223 | case 0x00A0: |
michael@0 | 2224 | aOut.AppendLiteral(" "); |
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("<") - 2; |
michael@0 | 2255 | break; |
michael@0 | 2256 | case '>': |
michael@0 | 2257 | extraSpaceNeeded += ArrayLength(">") - 2; |
michael@0 | 2258 | break; |
michael@0 | 2259 | case '&': |
michael@0 | 2260 | extraSpaceNeeded += ArrayLength("&") - 2; |
michael@0 | 2261 | break; |
michael@0 | 2262 | case 0x00A0: |
michael@0 | 2263 | extraSpaceNeeded += ArrayLength(" ") - 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("<") - 2; |
michael@0 | 2276 | break; |
michael@0 | 2277 | case '>': |
michael@0 | 2278 | extraSpaceNeeded += ArrayLength(">") - 2; |
michael@0 | 2279 | break; |
michael@0 | 2280 | case '&': |
michael@0 | 2281 | extraSpaceNeeded += ArrayLength("&") - 2; |
michael@0 | 2282 | break; |
michael@0 | 2283 | case 0x00A0: |
michael@0 | 2284 | extraSpaceNeeded += ArrayLength(" ") - 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(""") - 2; |
michael@0 | 2310 | break; |
michael@0 | 2311 | case '&': |
michael@0 | 2312 | extraSpaceNeeded += ArrayLength("&") - 2; |
michael@0 | 2313 | break; |
michael@0 | 2314 | case 0x00A0: |
michael@0 | 2315 | extraSpaceNeeded += ArrayLength(" ") - 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 | } |