Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=79: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Base class for all element classes; this provides an implementation
9 * of DOM Core's nsIDOMElement, implements nsIContent, provides
10 * utility methods for subclasses, and so forth.
11 */
13 #include "mozilla/dom/ElementInlines.h"
15 #include "mozilla/DebugOnly.h"
16 #include "mozilla/dom/Attr.h"
17 #include "nsDOMAttributeMap.h"
18 #include "nsIAtom.h"
19 #include "nsIContentInlines.h"
20 #include "nsINodeInfo.h"
21 #include "nsIDocumentInlines.h"
22 #include "nsIDOMNodeList.h"
23 #include "nsIDOMDocument.h"
24 #include "nsIContentIterator.h"
25 #include "nsFocusManager.h"
26 #include "nsILinkHandler.h"
27 #include "nsIScriptGlobalObject.h"
28 #include "nsIURL.h"
29 #include "nsNetUtil.h"
30 #include "nsIFrame.h"
31 #include "nsIAnonymousContentCreator.h"
32 #include "nsIPresShell.h"
33 #include "nsPresContext.h"
34 #include "nsStyleConsts.h"
35 #include "nsString.h"
36 #include "nsUnicharUtils.h"
37 #include "nsIDOMEvent.h"
38 #include "nsDOMCID.h"
39 #include "nsIServiceManager.h"
40 #include "nsIDOMCSSStyleDeclaration.h"
41 #include "nsDOMCSSAttrDeclaration.h"
42 #include "nsNameSpaceManager.h"
43 #include "nsContentList.h"
44 #include "nsDOMTokenList.h"
45 #include "nsXBLPrototypeBinding.h"
46 #include "nsError.h"
47 #include "nsDOMString.h"
48 #include "nsIScriptSecurityManager.h"
49 #include "nsIDOMMutationEvent.h"
50 #include "mozilla/AsyncEventDispatcher.h"
51 #include "mozilla/ContentEvents.h"
52 #include "mozilla/EventDispatcher.h"
53 #include "mozilla/EventListenerManager.h"
54 #include "mozilla/EventStateManager.h"
55 #include "mozilla/EventStates.h"
56 #include "mozilla/InternalMutationEvent.h"
57 #include "mozilla/MouseEvents.h"
58 #include "mozilla/TextEvents.h"
59 #include "nsNodeUtils.h"
60 #include "mozilla/dom/DirectionalityUtils.h"
61 #include "nsDocument.h"
62 #include "nsAttrValueOrString.h"
63 #include "nsAttrValueInlines.h"
64 #ifdef MOZ_XUL
65 #include "nsXULElement.h"
66 #endif /* MOZ_XUL */
67 #include "nsFrameManager.h"
68 #include "nsFrameSelection.h"
69 #ifdef DEBUG
70 #include "nsRange.h"
71 #endif
73 #include "nsBindingManager.h"
74 #include "nsXBLBinding.h"
75 #include "nsPIDOMWindow.h"
76 #include "nsPIBoxObject.h"
77 #include "mozilla/dom/DOMRect.h"
78 #include "nsSVGUtils.h"
79 #include "nsLayoutUtils.h"
80 #include "nsGkAtoms.h"
81 #include "nsContentUtils.h"
82 #include "ChildIterator.h"
84 #include "nsIDOMEventListener.h"
85 #include "nsIWebNavigation.h"
86 #include "nsIBaseWindow.h"
87 #include "nsIWidget.h"
89 #include "nsNodeInfoManager.h"
90 #include "nsICategoryManager.h"
91 #include "nsIDOMDocumentType.h"
92 #include "nsIDOMUserDataHandler.h"
93 #include "nsGenericHTMLElement.h"
94 #include "nsIEditor.h"
95 #include "nsIEditorIMESupport.h"
96 #include "nsContentCreatorFunctions.h"
97 #include "nsIControllers.h"
98 #include "nsView.h"
99 #include "nsViewManager.h"
100 #include "nsIScrollableFrame.h"
101 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
102 #include "nsCSSRuleProcessor.h"
103 #include "nsRuleProcessorData.h"
104 #include "nsTextNode.h"
106 #ifdef MOZ_XUL
107 #include "nsIXULDocument.h"
108 #endif /* MOZ_XUL */
110 #include "nsCycleCollectionParticipant.h"
111 #include "nsCCUncollectableMarker.h"
113 #include "mozAutoDocUpdate.h"
115 #include "nsCSSParser.h"
116 #include "prprf.h"
117 #include "nsDOMMutationObserver.h"
118 #include "nsSVGFeatures.h"
119 #include "nsWrapperCacheInlines.h"
120 #include "xpcpublic.h"
121 #include "nsIScriptError.h"
122 #include "mozilla/Telemetry.h"
124 #include "mozilla/CORSMode.h"
125 #include "mozilla/dom/ShadowRoot.h"
127 #include "nsStyledElement.h"
128 #include "nsXBLService.h"
129 #include "nsITextControlElement.h"
130 #include "nsITextControlFrame.h"
131 #include "nsISupportsImpl.h"
132 #include "mozilla/dom/DocumentFragment.h"
133 #include "mozilla/IntegerPrintfMacros.h"
135 using namespace mozilla;
136 using namespace mozilla::dom;
138 NS_IMETHODIMP
139 Element::QueryInterface(REFNSIID aIID, void** aInstancePtr)
140 {
141 NS_ASSERTION(aInstancePtr,
142 "QueryInterface requires a non-NULL destination!");
143 nsresult rv = FragmentOrElement::QueryInterface(aIID, aInstancePtr);
144 if (NS_SUCCEEDED(rv)) {
145 return NS_OK;
146 }
148 // Give the binding manager a chance to get an interface for this element.
149 return OwnerDoc()->BindingManager()->GetBindingImplementation(this, aIID,
150 aInstancePtr);
151 }
153 EventStates
154 Element::IntrinsicState() const
155 {
156 return IsEditable() ? NS_EVENT_STATE_MOZ_READWRITE :
157 NS_EVENT_STATE_MOZ_READONLY;
158 }
160 void
161 Element::NotifyStateChange(EventStates aStates)
162 {
163 nsIDocument* doc = GetCurrentDoc();
164 if (doc) {
165 nsAutoScriptBlocker scriptBlocker;
166 doc->ContentStateChanged(this, aStates);
167 }
168 }
170 void
171 Element::UpdateLinkState(EventStates aState)
172 {
173 NS_ABORT_IF_FALSE(!aState.HasAtLeastOneOfStates(~(NS_EVENT_STATE_VISITED |
174 NS_EVENT_STATE_UNVISITED)),
175 "Unexpected link state bits");
176 mState =
177 (mState & ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) |
178 aState;
179 }
181 void
182 Element::UpdateState(bool aNotify)
183 {
184 EventStates oldState = mState;
185 mState = IntrinsicState() | (oldState & ESM_MANAGED_STATES);
186 if (aNotify) {
187 EventStates changedStates = oldState ^ mState;
188 if (!changedStates.IsEmpty()) {
189 nsIDocument* doc = GetCurrentDoc();
190 if (doc) {
191 nsAutoScriptBlocker scriptBlocker;
192 doc->ContentStateChanged(this, changedStates);
193 }
194 }
195 }
196 }
198 void
199 nsIContent::UpdateEditableState(bool aNotify)
200 {
201 // Guaranteed to be non-element content
202 NS_ASSERTION(!IsElement(), "What happened here?");
203 nsIContent *parent = GetParent();
205 // Skip over unknown native anonymous content to avoid setting a flag we
206 // can't clear later
207 bool isUnknownNativeAnon = false;
208 if (IsInNativeAnonymousSubtree()) {
209 isUnknownNativeAnon = true;
210 nsCOMPtr<nsIContent> root = this;
211 while (root && !root->IsRootOfNativeAnonymousSubtree()) {
212 root = root->GetParent();
213 }
214 // root should always be true here, but isn't -- bug 999416
215 if (root) {
216 nsIFrame* rootFrame = root->GetPrimaryFrame();
217 if (rootFrame) {
218 nsIFrame* parentFrame = rootFrame->GetParent();
219 nsITextControlFrame* textCtrl = do_QueryFrame(parentFrame);
220 isUnknownNativeAnon = !textCtrl;
221 }
222 }
223 }
225 SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE) &&
226 !isUnknownNativeAnon);
227 }
229 void
230 Element::UpdateEditableState(bool aNotify)
231 {
232 nsIContent *parent = GetParent();
234 SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE));
235 if (aNotify) {
236 UpdateState(aNotify);
237 } else {
238 // Avoid calling UpdateState in this very common case, because
239 // this gets called for pretty much every single element on
240 // insertion into the document and UpdateState can be slow for
241 // some kinds of elements even when not notifying.
242 if (IsEditable()) {
243 RemoveStatesSilently(NS_EVENT_STATE_MOZ_READONLY);
244 AddStatesSilently(NS_EVENT_STATE_MOZ_READWRITE);
245 } else {
246 RemoveStatesSilently(NS_EVENT_STATE_MOZ_READWRITE);
247 AddStatesSilently(NS_EVENT_STATE_MOZ_READONLY);
248 }
249 }
250 }
252 EventStates
253 Element::StyleStateFromLocks() const
254 {
255 EventStates locks = LockedStyleStates();
256 EventStates state = mState | locks;
258 if (locks.HasState(NS_EVENT_STATE_VISITED)) {
259 return state & ~NS_EVENT_STATE_UNVISITED;
260 }
261 if (locks.HasState(NS_EVENT_STATE_UNVISITED)) {
262 return state & ~NS_EVENT_STATE_VISITED;
263 }
264 return state;
265 }
267 EventStates
268 Element::LockedStyleStates() const
269 {
270 EventStates* locks =
271 static_cast<EventStates*>(GetProperty(nsGkAtoms::lockedStyleStates));
272 if (locks) {
273 return *locks;
274 }
275 return EventStates();
276 }
278 void
279 Element::NotifyStyleStateChange(EventStates aStates)
280 {
281 nsIDocument* doc = GetCurrentDoc();
282 if (doc) {
283 nsIPresShell *presShell = doc->GetShell();
284 if (presShell) {
285 nsAutoScriptBlocker scriptBlocker;
286 presShell->ContentStateChanged(doc, this, aStates);
287 }
288 }
289 }
291 void
292 Element::LockStyleStates(EventStates aStates)
293 {
294 EventStates* locks = new EventStates(LockedStyleStates());
296 *locks |= aStates;
298 if (aStates.HasState(NS_EVENT_STATE_VISITED)) {
299 *locks &= ~NS_EVENT_STATE_UNVISITED;
300 }
301 if (aStates.HasState(NS_EVENT_STATE_UNVISITED)) {
302 *locks &= ~NS_EVENT_STATE_VISITED;
303 }
305 SetProperty(nsGkAtoms::lockedStyleStates, locks,
306 nsINode::DeleteProperty<EventStates>);
307 SetHasLockedStyleStates();
309 NotifyStyleStateChange(aStates);
310 }
312 void
313 Element::UnlockStyleStates(EventStates aStates)
314 {
315 EventStates* locks = new EventStates(LockedStyleStates());
317 *locks &= ~aStates;
319 if (locks->IsEmpty()) {
320 DeleteProperty(nsGkAtoms::lockedStyleStates);
321 ClearHasLockedStyleStates();
322 delete locks;
323 }
324 else {
325 SetProperty(nsGkAtoms::lockedStyleStates, locks,
326 nsINode::DeleteProperty<EventStates>);
327 }
329 NotifyStyleStateChange(aStates);
330 }
332 void
333 Element::ClearStyleStateLocks()
334 {
335 EventStates locks = LockedStyleStates();
337 DeleteProperty(nsGkAtoms::lockedStyleStates);
338 ClearHasLockedStyleStates();
340 NotifyStyleStateChange(locks);
341 }
343 bool
344 Element::GetBindingURL(nsIDocument *aDocument, css::URLValue **aResult)
345 {
346 // If we have a frame the frame has already loaded the binding. And
347 // otherwise, don't do anything else here unless we're dealing with
348 // XUL or an HTML element that may have a plugin-related overlay
349 // (i.e. object, embed, or applet).
350 bool isXULorPluginElement = (IsXUL() ||
351 IsHTML(nsGkAtoms::object) ||
352 IsHTML(nsGkAtoms::embed) ||
353 IsHTML(nsGkAtoms::applet));
354 nsIPresShell *shell = aDocument->GetShell();
355 if (!shell || GetPrimaryFrame() || !isXULorPluginElement) {
356 *aResult = nullptr;
358 return true;
359 }
361 // Get the computed -moz-binding directly from the style context
362 nsPresContext *pctx = shell->GetPresContext();
363 NS_ENSURE_TRUE(pctx, false);
365 nsRefPtr<nsStyleContext> sc = pctx->StyleSet()->ResolveStyleFor(this,
366 nullptr);
367 NS_ENSURE_TRUE(sc, false);
369 *aResult = sc->StyleDisplay()->mBinding;
371 return true;
372 }
374 JSObject*
375 Element::WrapObject(JSContext *aCx)
376 {
377 JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx));
378 if (!obj) {
379 return nullptr;
380 }
382 // Custom element prototype swizzling.
383 CustomElementData* data = GetCustomElementData();
384 if (obj && data) {
385 // If this is a registered custom element then fix the prototype.
386 JSAutoCompartment ac(aCx, obj);
387 nsDocument* document = static_cast<nsDocument*>(OwnerDoc());
388 JS::Rooted<JSObject*> prototype(aCx);
389 document->GetCustomPrototype(NodeInfo()->NamespaceID(), data->mType, &prototype);
390 if (prototype) {
391 if (!JS_WrapObject(aCx, &prototype) || !JS_SetPrototype(aCx, obj, prototype)) {
392 dom::Throw(aCx, NS_ERROR_FAILURE);
393 return nullptr;
394 }
395 }
396 }
398 nsIDocument* doc;
399 if (HasFlag(NODE_FORCE_XBL_BINDINGS)) {
400 doc = OwnerDoc();
401 }
402 else {
403 doc = GetCurrentDoc();
404 }
406 if (!doc) {
407 // There's no baseclass that cares about this call so we just
408 // return here.
409 return obj;
410 }
412 // We must ensure that the XBL Binding is installed before we hand
413 // back this object.
415 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) && GetXBLBinding()) {
416 // There's already a binding for this element so nothing left to
417 // be done here.
419 // In theory we could call ExecuteAttachedHandler here when it's safe to
420 // run script if we also removed the binding from the PAQ queue, but that
421 // seems like a scary change that would mosly just add more
422 // inconsistencies.
423 return obj;
424 }
426 // Make sure the style context goes away _before_ we load the binding
427 // since that can destroy the relevant presshell.
428 mozilla::css::URLValue *bindingURL;
429 bool ok = GetBindingURL(doc, &bindingURL);
430 if (!ok) {
431 dom::Throw(aCx, NS_ERROR_FAILURE);
432 return nullptr;
433 }
435 if (!bindingURL) {
436 // No binding, nothing left to do here.
437 return obj;
438 }
440 nsCOMPtr<nsIURI> uri = bindingURL->GetURI();
441 nsCOMPtr<nsIPrincipal> principal = bindingURL->mOriginPrincipal;
443 // We have a binding that must be installed.
444 bool dummy;
446 nsXBLService* xblService = nsXBLService::GetInstance();
447 if (!xblService) {
448 dom::Throw(aCx, NS_ERROR_NOT_AVAILABLE);
449 return nullptr;
450 }
452 {
453 // Make a scope so that ~nsRefPtr can GC before returning obj.
454 nsRefPtr<nsXBLBinding> binding;
455 xblService->LoadBindings(this, uri, principal, getter_AddRefs(binding), &dummy);
457 if (binding) {
458 if (nsContentUtils::IsSafeToRunScript()) {
459 binding->ExecuteAttachedHandler();
460 }
461 else {
462 nsContentUtils::AddScriptRunner(
463 NS_NewRunnableMethod(binding, &nsXBLBinding::ExecuteAttachedHandler));
464 }
465 }
466 }
468 return obj;
469 }
471 nsDOMTokenList*
472 Element::GetClassList()
473 {
474 Element::nsDOMSlots *slots = DOMSlots();
476 if (!slots->mClassList) {
477 nsIAtom* classAttr = GetClassAttributeName();
478 if (classAttr) {
479 slots->mClassList = new nsDOMTokenList(this, classAttr);
480 }
481 }
483 return slots->mClassList;
484 }
486 void
487 Element::GetClassList(nsISupports** aClassList)
488 {
489 NS_IF_ADDREF(*aClassList = GetClassList());
490 }
492 already_AddRefed<nsIHTMLCollection>
493 Element::GetElementsByTagName(const nsAString& aLocalName)
494 {
495 return NS_GetContentList(this, kNameSpaceID_Unknown, aLocalName);
496 }
498 void
499 Element::GetElementsByTagName(const nsAString& aLocalName,
500 nsIDOMHTMLCollection** aResult)
501 {
502 *aResult = GetElementsByTagName(aLocalName).take();
503 }
505 nsIFrame*
506 Element::GetStyledFrame()
507 {
508 nsIFrame *frame = GetPrimaryFrame(Flush_Layout);
509 return frame ? nsLayoutUtils::GetStyleFrame(frame) : nullptr;
510 }
512 nsIScrollableFrame*
513 Element::GetScrollFrame(nsIFrame **aStyledFrame, bool aFlushLayout)
514 {
515 // it isn't clear what to return for SVG nodes, so just return nothing
516 if (IsSVG()) {
517 if (aStyledFrame) {
518 *aStyledFrame = nullptr;
519 }
520 return nullptr;
521 }
523 // Inline version of GetStyledFrame to use Flush_None if needed.
524 nsIFrame* frame = GetPrimaryFrame(aFlushLayout ? Flush_Layout : Flush_None);
525 if (frame) {
526 frame = nsLayoutUtils::GetStyleFrame(frame);
527 }
529 if (aStyledFrame) {
530 *aStyledFrame = frame;
531 }
532 if (!frame) {
533 return nullptr;
534 }
536 // menu frames implement GetScrollTargetFrame but we don't want
537 // to use it here. Similar for comboboxes.
538 nsIAtom* type = frame->GetType();
539 if (type != nsGkAtoms::menuFrame && type != nsGkAtoms::comboboxControlFrame) {
540 nsIScrollableFrame *scrollFrame = frame->GetScrollTargetFrame();
541 if (scrollFrame)
542 return scrollFrame;
543 }
545 nsIDocument* doc = OwnerDoc();
546 bool quirksMode = doc->GetCompatibilityMode() == eCompatibility_NavQuirks;
547 Element* elementWithRootScrollInfo =
548 quirksMode ? doc->GetBodyElement() : doc->GetRootElement();
549 if (this == elementWithRootScrollInfo) {
550 // In quirks mode, the scroll info for the body element should map to the
551 // root scrollable frame.
552 // In strict mode, the scroll info for the root element should map to the
553 // the root scrollable frame.
554 return frame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
555 }
557 return nullptr;
558 }
560 void
561 Element::ScrollIntoView(bool aTop)
562 {
563 nsIDocument *document = GetCurrentDoc();
564 if (!document) {
565 return;
566 }
568 // Get the presentation shell
569 nsCOMPtr<nsIPresShell> presShell = document->GetShell();
570 if (!presShell) {
571 return;
572 }
574 int16_t vpercent = aTop ? nsIPresShell::SCROLL_TOP :
575 nsIPresShell::SCROLL_BOTTOM;
577 presShell->ScrollContentIntoView(this,
578 nsIPresShell::ScrollAxis(
579 vpercent,
580 nsIPresShell::SCROLL_ALWAYS),
581 nsIPresShell::ScrollAxis(),
582 nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
583 }
585 bool
586 Element::ScrollByNoFlush(int32_t aDx, int32_t aDy)
587 {
588 nsIScrollableFrame* sf = GetScrollFrame(nullptr, false);
589 if (!sf) {
590 return false;
591 }
593 nsWeakFrame weakRef(sf->GetScrolledFrame());
595 CSSIntPoint before = sf->GetScrollPositionCSSPixels();
596 sf->ScrollToCSSPixelsApproximate(CSSIntPoint(before.x + aDx, before.y + aDy));
598 // The frame was destroyed, can't keep on scrolling.
599 if (!weakRef.IsAlive()) {
600 return false;
601 }
603 CSSIntPoint after = sf->GetScrollPositionCSSPixels();
604 return (before != after);
605 }
607 static nsSize GetScrollRectSizeForOverflowVisibleFrame(nsIFrame* aFrame)
608 {
609 if (!aFrame) {
610 return nsSize(0,0);
611 }
613 nsRect paddingRect = aFrame->GetPaddingRectRelativeToSelf();
614 nsOverflowAreas overflowAreas(paddingRect, paddingRect);
615 // Add the scrollable overflow areas of children (if any) to the paddingRect.
616 // It's important to start with the paddingRect, otherwise if there are no
617 // children the overflow rect will be 0,0,0,0 which will force the point 0,0
618 // to be included in the final rect.
619 nsLayoutUtils::UnionChildOverflow(aFrame, overflowAreas);
620 // Make sure that an empty padding-rect's edges are included, by adding
621 // the padding-rect in again with UnionEdges.
622 nsRect overflowRect =
623 overflowAreas.ScrollableOverflow().UnionEdges(paddingRect);
624 return nsLayoutUtils::GetScrolledRect(aFrame,
625 overflowRect, paddingRect.Size(),
626 aFrame->StyleVisibility()->mDirection).Size();
627 }
629 int32_t
630 Element::ScrollHeight()
631 {
632 if (IsSVG())
633 return 0;
635 nsIScrollableFrame* sf = GetScrollFrame();
636 nscoord height;
637 if (sf) {
638 height = sf->GetScrollRange().height + sf->GetScrollPortRect().height;
639 } else {
640 height = GetScrollRectSizeForOverflowVisibleFrame(GetStyledFrame()).height;
641 }
643 return nsPresContext::AppUnitsToIntCSSPixels(height);
644 }
646 int32_t
647 Element::ScrollWidth()
648 {
649 if (IsSVG())
650 return 0;
652 nsIScrollableFrame* sf = GetScrollFrame();
653 nscoord width;
654 if (sf) {
655 width = sf->GetScrollRange().width + sf->GetScrollPortRect().width;
656 } else {
657 width = GetScrollRectSizeForOverflowVisibleFrame(GetStyledFrame()).width;
658 }
660 return nsPresContext::AppUnitsToIntCSSPixels(width);
661 }
663 nsRect
664 Element::GetClientAreaRect()
665 {
666 nsIFrame* styledFrame;
667 nsIScrollableFrame* sf = GetScrollFrame(&styledFrame);
669 if (sf) {
670 return sf->GetScrollPortRect();
671 }
673 if (styledFrame &&
674 (styledFrame->StyleDisplay()->mDisplay != NS_STYLE_DISPLAY_INLINE ||
675 styledFrame->IsFrameOfType(nsIFrame::eReplaced))) {
676 // Special case code to make client area work even when there isn't
677 // a scroll view, see bug 180552, bug 227567.
678 return styledFrame->GetPaddingRect() - styledFrame->GetPositionIgnoringScrolling();
679 }
681 // SVG nodes reach here and just return 0
682 return nsRect(0, 0, 0, 0);
683 }
685 already_AddRefed<DOMRect>
686 Element::GetBoundingClientRect()
687 {
688 nsRefPtr<DOMRect> rect = new DOMRect(this);
690 nsIFrame* frame = GetPrimaryFrame(Flush_Layout);
691 if (!frame) {
692 // display:none, perhaps? Return the empty rect
693 return rect.forget();
694 }
696 nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(frame,
697 nsLayoutUtils::GetContainingBlockForClientRect(frame),
698 nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
699 rect->SetLayoutRect(r);
700 return rect.forget();
701 }
703 already_AddRefed<DOMRectList>
704 Element::GetClientRects()
705 {
706 nsRefPtr<DOMRectList> rectList = new DOMRectList(this);
708 nsIFrame* frame = GetPrimaryFrame(Flush_Layout);
709 if (!frame) {
710 // display:none, perhaps? Return an empty list
711 return rectList.forget();
712 }
714 nsLayoutUtils::RectListBuilder builder(rectList);
715 nsLayoutUtils::GetAllInFlowRects(frame,
716 nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder,
717 nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
718 return rectList.forget();
719 }
722 //----------------------------------------------------------------------
724 void
725 Element::AddToIdTable(nsIAtom* aId)
726 {
727 NS_ASSERTION(HasID(), "Node doesn't have an ID?");
728 if (HasFlag(NODE_IS_IN_SHADOW_TREE)) {
729 ShadowRoot* containingShadow = GetContainingShadow();
730 containingShadow->AddToIdTable(this, aId);
731 } else {
732 nsIDocument* doc = GetCurrentDoc();
733 if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) {
734 doc->AddToIdTable(this, aId);
735 }
736 }
737 }
739 void
740 Element::RemoveFromIdTable()
741 {
742 if (HasID()) {
743 RemoveFromIdTable(DoGetID());
744 }
745 }
747 void
748 Element::RemoveFromIdTable(nsIAtom* aId)
749 {
750 NS_ASSERTION(HasID(), "Node doesn't have an ID?");
751 if (HasFlag(NODE_IS_IN_SHADOW_TREE)) {
752 ShadowRoot* containingShadow = GetContainingShadow();
753 // Check for containingShadow because it may have
754 // been deleted during unlinking.
755 if (containingShadow) {
756 containingShadow->RemoveFromIdTable(this, aId);
757 }
758 } else {
759 nsIDocument* doc = GetCurrentDoc();
760 if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) {
761 // id can be null during mutation events evilness. Also, XUL elements
762 // loose their proto attributes during cc-unlink, so this can happen
763 // during cc-unlink too.
764 if (aId) {
765 doc->RemoveFromIdTable(this, aId);
766 }
767 }
768 }
769 }
771 already_AddRefed<ShadowRoot>
772 Element::CreateShadowRoot(ErrorResult& aError)
773 {
774 nsAutoScriptBlocker scriptBlocker;
776 nsCOMPtr<nsINodeInfo> nodeInfo;
777 nodeInfo = mNodeInfo->NodeInfoManager()->GetNodeInfo(
778 nsGkAtoms::documentFragmentNodeName, nullptr, kNameSpaceID_None,
779 nsIDOMNode::DOCUMENT_FRAGMENT_NODE);
781 nsRefPtr<nsXBLDocumentInfo> docInfo = new nsXBLDocumentInfo(OwnerDoc());
783 nsXBLPrototypeBinding* protoBinding = new nsXBLPrototypeBinding();
784 aError = protoBinding->Init(NS_LITERAL_CSTRING("shadowroot"),
785 docInfo, this, true);
786 if (aError.Failed()) {
787 delete protoBinding;
788 return nullptr;
789 }
791 // Unlike for XBL, false is the default for inheriting style.
792 protoBinding->SetInheritsStyle(false);
794 // Calling SetPrototypeBinding takes ownership of protoBinding.
795 docInfo->SetPrototypeBinding(NS_LITERAL_CSTRING("shadowroot"), protoBinding);
797 nsRefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget(),
798 protoBinding);
800 // Replace the old ShadowRoot with the new one and let the old
801 // ShadowRoot know about the younger ShadowRoot because the old
802 // ShadowRoot is projected into the younger ShadowRoot's shadow
803 // insertion point (if it exists).
804 ShadowRoot* olderShadow = GetShadowRoot();
805 SetShadowRoot(shadowRoot);
806 if (olderShadow) {
807 olderShadow->SetYoungerShadow(shadowRoot);
808 }
810 // xblBinding takes ownership of docInfo.
811 nsRefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding);
812 shadowRoot->SetAssociatedBinding(xblBinding);
813 xblBinding->SetBoundElement(this);
815 SetXBLBinding(xblBinding);
817 // Recreate the frame for the bound content because binding a ShadowRoot
818 // changes how things are rendered.
819 nsIDocument* doc = GetCurrentDoc();
820 if (doc) {
821 nsIPresShell *shell = doc->GetShell();
822 if (shell) {
823 shell->RecreateFramesFor(this);
824 }
825 }
827 return shadowRoot.forget();
828 }
830 void
831 Element::GetAttribute(const nsAString& aName, DOMString& aReturn)
832 {
833 const nsAttrValue* val =
834 mAttrsAndChildren.GetAttr(aName,
835 IsHTML() && IsInHTMLDocument() ?
836 eIgnoreCase : eCaseMatters);
837 if (val) {
838 val->ToString(aReturn);
839 } else {
840 if (IsXUL()) {
841 // XXX should be SetDOMStringToNull(aReturn);
842 // See bug 232598
843 // aReturn is already empty
844 } else {
845 aReturn.SetNull();
846 }
847 }
848 }
850 void
851 Element::SetAttribute(const nsAString& aName,
852 const nsAString& aValue,
853 ErrorResult& aError)
854 {
855 const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName);
857 if (!name) {
858 aError = nsContentUtils::CheckQName(aName, false);
859 if (aError.Failed()) {
860 return;
861 }
863 nsCOMPtr<nsIAtom> nameAtom;
864 if (IsHTML() && IsInHTMLDocument()) {
865 nsAutoString lower;
866 nsresult rv = nsContentUtils::ASCIIToLower(aName, lower);
867 if (NS_SUCCEEDED(rv)) {
868 nameAtom = do_GetAtom(lower);
869 }
870 }
871 else {
872 nameAtom = do_GetAtom(aName);
873 }
874 if (!nameAtom) {
875 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
876 return;
877 }
878 aError = SetAttr(kNameSpaceID_None, nameAtom, aValue, true);
879 return;
880 }
882 aError = SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(),
883 aValue, true);
884 return;
885 }
887 void
888 Element::RemoveAttribute(const nsAString& aName, ErrorResult& aError)
889 {
890 const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName);
892 if (!name) {
893 // If there is no canonical nsAttrName for this attribute name, then the
894 // attribute does not exist and we can't get its namespace ID and
895 // local name below, so we return early.
896 return;
897 }
899 // Hold a strong reference here so that the atom or nodeinfo doesn't go
900 // away during UnsetAttr. If it did UnsetAttr would be left with a
901 // dangling pointer as argument without knowing it.
902 nsAttrName tmp(*name);
904 aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true);
905 }
907 Attr*
908 Element::GetAttributeNode(const nsAString& aName)
909 {
910 OwnerDoc()->WarnOnceAbout(nsIDocument::eGetAttributeNode);
911 return Attributes()->GetNamedItem(aName);
912 }
914 already_AddRefed<Attr>
915 Element::SetAttributeNode(Attr& aNewAttr, ErrorResult& aError)
916 {
917 OwnerDoc()->WarnOnceAbout(nsIDocument::eSetAttributeNode);
919 return Attributes()->SetNamedItem(aNewAttr, aError);
920 }
922 already_AddRefed<Attr>
923 Element::RemoveAttributeNode(Attr& aAttribute,
924 ErrorResult& aError)
925 {
926 OwnerDoc()->WarnOnceAbout(nsIDocument::eRemoveAttributeNode);
927 return Attributes()->RemoveNamedItem(aAttribute.NodeName(), aError);
928 }
930 void
931 Element::GetAttributeNS(const nsAString& aNamespaceURI,
932 const nsAString& aLocalName,
933 nsAString& aReturn)
934 {
935 int32_t nsid =
936 nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
938 if (nsid == kNameSpaceID_Unknown) {
939 // Unknown namespace means no attribute.
940 SetDOMStringToNull(aReturn);
941 return;
942 }
944 nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName);
945 bool hasAttr = GetAttr(nsid, name, aReturn);
946 if (!hasAttr) {
947 SetDOMStringToNull(aReturn);
948 }
949 }
951 void
952 Element::SetAttributeNS(const nsAString& aNamespaceURI,
953 const nsAString& aQualifiedName,
954 const nsAString& aValue,
955 ErrorResult& aError)
956 {
957 nsCOMPtr<nsINodeInfo> ni;
958 aError =
959 nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName,
960 mNodeInfo->NodeInfoManager(),
961 nsIDOMNode::ATTRIBUTE_NODE,
962 getter_AddRefs(ni));
963 if (aError.Failed()) {
964 return;
965 }
967 aError = SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(),
968 aValue, true);
969 }
971 void
972 Element::RemoveAttributeNS(const nsAString& aNamespaceURI,
973 const nsAString& aLocalName,
974 ErrorResult& aError)
975 {
976 nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName);
977 int32_t nsid =
978 nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
980 if (nsid == kNameSpaceID_Unknown) {
981 // If the namespace ID is unknown, it means there can't possibly be an
982 // existing attribute. We would need a known namespace ID to pass into
983 // UnsetAttr, so we return early if we don't have one.
984 return;
985 }
987 aError = UnsetAttr(nsid, name, true);
988 }
990 Attr*
991 Element::GetAttributeNodeNS(const nsAString& aNamespaceURI,
992 const nsAString& aLocalName)
993 {
994 OwnerDoc()->WarnOnceAbout(nsIDocument::eGetAttributeNodeNS);
996 return GetAttributeNodeNSInternal(aNamespaceURI, aLocalName);
997 }
999 Attr*
1000 Element::GetAttributeNodeNSInternal(const nsAString& aNamespaceURI,
1001 const nsAString& aLocalName)
1002 {
1003 return Attributes()->GetNamedItemNS(aNamespaceURI, aLocalName);
1004 }
1006 already_AddRefed<Attr>
1007 Element::SetAttributeNodeNS(Attr& aNewAttr,
1008 ErrorResult& aError)
1009 {
1010 OwnerDoc()->WarnOnceAbout(nsIDocument::eSetAttributeNodeNS);
1011 return Attributes()->SetNamedItemNS(aNewAttr, aError);
1012 }
1014 already_AddRefed<nsIHTMLCollection>
1015 Element::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
1016 const nsAString& aLocalName,
1017 ErrorResult& aError)
1018 {
1019 int32_t nameSpaceId = kNameSpaceID_Wildcard;
1021 if (!aNamespaceURI.EqualsLiteral("*")) {
1022 aError =
1023 nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
1024 nameSpaceId);
1025 if (aError.Failed()) {
1026 return nullptr;
1027 }
1028 }
1030 NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
1032 return NS_GetContentList(this, nameSpaceId, aLocalName);
1033 }
1035 nsresult
1036 Element::GetElementsByTagNameNS(const nsAString& namespaceURI,
1037 const nsAString& localName,
1038 nsIDOMHTMLCollection** aResult)
1039 {
1040 mozilla::ErrorResult rv;
1041 nsCOMPtr<nsIHTMLCollection> list =
1042 GetElementsByTagNameNS(namespaceURI, localName, rv);
1043 if (rv.Failed()) {
1044 return rv.ErrorCode();
1045 }
1046 list.forget(aResult);
1047 return NS_OK;
1048 }
1050 bool
1051 Element::HasAttributeNS(const nsAString& aNamespaceURI,
1052 const nsAString& aLocalName) const
1053 {
1054 int32_t nsid =
1055 nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
1057 if (nsid == kNameSpaceID_Unknown) {
1058 // Unknown namespace means no attr...
1059 return false;
1060 }
1062 nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName);
1063 return HasAttr(nsid, name);
1064 }
1066 already_AddRefed<nsIHTMLCollection>
1067 Element::GetElementsByClassName(const nsAString& aClassNames)
1068 {
1069 return nsContentUtils::GetElementsByClassName(this, aClassNames);
1070 }
1072 nsresult
1073 Element::GetElementsByClassName(const nsAString& aClassNames,
1074 nsIDOMHTMLCollection** aResult)
1075 {
1076 *aResult =
1077 nsContentUtils::GetElementsByClassName(this, aClassNames).take();
1078 return NS_OK;
1079 }
1081 nsresult
1082 Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
1083 nsIContent* aBindingParent,
1084 bool aCompileEventHandlers)
1085 {
1086 NS_PRECONDITION(aParent || aDocument, "Must have document if no parent!");
1087 NS_PRECONDITION((NODE_FROM(aParent, aDocument)->OwnerDoc() == OwnerDoc()),
1088 "Must have the same owner document");
1089 NS_PRECONDITION(!aParent || aDocument == aParent->GetCurrentDoc(),
1090 "aDocument must be current doc of aParent");
1091 NS_PRECONDITION(!GetCurrentDoc(), "Already have a document. Unbind first!");
1092 // Note that as we recurse into the kids, they'll have a non-null parent. So
1093 // only assert if our parent is _changing_ while we have a parent.
1094 NS_PRECONDITION(!GetParent() || aParent == GetParent(),
1095 "Already have a parent. Unbind first!");
1096 NS_PRECONDITION(!GetBindingParent() ||
1097 aBindingParent == GetBindingParent() ||
1098 (!aBindingParent && aParent &&
1099 aParent->GetBindingParent() == GetBindingParent()),
1100 "Already have a binding parent. Unbind first!");
1101 NS_PRECONDITION(!aParent || !aDocument ||
1102 !aParent->HasFlag(NODE_FORCE_XBL_BINDINGS),
1103 "Parent in document but flagged as forcing XBL");
1104 NS_PRECONDITION(aBindingParent != this,
1105 "Content must not be its own binding parent");
1106 NS_PRECONDITION(!IsRootOfNativeAnonymousSubtree() ||
1107 aBindingParent == aParent,
1108 "Native anonymous content must have its parent as its "
1109 "own binding parent");
1110 NS_PRECONDITION(aBindingParent || !aParent ||
1111 aBindingParent == aParent->GetBindingParent(),
1112 "We should be passed the right binding parent");
1114 #ifdef MOZ_XUL
1115 // First set the binding parent
1116 nsXULElement* xulElem = nsXULElement::FromContent(this);
1117 if (xulElem) {
1118 xulElem->SetXULBindingParent(aBindingParent);
1119 }
1120 else
1121 #endif
1122 {
1123 if (aBindingParent) {
1124 nsDOMSlots *slots = DOMSlots();
1126 slots->mBindingParent = aBindingParent; // Weak, so no addref happens.
1127 }
1128 }
1129 NS_ASSERTION(!aBindingParent || IsRootOfNativeAnonymousSubtree() ||
1130 !HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) ||
1131 (aParent && aParent->IsInNativeAnonymousSubtree()),
1132 "Trying to re-bind content from native anonymous subtree to "
1133 "non-native anonymous parent!");
1134 if (aParent) {
1135 if (aParent->IsInNativeAnonymousSubtree()) {
1136 SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
1137 }
1138 if (aParent->HasFlag(NODE_CHROME_ONLY_ACCESS)) {
1139 SetFlags(NODE_CHROME_ONLY_ACCESS);
1140 }
1141 if (aParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
1142 SetFlags(NODE_IS_IN_SHADOW_TREE);
1143 }
1144 ShadowRoot* parentContainingShadow = aParent->GetContainingShadow();
1145 if (parentContainingShadow) {
1146 DOMSlots()->mContainingShadow = parentContainingShadow;
1147 }
1148 }
1150 bool hadForceXBL = HasFlag(NODE_FORCE_XBL_BINDINGS);
1152 // Now set the parent and set the "Force attach xbl" flag if needed.
1153 if (aParent) {
1154 if (!GetParent()) {
1155 NS_ADDREF(aParent);
1156 }
1157 mParent = aParent;
1159 if (aParent->HasFlag(NODE_FORCE_XBL_BINDINGS)) {
1160 SetFlags(NODE_FORCE_XBL_BINDINGS);
1161 }
1162 }
1163 else {
1164 mParent = aDocument;
1165 }
1166 SetParentIsContent(aParent);
1168 // XXXbz sXBL/XBL2 issue!
1170 // Finally, set the document
1171 if (aDocument) {
1172 // Notify XBL- & nsIAnonymousContentCreator-generated
1173 // anonymous content that the document is changing.
1174 // XXXbz ordering issues here? Probably not, since ChangeDocumentFor is
1175 // just pretty broken anyway.... Need to get it working.
1176 // XXXbz XBL doesn't handle this (asserts), and we don't really want
1177 // to be doing this during parsing anyway... sort this out.
1178 // aDocument->BindingManager()->ChangeDocumentFor(this, nullptr,
1179 // aDocument);
1181 // We no longer need to track the subtree pointer (and in fact we'll assert
1182 // if we do this any later).
1183 ClearSubtreeRootPointer();
1185 // Being added to a document.
1186 SetInDocument();
1188 if (GetCustomElementData()) {
1189 // Enqueue an attached callback for the custom element.
1190 aDocument->EnqueueLifecycleCallback(nsIDocument::eAttached, this);
1191 }
1193 // Unset this flag since we now really are in a document.
1194 UnsetFlags(NODE_FORCE_XBL_BINDINGS |
1195 // And clear the lazy frame construction bits.
1196 NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
1197 // And the restyle bits
1198 ELEMENT_ALL_RESTYLE_FLAGS);
1200 // Propagate scoped style sheet tracking bit.
1201 SetIsElementInStyleScope(mParent->IsElementInStyleScope());
1202 } else {
1203 // If we're not in the doc, update our subtree pointer.
1204 SetSubtreeRootPointer(aParent->SubtreeRoot());
1205 }
1207 // This has to be here, rather than in nsGenericHTMLElement::BindToTree,
1208 // because it has to happen after updating the parent pointer, but before
1209 // recursively binding the kids.
1210 if (IsHTML()) {
1211 SetDirOnBind(this, aParent);
1212 }
1214 // If NODE_FORCE_XBL_BINDINGS was set we might have anonymous children
1215 // that also need to be told that they are moving.
1216 nsresult rv;
1217 if (hadForceXBL) {
1218 nsBindingManager* bmgr = OwnerDoc()->BindingManager();
1220 nsXBLBinding* contBinding = bmgr->GetBindingWithContent(this);
1221 // First check if we have a binding...
1222 if (contBinding) {
1223 nsCOMPtr<nsIContent> anonRoot = contBinding->GetAnonymousContent();
1224 bool allowScripts = contBinding->AllowScripts();
1225 for (nsCOMPtr<nsIContent> child = anonRoot->GetFirstChild();
1226 child;
1227 child = child->GetNextSibling()) {
1228 rv = child->BindToTree(aDocument, this, this, allowScripts);
1229 NS_ENSURE_SUCCESS(rv, rv);
1230 }
1231 }
1232 }
1234 UpdateEditableState(false);
1236 // Now recurse into our kids
1237 for (nsIContent* child = GetFirstChild(); child;
1238 child = child->GetNextSibling()) {
1239 rv = child->BindToTree(aDocument, this, aBindingParent,
1240 aCompileEventHandlers);
1241 NS_ENSURE_SUCCESS(rv, rv);
1242 }
1244 nsNodeUtils::ParentChainChanged(this);
1246 if (HasID()) {
1247 AddToIdTable(DoGetID());
1248 }
1250 if (MayHaveStyle() && !IsXUL()) {
1251 // XXXbz if we already have a style attr parsed, this won't do
1252 // anything... need to fix that.
1253 // If MayHaveStyle() is true, we must be an nsStyledElement
1254 static_cast<nsStyledElement*>(this)->ReparseStyleAttribute(false);
1255 }
1257 if (aDocument) {
1258 // If we're in a document now, let our mapped attrs know what their new
1259 // sheet is. This is safe to run for non-mapped-attribute elements too;
1260 // it'll just do a small bit of unnecessary work. But most elements in
1261 // practice are mapped-attribute elements.
1262 nsHTMLStyleSheet* sheet = aDocument->GetAttributeStyleSheet();
1263 if (sheet) {
1264 mAttrsAndChildren.SetMappedAttrStyleSheet(sheet);
1265 }
1266 }
1268 // XXXbz script execution during binding can trigger some of these
1269 // postcondition asserts.... But we do want that, since things will
1270 // generally be quite broken when that happens.
1271 NS_POSTCONDITION(aDocument == GetCurrentDoc(), "Bound to wrong document");
1272 NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent");
1273 NS_POSTCONDITION(aBindingParent == GetBindingParent(),
1274 "Bound to wrong binding parent");
1276 return NS_OK;
1277 }
1279 class RemoveFromBindingManagerRunnable : public nsRunnable {
1280 public:
1281 RemoveFromBindingManagerRunnable(nsBindingManager* aManager,
1282 Element* aElement,
1283 nsIDocument* aDoc):
1284 mManager(aManager), mElement(aElement), mDoc(aDoc)
1285 {}
1287 NS_IMETHOD Run()
1288 {
1289 // It may be the case that the element was removed from the
1290 // DOM, causing this runnable to be created, then inserted back
1291 // into the document before the this runnable had a chance to
1292 // tear down the binding. Only tear down the binding if the element
1293 // is still no longer in the DOM. nsXBLService::LoadBinding tears
1294 // down the old binding if the element is inserted back into the
1295 // DOM and loads a different binding.
1296 if (!mElement->IsInDoc()) {
1297 mManager->RemovedFromDocumentInternal(mElement, mDoc);
1298 }
1300 return NS_OK;
1301 }
1303 private:
1304 nsRefPtr<nsBindingManager> mManager;
1305 nsRefPtr<Element> mElement;
1306 nsCOMPtr<nsIDocument> mDoc;
1307 };
1309 void
1310 Element::UnbindFromTree(bool aDeep, bool aNullParent)
1311 {
1312 NS_PRECONDITION(aDeep || (!GetCurrentDoc() && !GetBindingParent()),
1313 "Shallow unbind won't clear document and binding parent on "
1314 "kids!");
1316 RemoveFromIdTable();
1318 // Make sure to unbind this node before doing the kids
1319 nsIDocument *document =
1320 HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetCurrentDoc();
1322 if (aNullParent) {
1323 if (IsFullScreenAncestor()) {
1324 // The element being removed is an ancestor of the full-screen element,
1325 // exit full-screen state.
1326 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
1327 NS_LITERAL_CSTRING("DOM"), OwnerDoc(),
1328 nsContentUtils::eDOM_PROPERTIES,
1329 "RemovedFullScreenElement");
1330 // Fully exit full-screen.
1331 nsIDocument::ExitFullscreen(OwnerDoc(), /* async */ false);
1332 }
1333 if (HasPointerLock()) {
1334 nsIDocument::UnlockPointer();
1335 }
1336 if (GetParent()) {
1337 nsINode* p = mParent;
1338 mParent = nullptr;
1339 NS_RELEASE(p);
1340 } else {
1341 mParent = nullptr;
1342 }
1343 SetParentIsContent(false);
1344 }
1345 ClearInDocument();
1347 // Begin keeping track of our subtree root.
1348 SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot());
1350 if (document) {
1351 // Notify XBL- & nsIAnonymousContentCreator-generated
1352 // anonymous content that the document is changing.
1353 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
1354 nsContentUtils::AddScriptRunner(
1355 new RemoveFromBindingManagerRunnable(document->BindingManager(), this,
1356 document));
1357 }
1359 document->ClearBoxObjectFor(this);
1361 if (GetCustomElementData()) {
1362 // Enqueue a detached callback for the custom element.
1363 document->EnqueueLifecycleCallback(nsIDocument::eDetached, this);
1364 }
1365 }
1367 // Ensure that CSS transitions don't continue on an element at a
1368 // different place in the tree (even if reinserted before next
1369 // animation refresh).
1370 // FIXME (Bug 522599): Need a test for this.
1371 if (HasFlag(NODE_HAS_PROPERTIES)) {
1372 DeleteProperty(nsGkAtoms::transitionsOfBeforeProperty);
1373 DeleteProperty(nsGkAtoms::transitionsOfAfterProperty);
1374 DeleteProperty(nsGkAtoms::transitionsProperty);
1375 DeleteProperty(nsGkAtoms::animationsOfBeforeProperty);
1376 DeleteProperty(nsGkAtoms::animationsOfAfterProperty);
1377 DeleteProperty(nsGkAtoms::animationsProperty);
1378 }
1380 // Unset this since that's what the old code effectively did.
1381 UnsetFlags(NODE_FORCE_XBL_BINDINGS | NODE_IS_IN_SHADOW_TREE);
1383 #ifdef MOZ_XUL
1384 nsXULElement* xulElem = nsXULElement::FromContent(this);
1385 if (xulElem) {
1386 xulElem->SetXULBindingParent(nullptr);
1387 }
1388 else
1389 #endif
1390 {
1391 nsDOMSlots *slots = GetExistingDOMSlots();
1392 if (slots) {
1393 slots->mBindingParent = nullptr;
1394 slots->mContainingShadow = nullptr;
1395 }
1396 }
1398 // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree,
1399 // because it has to happen after unsetting the parent pointer, but before
1400 // recursively unbinding the kids.
1401 if (IsHTML()) {
1402 ResetDir(this);
1403 }
1405 if (aDeep) {
1406 // Do the kids. Don't call GetChildCount() here since that'll force
1407 // XUL to generate template children, which there is no need for since
1408 // all we're going to do is unbind them anyway.
1409 uint32_t i, n = mAttrsAndChildren.ChildCount();
1411 for (i = 0; i < n; ++i) {
1412 // Note that we pass false for aNullParent here, since we don't want
1413 // the kids to forget us. We _do_ want them to forget their binding
1414 // parent, though, since this only walks non-anonymous kids.
1415 mAttrsAndChildren.ChildAt(i)->UnbindFromTree(true, false);
1416 }
1417 }
1419 nsNodeUtils::ParentChainChanged(this);
1420 }
1422 nsICSSDeclaration*
1423 Element::GetSMILOverrideStyle()
1424 {
1425 Element::nsDOMSlots *slots = DOMSlots();
1427 if (!slots->mSMILOverrideStyle) {
1428 slots->mSMILOverrideStyle = new nsDOMCSSAttributeDeclaration(this, true);
1429 }
1431 return slots->mSMILOverrideStyle;
1432 }
1434 css::StyleRule*
1435 Element::GetSMILOverrideStyleRule()
1436 {
1437 Element::nsDOMSlots *slots = GetExistingDOMSlots();
1438 return slots ? slots->mSMILOverrideStyleRule.get() : nullptr;
1439 }
1441 nsresult
1442 Element::SetSMILOverrideStyleRule(css::StyleRule* aStyleRule,
1443 bool aNotify)
1444 {
1445 Element::nsDOMSlots *slots = DOMSlots();
1447 slots->mSMILOverrideStyleRule = aStyleRule;
1449 if (aNotify) {
1450 nsIDocument* doc = GetCurrentDoc();
1451 // Only need to request a restyle if we're in a document. (We might not
1452 // be in a document, if we're clearing animation effects on a target node
1453 // that's been detached since the previous animation sample.)
1454 if (doc) {
1455 nsCOMPtr<nsIPresShell> shell = doc->GetShell();
1456 if (shell) {
1457 shell->RestyleForAnimation(this, eRestyle_Self);
1458 }
1459 }
1460 }
1462 return NS_OK;
1463 }
1465 bool
1466 Element::IsLabelable() const
1467 {
1468 return false;
1469 }
1471 css::StyleRule*
1472 Element::GetInlineStyleRule()
1473 {
1474 return nullptr;
1475 }
1477 nsresult
1478 Element::SetInlineStyleRule(css::StyleRule* aStyleRule,
1479 const nsAString* aSerialized,
1480 bool aNotify)
1481 {
1482 NS_NOTYETIMPLEMENTED("Element::SetInlineStyleRule");
1483 return NS_ERROR_NOT_IMPLEMENTED;
1484 }
1486 NS_IMETHODIMP_(bool)
1487 Element::IsAttributeMapped(const nsIAtom* aAttribute) const
1488 {
1489 return false;
1490 }
1492 nsChangeHint
1493 Element::GetAttributeChangeHint(const nsIAtom* aAttribute,
1494 int32_t aModType) const
1495 {
1496 return nsChangeHint(0);
1497 }
1499 nsIAtom *
1500 Element::GetClassAttributeName() const
1501 {
1502 return nullptr;
1503 }
1505 bool
1506 Element::FindAttributeDependence(const nsIAtom* aAttribute,
1507 const MappedAttributeEntry* const aMaps[],
1508 uint32_t aMapCount)
1509 {
1510 for (uint32_t mapindex = 0; mapindex < aMapCount; ++mapindex) {
1511 for (const MappedAttributeEntry* map = aMaps[mapindex];
1512 map->attribute; ++map) {
1513 if (aAttribute == *map->attribute) {
1514 return true;
1515 }
1516 }
1517 }
1519 return false;
1520 }
1522 already_AddRefed<nsINodeInfo>
1523 Element::GetExistingAttrNameFromQName(const nsAString& aStr) const
1524 {
1525 const nsAttrName* name = InternalGetExistingAttrNameFromQName(aStr);
1526 if (!name) {
1527 return nullptr;
1528 }
1530 nsCOMPtr<nsINodeInfo> nodeInfo;
1531 if (name->IsAtom()) {
1532 nodeInfo = mNodeInfo->NodeInfoManager()->
1533 GetNodeInfo(name->Atom(), nullptr, kNameSpaceID_None,
1534 nsIDOMNode::ATTRIBUTE_NODE);
1535 }
1536 else {
1537 nodeInfo = name->NodeInfo();
1538 }
1540 return nodeInfo.forget();
1541 }
1543 // static
1544 bool
1545 Element::ShouldBlur(nsIContent *aContent)
1546 {
1547 // Determine if the current element is focused, if it is not focused
1548 // then we should not try to blur
1549 nsIDocument *document = aContent->GetDocument();
1550 if (!document)
1551 return false;
1553 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(document->GetWindow());
1554 if (!window)
1555 return false;
1557 nsCOMPtr<nsPIDOMWindow> focusedFrame;
1558 nsIContent* contentToBlur =
1559 nsFocusManager::GetFocusedDescendant(window, false, getter_AddRefs(focusedFrame));
1560 if (contentToBlur == aContent)
1561 return true;
1563 // if focus on this element would get redirected, then check the redirected
1564 // content as well when blurring.
1565 return (contentToBlur && nsFocusManager::GetRedirectedFocus(aContent) == contentToBlur);
1566 }
1568 bool
1569 Element::IsNodeOfType(uint32_t aFlags) const
1570 {
1571 return !(aFlags & ~eCONTENT);
1572 }
1574 /* static */
1575 nsresult
1576 Element::DispatchEvent(nsPresContext* aPresContext,
1577 WidgetEvent* aEvent,
1578 nsIContent* aTarget,
1579 bool aFullDispatch,
1580 nsEventStatus* aStatus)
1581 {
1582 NS_PRECONDITION(aTarget, "Must have target");
1583 NS_PRECONDITION(aEvent, "Must have source event");
1584 NS_PRECONDITION(aStatus, "Null out param?");
1586 if (!aPresContext) {
1587 return NS_OK;
1588 }
1590 nsCOMPtr<nsIPresShell> shell = aPresContext->GetPresShell();
1591 if (!shell) {
1592 return NS_OK;
1593 }
1595 if (aFullDispatch) {
1596 return shell->HandleEventWithTarget(aEvent, nullptr, aTarget, aStatus);
1597 }
1599 return shell->HandleDOMEventWithTarget(aTarget, aEvent, aStatus);
1600 }
1602 /* static */
1603 nsresult
1604 Element::DispatchClickEvent(nsPresContext* aPresContext,
1605 WidgetInputEvent* aSourceEvent,
1606 nsIContent* aTarget,
1607 bool aFullDispatch,
1608 const EventFlags* aExtraEventFlags,
1609 nsEventStatus* aStatus)
1610 {
1611 NS_PRECONDITION(aTarget, "Must have target");
1612 NS_PRECONDITION(aSourceEvent, "Must have source event");
1613 NS_PRECONDITION(aStatus, "Null out param?");
1615 WidgetMouseEvent event(aSourceEvent->mFlags.mIsTrusted, NS_MOUSE_CLICK,
1616 aSourceEvent->widget, WidgetMouseEvent::eReal);
1617 event.refPoint = aSourceEvent->refPoint;
1618 uint32_t clickCount = 1;
1619 float pressure = 0;
1620 uint16_t inputSource = 0;
1621 WidgetMouseEvent* sourceMouseEvent = aSourceEvent->AsMouseEvent();
1622 if (sourceMouseEvent) {
1623 clickCount = sourceMouseEvent->clickCount;
1624 pressure = sourceMouseEvent->pressure;
1625 inputSource = sourceMouseEvent->inputSource;
1626 } else if (aSourceEvent->eventStructType == NS_KEY_EVENT) {
1627 inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
1628 }
1629 event.pressure = pressure;
1630 event.clickCount = clickCount;
1631 event.inputSource = inputSource;
1632 event.modifiers = aSourceEvent->modifiers;
1633 if (aExtraEventFlags) {
1634 // Be careful not to overwrite existing flags!
1635 event.mFlags.Union(*aExtraEventFlags);
1636 }
1638 return DispatchEvent(aPresContext, &event, aTarget, aFullDispatch, aStatus);
1639 }
1641 nsIFrame*
1642 Element::GetPrimaryFrame(mozFlushType aType)
1643 {
1644 nsIDocument* doc = GetCurrentDoc();
1645 if (!doc) {
1646 return nullptr;
1647 }
1649 // Cause a flush, so we get up-to-date frame
1650 // information
1651 if (aType != Flush_None) {
1652 doc->FlushPendingNotifications(aType);
1653 }
1655 return GetPrimaryFrame();
1656 }
1658 //----------------------------------------------------------------------
1659 nsresult
1660 Element::LeaveLink(nsPresContext* aPresContext)
1661 {
1662 nsILinkHandler *handler = aPresContext->GetLinkHandler();
1663 if (!handler) {
1664 return NS_OK;
1665 }
1667 return handler->OnLeaveLink();
1668 }
1670 nsresult
1671 Element::SetEventHandler(nsIAtom* aEventName,
1672 const nsAString& aValue,
1673 bool aDefer)
1674 {
1675 nsIDocument *ownerDoc = OwnerDoc();
1676 if (ownerDoc->IsLoadedAsData()) {
1677 // Make this a no-op rather than throwing an error to avoid
1678 // the error causing problems setting the attribute.
1679 return NS_OK;
1680 }
1682 NS_PRECONDITION(aEventName, "Must have event name!");
1683 bool defer = true;
1684 EventListenerManager* manager =
1685 GetEventListenerManagerForAttr(aEventName, &defer);
1686 if (!manager) {
1687 return NS_OK;
1688 }
1690 defer = defer && aDefer; // only defer if everyone agrees...
1691 manager->SetEventHandler(aEventName, aValue,
1692 nsIProgrammingLanguage::JAVASCRIPT,
1693 defer, !nsContentUtils::IsChromeDoc(ownerDoc),
1694 this);
1695 return NS_OK;
1696 }
1699 //----------------------------------------------------------------------
1701 const nsAttrName*
1702 Element::InternalGetExistingAttrNameFromQName(const nsAString& aStr) const
1703 {
1704 return mAttrsAndChildren.GetExistingAttrNameFromQName(aStr);
1705 }
1707 bool
1708 Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
1709 nsIAtom* aName,
1710 nsIAtom* aPrefix,
1711 const nsAttrValueOrString& aValue,
1712 bool aNotify,
1713 nsAttrValue& aOldValue,
1714 uint8_t* aModType,
1715 bool* aHasListeners)
1716 {
1717 bool modification = false;
1718 *aHasListeners = aNotify &&
1719 nsContentUtils::HasMutationListeners(this,
1720 NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
1721 this);
1723 // If we have no listeners and aNotify is false, we are almost certainly
1724 // coming from the content sink and will almost certainly have no previous
1725 // value. Even if we do, setting the value is cheap when we have no
1726 // listeners and don't plan to notify. The check for aNotify here is an
1727 // optimization, the check for *aHasListeners is a correctness issue.
1728 if (*aHasListeners || aNotify) {
1729 nsAttrInfo info(GetAttrInfo(aNamespaceID, aName));
1730 if (info.mValue) {
1731 // Check whether the old value is the same as the new one. Note that we
1732 // only need to actually _get_ the old value if we have listeners or
1733 // if the element is a custom element (because it may have an
1734 // attribute changed callback).
1735 if (*aHasListeners || GetCustomElementData()) {
1736 // Need to store the old value.
1737 //
1738 // If the current attribute value contains a pointer to some other data
1739 // structure that gets updated in the process of setting the attribute
1740 // we'll no longer have the old value of the attribute. Therefore, we
1741 // should serialize the attribute value now to keep a snapshot.
1742 //
1743 // We have to serialize the value anyway in order to create the
1744 // mutation event so there's no cost in doing it now.
1745 aOldValue.SetToSerialized(*info.mValue);
1746 }
1747 bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
1748 if (valueMatches && aPrefix == info.mName->GetPrefix()) {
1749 return true;
1750 }
1751 modification = true;
1752 }
1753 }
1754 *aModType = modification ?
1755 static_cast<uint8_t>(nsIDOMMutationEvent::MODIFICATION) :
1756 static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION);
1757 return false;
1758 }
1760 bool
1761 Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* aName,
1762 nsIAtom* aPrefix,
1763 const nsAttrValueOrString& aValue,
1764 bool aNotify, nsAttrValue& aOldValue,
1765 uint8_t* aModType, bool* aHasListeners)
1766 {
1767 if (!MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, aValue, aNotify,
1768 aOldValue, aModType, aHasListeners)) {
1769 return false;
1770 }
1772 nsAutoScriptBlocker scriptBlocker;
1773 nsNodeUtils::AttributeSetToCurrentValue(this, aNamespaceID, aName);
1774 return true;
1775 }
1777 nsresult
1778 Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName,
1779 nsIAtom* aPrefix, const nsAString& aValue,
1780 bool aNotify)
1781 {
1782 // Keep this in sync with SetParsedAttr below
1784 NS_ENSURE_ARG_POINTER(aName);
1785 NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
1786 "Don't call SetAttr with unknown namespace");
1788 if (!mAttrsAndChildren.CanFitMoreAttrs()) {
1789 return NS_ERROR_FAILURE;
1790 }
1792 uint8_t modType;
1793 bool hasListeners;
1794 nsAttrValueOrString value(aValue);
1795 nsAttrValue oldValue;
1797 if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
1798 oldValue, &modType, &hasListeners)) {
1799 return NS_OK;
1800 }
1802 nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
1803 NS_ENSURE_SUCCESS(rv, rv);
1805 if (aNotify) {
1806 nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType);
1807 }
1809 // Hold a script blocker while calling ParseAttribute since that can call
1810 // out to id-observers
1811 nsAutoScriptBlocker scriptBlocker;
1813 nsAttrValue attrValue;
1814 if (!ParseAttribute(aNamespaceID, aName, aValue, attrValue)) {
1815 attrValue.SetTo(aValue);
1816 }
1818 return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
1819 attrValue, modType, hasListeners, aNotify,
1820 kCallAfterSetAttr);
1821 }
1823 nsresult
1824 Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName,
1825 nsIAtom* aPrefix, nsAttrValue& aParsedValue,
1826 bool aNotify)
1827 {
1828 // Keep this in sync with SetAttr above
1830 NS_ENSURE_ARG_POINTER(aName);
1831 NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
1832 "Don't call SetAttr with unknown namespace");
1834 if (!mAttrsAndChildren.CanFitMoreAttrs()) {
1835 return NS_ERROR_FAILURE;
1836 }
1839 uint8_t modType;
1840 bool hasListeners;
1841 nsAttrValueOrString value(aParsedValue);
1842 nsAttrValue oldValue;
1844 if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
1845 oldValue, &modType, &hasListeners)) {
1846 return NS_OK;
1847 }
1849 nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
1850 NS_ENSURE_SUCCESS(rv, rv);
1852 if (aNotify) {
1853 nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType);
1854 }
1856 return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
1857 aParsedValue, modType, hasListeners, aNotify,
1858 kCallAfterSetAttr);
1859 }
1861 nsresult
1862 Element::SetAttrAndNotify(int32_t aNamespaceID,
1863 nsIAtom* aName,
1864 nsIAtom* aPrefix,
1865 const nsAttrValue& aOldValue,
1866 nsAttrValue& aParsedValue,
1867 uint8_t aModType,
1868 bool aFireMutation,
1869 bool aNotify,
1870 bool aCallAfterSetAttr)
1871 {
1872 nsresult rv;
1874 nsIDocument* document = GetCurrentDoc();
1875 mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
1877 nsMutationGuard::DidMutate();
1879 // Copy aParsedValue for later use since it will be lost when we call
1880 // SetAndTakeMappedAttr below
1881 nsAttrValue aValueForAfterSetAttr;
1882 if (aCallAfterSetAttr) {
1883 aValueForAfterSetAttr.SetTo(aParsedValue);
1884 }
1886 bool hadValidDir = false;
1887 bool hadDirAuto = false;
1889 if (aNamespaceID == kNameSpaceID_None) {
1890 if (aName == nsGkAtoms::dir) {
1891 hadValidDir = HasValidDir() || IsHTML(nsGkAtoms::bdi);
1892 hadDirAuto = HasDirAuto(); // already takes bdi into account
1893 }
1895 // XXXbz Perhaps we should push up the attribute mapping function
1896 // stuff to Element?
1897 if (!IsAttributeMapped(aName) ||
1898 !SetMappedAttribute(document, aName, aParsedValue, &rv)) {
1899 rv = mAttrsAndChildren.SetAndTakeAttr(aName, aParsedValue);
1900 }
1901 }
1902 else {
1903 nsCOMPtr<nsINodeInfo> ni;
1904 ni = mNodeInfo->NodeInfoManager()->GetNodeInfo(aName, aPrefix,
1905 aNamespaceID,
1906 nsIDOMNode::ATTRIBUTE_NODE);
1908 rv = mAttrsAndChildren.SetAndTakeAttr(ni, aParsedValue);
1909 }
1910 NS_ENSURE_SUCCESS(rv, rv);
1912 if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
1913 nsRefPtr<nsXBLBinding> binding = GetXBLBinding();
1914 if (binding) {
1915 binding->AttributeChanged(aName, aNamespaceID, false, aNotify);
1916 }
1917 }
1919 UpdateState(aNotify);
1921 nsIDocument* ownerDoc = OwnerDoc();
1922 if (ownerDoc && GetCustomElementData()) {
1923 nsCOMPtr<nsIAtom> oldValueAtom = aOldValue.GetAsAtom();
1924 nsCOMPtr<nsIAtom> newValueAtom = aValueForAfterSetAttr.GetAsAtom();
1925 LifecycleCallbackArgs args = {
1926 nsDependentAtomString(aName),
1927 aModType == nsIDOMMutationEvent::ADDITION ?
1928 NullString() : nsDependentAtomString(oldValueAtom),
1929 nsDependentAtomString(newValueAtom)
1930 };
1932 ownerDoc->EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, this, &args);
1933 }
1935 if (aCallAfterSetAttr) {
1936 rv = AfterSetAttr(aNamespaceID, aName, &aValueForAfterSetAttr, aNotify);
1937 NS_ENSURE_SUCCESS(rv, rv);
1939 if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
1940 OnSetDirAttr(this, &aValueForAfterSetAttr,
1941 hadValidDir, hadDirAuto, aNotify);
1942 }
1943 }
1945 if (aNotify) {
1946 nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType);
1947 }
1949 if (aFireMutation) {
1950 InternalMutationEvent mutation(true, NS_MUTATION_ATTRMODIFIED);
1952 nsAutoString ns;
1953 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
1954 Attr* attrNode =
1955 GetAttributeNodeNSInternal(ns, nsDependentAtomString(aName));
1956 mutation.mRelatedNode = attrNode;
1958 mutation.mAttrName = aName;
1959 nsAutoString newValue;
1960 GetAttr(aNamespaceID, aName, newValue);
1961 if (!newValue.IsEmpty()) {
1962 mutation.mNewAttrValue = do_GetAtom(newValue);
1963 }
1964 if (!aOldValue.IsEmptyString()) {
1965 mutation.mPrevAttrValue = aOldValue.GetAsAtom();
1966 }
1967 mutation.mAttrChange = aModType;
1969 mozAutoSubtreeModified subtree(OwnerDoc(), this);
1970 (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe();
1971 }
1973 return NS_OK;
1974 }
1976 bool
1977 Element::ParseAttribute(int32_t aNamespaceID,
1978 nsIAtom* aAttribute,
1979 const nsAString& aValue,
1980 nsAttrValue& aResult)
1981 {
1982 return false;
1983 }
1985 bool
1986 Element::SetMappedAttribute(nsIDocument* aDocument,
1987 nsIAtom* aName,
1988 nsAttrValue& aValue,
1989 nsresult* aRetval)
1990 {
1991 *aRetval = NS_OK;
1992 return false;
1993 }
1995 EventListenerManager*
1996 Element::GetEventListenerManagerForAttr(nsIAtom* aAttrName,
1997 bool* aDefer)
1998 {
1999 *aDefer = true;
2000 return GetOrCreateListenerManager();
2001 }
2003 Element::nsAttrInfo
2004 Element::GetAttrInfo(int32_t aNamespaceID, nsIAtom* aName) const
2005 {
2006 NS_ASSERTION(nullptr != aName, "must have attribute name");
2007 NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
2008 "must have a real namespace ID!");
2010 int32_t index = mAttrsAndChildren.IndexOfAttr(aName, aNamespaceID);
2011 if (index >= 0) {
2012 return nsAttrInfo(mAttrsAndChildren.AttrNameAt(index),
2013 mAttrsAndChildren.AttrAt(index));
2014 }
2016 return nsAttrInfo(nullptr, nullptr);
2017 }
2020 bool
2021 Element::GetAttr(int32_t aNameSpaceID, nsIAtom* aName,
2022 nsAString& aResult) const
2023 {
2024 DOMString str;
2025 bool haveAttr = GetAttr(aNameSpaceID, aName, str);
2026 str.ToString(aResult);
2027 return haveAttr;
2028 }
2030 int32_t
2031 Element::FindAttrValueIn(int32_t aNameSpaceID,
2032 nsIAtom* aName,
2033 AttrValuesArray* aValues,
2034 nsCaseTreatment aCaseSensitive) const
2035 {
2036 NS_ASSERTION(aName, "Must have attr name");
2037 NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown, "Must have namespace");
2038 NS_ASSERTION(aValues, "Null value array");
2040 const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName, aNameSpaceID);
2041 if (val) {
2042 for (int32_t i = 0; aValues[i]; ++i) {
2043 if (val->Equals(*aValues[i], aCaseSensitive)) {
2044 return i;
2045 }
2046 }
2047 return ATTR_VALUE_NO_MATCH;
2048 }
2049 return ATTR_MISSING;
2050 }
2052 nsresult
2053 Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
2054 bool aNotify)
2055 {
2056 NS_ASSERTION(nullptr != aName, "must have attribute name");
2058 int32_t index = mAttrsAndChildren.IndexOfAttr(aName, aNameSpaceID);
2059 if (index < 0) {
2060 return NS_OK;
2061 }
2063 nsresult rv = BeforeSetAttr(aNameSpaceID, aName, nullptr, aNotify);
2064 NS_ENSURE_SUCCESS(rv, rv);
2066 nsIDocument *document = GetCurrentDoc();
2067 mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
2069 if (aNotify) {
2070 nsNodeUtils::AttributeWillChange(this, aNameSpaceID, aName,
2071 nsIDOMMutationEvent::REMOVAL);
2072 }
2074 bool hasMutationListeners = aNotify &&
2075 nsContentUtils::HasMutationListeners(this,
2076 NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
2077 this);
2079 // Grab the attr node if needed before we remove it from the attr map
2080 nsRefPtr<Attr> attrNode;
2081 if (hasMutationListeners) {
2082 nsAutoString ns;
2083 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, ns);
2084 attrNode = GetAttributeNodeNSInternal(ns, nsDependentAtomString(aName));
2085 }
2087 // Clear binding to nsIDOMMozNamedAttrMap
2088 nsDOMSlots *slots = GetExistingDOMSlots();
2089 if (slots && slots->mAttributeMap) {
2090 slots->mAttributeMap->DropAttribute(aNameSpaceID, aName);
2091 }
2093 // The id-handling code, and in the future possibly other code, need to
2094 // react to unexpected attribute changes.
2095 nsMutationGuard::DidMutate();
2097 bool hadValidDir = false;
2098 bool hadDirAuto = false;
2100 if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
2101 hadValidDir = HasValidDir() || IsHTML(nsGkAtoms::bdi);
2102 hadDirAuto = HasDirAuto(); // already takes bdi into account
2103 }
2105 nsAttrValue oldValue;
2106 rv = mAttrsAndChildren.RemoveAttrAt(index, oldValue);
2107 NS_ENSURE_SUCCESS(rv, rv);
2109 if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
2110 nsRefPtr<nsXBLBinding> binding = GetXBLBinding();
2111 if (binding) {
2112 binding->AttributeChanged(aName, aNameSpaceID, true, aNotify);
2113 }
2114 }
2116 UpdateState(aNotify);
2118 nsIDocument* ownerDoc = OwnerDoc();
2119 if (ownerDoc && GetCustomElementData()) {
2120 nsCOMPtr<nsIAtom> oldValueAtom = oldValue.GetAsAtom();
2121 LifecycleCallbackArgs args = {
2122 nsDependentAtomString(aName),
2123 nsDependentAtomString(oldValueAtom),
2124 NullString()
2125 };
2127 ownerDoc->EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, this, &args);
2128 }
2130 if (aNotify) {
2131 nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
2132 nsIDOMMutationEvent::REMOVAL);
2133 }
2135 rv = AfterSetAttr(aNameSpaceID, aName, nullptr, aNotify);
2136 NS_ENSURE_SUCCESS(rv, rv);
2138 if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
2139 OnSetDirAttr(this, nullptr, hadValidDir, hadDirAuto, aNotify);
2140 }
2142 if (hasMutationListeners) {
2143 InternalMutationEvent mutation(true, NS_MUTATION_ATTRMODIFIED);
2145 mutation.mRelatedNode = attrNode;
2146 mutation.mAttrName = aName;
2148 nsAutoString value;
2149 oldValue.ToString(value);
2150 if (!value.IsEmpty())
2151 mutation.mPrevAttrValue = do_GetAtom(value);
2152 mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
2154 mozAutoSubtreeModified subtree(OwnerDoc(), this);
2155 (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe();
2156 }
2158 return NS_OK;
2159 }
2161 const nsAttrName*
2162 Element::GetAttrNameAt(uint32_t aIndex) const
2163 {
2164 return mAttrsAndChildren.GetSafeAttrNameAt(aIndex);
2165 }
2167 uint32_t
2168 Element::GetAttrCount() const
2169 {
2170 return mAttrsAndChildren.AttrCount();
2171 }
2173 void
2174 Element::DescribeAttribute(uint32_t index, nsAString& aOutDescription) const
2175 {
2176 // name
2177 mAttrsAndChildren.AttrNameAt(index)->GetQualifiedName(aOutDescription);
2179 // value
2180 aOutDescription.AppendLiteral("=\"");
2181 nsAutoString value;
2182 mAttrsAndChildren.AttrAt(index)->ToString(value);
2183 for (int i = value.Length(); i >= 0; --i) {
2184 if (value[i] == char16_t('"'))
2185 value.Insert(char16_t('\\'), uint32_t(i));
2186 }
2187 aOutDescription.Append(value);
2188 aOutDescription.AppendLiteral("\"");
2189 }
2191 #ifdef DEBUG
2192 void
2193 Element::ListAttributes(FILE* out) const
2194 {
2195 uint32_t index, count = mAttrsAndChildren.AttrCount();
2196 for (index = 0; index < count; index++) {
2197 nsAutoString attributeDescription;
2198 DescribeAttribute(index, attributeDescription);
2200 fputs(" ", out);
2201 fputs(NS_LossyConvertUTF16toASCII(attributeDescription).get(), out);
2202 }
2203 }
2205 void
2206 Element::List(FILE* out, int32_t aIndent,
2207 const nsCString& aPrefix) const
2208 {
2209 int32_t indent;
2210 for (indent = aIndent; --indent >= 0; ) fputs(" ", out);
2212 fputs(aPrefix.get(), out);
2214 fputs(NS_LossyConvertUTF16toASCII(mNodeInfo->QualifiedName()).get(), out);
2216 fprintf(out, "@%p", (void *)this);
2218 ListAttributes(out);
2220 fprintf(out, " state=[%llx]",
2221 static_cast<unsigned long long>(State().GetInternalValue()));
2222 fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
2223 if (IsCommonAncestorForRangeInSelection()) {
2224 nsRange::RangeHashTable* ranges =
2225 static_cast<nsRange::RangeHashTable*>(GetProperty(nsGkAtoms::range));
2226 fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0);
2227 }
2228 fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));
2229 fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get());
2231 nsIContent* child = GetFirstChild();
2232 if (child) {
2233 fputs("\n", out);
2235 for (; child; child = child->GetNextSibling()) {
2236 child->List(out, aIndent + 1);
2237 }
2239 for (indent = aIndent; --indent >= 0; ) fputs(" ", out);
2240 }
2242 fputs(">\n", out);
2244 Element* nonConstThis = const_cast<Element*>(this);
2246 // XXX sXBL/XBL2 issue! Owner or current document?
2247 nsIDocument *document = OwnerDoc();
2249 // Note: not listing nsIAnonymousContentCreator-created content...
2251 nsBindingManager* bindingManager = document->BindingManager();
2252 nsCOMPtr<nsIDOMNodeList> anonymousChildren;
2253 bindingManager->GetAnonymousNodesFor(nonConstThis,
2254 getter_AddRefs(anonymousChildren));
2256 if (anonymousChildren) {
2257 uint32_t length = 0;
2258 anonymousChildren->GetLength(&length);
2260 for (indent = aIndent; --indent >= 0; ) fputs(" ", out);
2261 fputs("anonymous-children<\n", out);
2263 for (uint32_t i = 0; i < length; ++i) {
2264 nsCOMPtr<nsIDOMNode> node;
2265 anonymousChildren->Item(i, getter_AddRefs(node));
2266 nsCOMPtr<nsIContent> child = do_QueryInterface(node);
2267 child->List(out, aIndent + 1);
2268 }
2270 for (indent = aIndent; --indent >= 0; ) fputs(" ", out);
2271 fputs(">\n", out);
2273 bool outHeader = false;
2274 ExplicitChildIterator iter(nonConstThis);
2275 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
2276 if (!outHeader) {
2277 outHeader = true;
2279 for (indent = aIndent; --indent >= 0; ) fputs(" ", out);
2280 fputs("content-list<\n", out);
2281 }
2283 child->List(out, aIndent + 1);
2284 }
2286 if (outHeader) {
2287 for (indent = aIndent; --indent >= 0; ) fputs(" ", out);
2288 fputs(">\n", out);
2289 }
2290 }
2291 }
2293 void
2294 Element::DumpContent(FILE* out, int32_t aIndent,
2295 bool aDumpAll) const
2296 {
2297 int32_t indent;
2298 for (indent = aIndent; --indent >= 0; ) fputs(" ", out);
2300 const nsString& buf = mNodeInfo->QualifiedName();
2301 fputs("<", out);
2302 fputs(NS_LossyConvertUTF16toASCII(buf).get(), out);
2304 if(aDumpAll) ListAttributes(out);
2306 fputs(">", out);
2308 if(aIndent) fputs("\n", out);
2310 for (nsIContent* child = GetFirstChild();
2311 child;
2312 child = child->GetNextSibling()) {
2313 int32_t indent = aIndent ? aIndent + 1 : 0;
2314 child->DumpContent(out, indent, aDumpAll);
2315 }
2316 for (indent = aIndent; --indent >= 0; ) fputs(" ", out);
2317 fputs("</", out);
2318 fputs(NS_LossyConvertUTF16toASCII(buf).get(), out);
2319 fputs(">", out);
2321 if(aIndent) fputs("\n", out);
2322 }
2323 #endif
2325 void
2326 Element::Describe(nsAString& aOutDescription) const
2327 {
2328 aOutDescription.Append(mNodeInfo->QualifiedName());
2329 aOutDescription.AppendPrintf("@%p", (void *)this);
2331 uint32_t index, count = mAttrsAndChildren.AttrCount();
2332 for (index = 0; index < count; index++) {
2333 aOutDescription.Append(' ');
2334 nsAutoString attributeDescription;
2335 DescribeAttribute(index, attributeDescription);
2336 aOutDescription.Append(attributeDescription);
2337 }
2338 }
2340 bool
2341 Element::CheckHandleEventForLinksPrecondition(EventChainVisitor& aVisitor,
2342 nsIURI** aURI) const
2343 {
2344 if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault ||
2345 (!aVisitor.mEvent->mFlags.mIsTrusted &&
2346 (aVisitor.mEvent->message != NS_MOUSE_CLICK) &&
2347 (aVisitor.mEvent->message != NS_KEY_PRESS) &&
2348 (aVisitor.mEvent->message != NS_UI_ACTIVATE)) ||
2349 !aVisitor.mPresContext ||
2350 aVisitor.mEvent->mFlags.mMultipleActionsPrevented) {
2351 return false;
2352 }
2354 // Make sure we actually are a link
2355 return IsLink(aURI);
2356 }
2358 nsresult
2359 Element::PreHandleEventForLinks(EventChainPreVisitor& aVisitor)
2360 {
2361 // Optimisation: return early if this event doesn't interest us.
2362 // IMPORTANT: this switch and the switch below it must be kept in sync!
2363 switch (aVisitor.mEvent->message) {
2364 case NS_MOUSE_ENTER_SYNTH:
2365 case NS_FOCUS_CONTENT:
2366 case NS_MOUSE_EXIT_SYNTH:
2367 case NS_BLUR_CONTENT:
2368 break;
2369 default:
2370 return NS_OK;
2371 }
2373 // Make sure we meet the preconditions before continuing
2374 nsCOMPtr<nsIURI> absURI;
2375 if (!CheckHandleEventForLinksPrecondition(aVisitor, getter_AddRefs(absURI))) {
2376 return NS_OK;
2377 }
2379 nsresult rv = NS_OK;
2381 // We do the status bar updates in PreHandleEvent so that the status bar gets
2382 // updated even if the event is consumed before we have a chance to set it.
2383 switch (aVisitor.mEvent->message) {
2384 // Set the status bar similarly for mouseover and focus
2385 case NS_MOUSE_ENTER_SYNTH:
2386 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
2387 // FALL THROUGH
2388 case NS_FOCUS_CONTENT: {
2389 InternalFocusEvent* focusEvent = aVisitor.mEvent->AsFocusEvent();
2390 if (!focusEvent || !focusEvent->isRefocus) {
2391 nsAutoString target;
2392 GetLinkTarget(target);
2393 nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
2394 false, true, true);
2395 // Make sure any ancestor links don't also TriggerLink
2396 aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
2397 }
2398 break;
2399 }
2400 case NS_MOUSE_EXIT_SYNTH:
2401 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
2402 // FALL THROUGH
2403 case NS_BLUR_CONTENT:
2404 rv = LeaveLink(aVisitor.mPresContext);
2405 if (NS_SUCCEEDED(rv)) {
2406 aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
2407 }
2408 break;
2410 default:
2411 // switch not in sync with the optimization switch earlier in this function
2412 NS_NOTREACHED("switch statements not in sync");
2413 return NS_ERROR_UNEXPECTED;
2414 }
2416 return rv;
2417 }
2419 nsresult
2420 Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor)
2421 {
2422 // Optimisation: return early if this event doesn't interest us.
2423 // IMPORTANT: this switch and the switch below it must be kept in sync!
2424 switch (aVisitor.mEvent->message) {
2425 case NS_MOUSE_BUTTON_DOWN:
2426 case NS_MOUSE_CLICK:
2427 case NS_UI_ACTIVATE:
2428 case NS_KEY_PRESS:
2429 break;
2430 default:
2431 return NS_OK;
2432 }
2434 // Make sure we meet the preconditions before continuing
2435 nsCOMPtr<nsIURI> absURI;
2436 if (!CheckHandleEventForLinksPrecondition(aVisitor, getter_AddRefs(absURI))) {
2437 return NS_OK;
2438 }
2440 nsresult rv = NS_OK;
2442 switch (aVisitor.mEvent->message) {
2443 case NS_MOUSE_BUTTON_DOWN:
2444 {
2445 if (aVisitor.mEvent->AsMouseEvent()->button ==
2446 WidgetMouseEvent::eLeftButton) {
2447 // don't make the link grab the focus if there is no link handler
2448 nsILinkHandler *handler = aVisitor.mPresContext->GetLinkHandler();
2449 nsIDocument *document = GetCurrentDoc();
2450 if (handler && document) {
2451 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
2452 if (fm) {
2453 aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
2454 nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(this);
2455 fm->SetFocus(elem, nsIFocusManager::FLAG_BYMOUSE |
2456 nsIFocusManager::FLAG_NOSCROLL);
2457 }
2459 EventStateManager::SetActiveManager(
2460 aVisitor.mPresContext->EventStateManager(), this);
2461 }
2462 }
2463 }
2464 break;
2466 case NS_MOUSE_CLICK: {
2467 WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
2468 if (mouseEvent->IsLeftClickEvent()) {
2469 if (mouseEvent->IsControl() || mouseEvent->IsMeta() ||
2470 mouseEvent->IsAlt() ||mouseEvent->IsShift()) {
2471 break;
2472 }
2474 // The default action is simply to dispatch DOMActivate
2475 nsCOMPtr<nsIPresShell> shell = aVisitor.mPresContext->GetPresShell();
2476 if (shell) {
2477 // single-click
2478 nsEventStatus status = nsEventStatus_eIgnore;
2479 InternalUIEvent actEvent(mouseEvent->mFlags.mIsTrusted, NS_UI_ACTIVATE);
2480 actEvent.detail = 1;
2482 rv = shell->HandleDOMEventWithTarget(this, &actEvent, &status);
2483 if (NS_SUCCEEDED(rv)) {
2484 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
2485 }
2486 }
2487 }
2488 break;
2489 }
2490 case NS_UI_ACTIVATE:
2491 {
2492 if (aVisitor.mEvent->originalTarget == this) {
2493 nsAutoString target;
2494 GetLinkTarget(target);
2495 nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
2496 true, true,
2497 aVisitor.mEvent->mFlags.mIsTrusted);
2498 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
2499 }
2500 }
2501 break;
2503 case NS_KEY_PRESS:
2504 {
2505 WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
2506 if (keyEvent && keyEvent->keyCode == NS_VK_RETURN) {
2507 nsEventStatus status = nsEventStatus_eIgnore;
2508 rv = DispatchClickEvent(aVisitor.mPresContext, keyEvent, this,
2509 false, nullptr, &status);
2510 if (NS_SUCCEEDED(rv)) {
2511 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
2512 }
2513 }
2514 }
2515 break;
2517 default:
2518 // switch not in sync with the optimization switch earlier in this function
2519 NS_NOTREACHED("switch statements not in sync");
2520 return NS_ERROR_UNEXPECTED;
2521 }
2523 return rv;
2524 }
2526 void
2527 Element::GetLinkTarget(nsAString& aTarget)
2528 {
2529 aTarget.Truncate();
2530 }
2532 bool
2533 Element::MozMatchesSelector(const nsAString& aSelector,
2534 ErrorResult& aError)
2535 {
2536 nsCSSSelectorList* selectorList = ParseSelectorList(aSelector, aError);
2537 if (!selectorList) {
2538 // Either we failed (and aError already has the exception), or this
2539 // is a pseudo-element-only selector that matches nothing.
2540 return false;
2541 }
2543 OwnerDoc()->FlushPendingLinkUpdates();
2544 TreeMatchContext matchingContext(false,
2545 nsRuleWalker::eRelevantLinkUnvisited,
2546 OwnerDoc(),
2547 TreeMatchContext::eNeverMatchVisited);
2548 matchingContext.SetHasSpecifiedScope();
2549 matchingContext.AddScopeElement(this);
2550 return nsCSSRuleProcessor::SelectorListMatches(this, matchingContext,
2551 selectorList);
2552 }
2554 static const nsAttrValue::EnumTable kCORSAttributeTable[] = {
2555 // Order matters here
2556 // See ParseCORSValue
2557 { "anonymous", CORS_ANONYMOUS },
2558 { "use-credentials", CORS_USE_CREDENTIALS },
2559 { 0 }
2560 };
2562 /* static */ void
2563 Element::ParseCORSValue(const nsAString& aValue,
2564 nsAttrValue& aResult)
2565 {
2566 DebugOnly<bool> success =
2567 aResult.ParseEnumValue(aValue, kCORSAttributeTable, false,
2568 // default value is anonymous if aValue is
2569 // not a value we understand
2570 &kCORSAttributeTable[0]);
2571 MOZ_ASSERT(success);
2572 }
2574 /* static */ CORSMode
2575 Element::StringToCORSMode(const nsAString& aValue)
2576 {
2577 if (aValue.IsVoid()) {
2578 return CORS_NONE;
2579 }
2581 nsAttrValue val;
2582 Element::ParseCORSValue(aValue, val);
2583 return CORSMode(val.GetEnumValue());
2584 }
2586 /* static */ CORSMode
2587 Element::AttrValueToCORSMode(const nsAttrValue* aValue)
2588 {
2589 if (!aValue) {
2590 return CORS_NONE;
2591 }
2593 return CORSMode(aValue->GetEnumValue());
2594 }
2596 static const char*
2597 GetFullScreenError(nsIDocument* aDoc)
2598 {
2599 // Block fullscreen requests in the chrome document when the fullscreen API
2600 // is configured for content only.
2601 if (nsContentUtils::IsFullscreenApiContentOnly() &&
2602 nsContentUtils::IsChromeDoc(aDoc)) {
2603 return "FullScreenDeniedContentOnly";
2604 }
2606 nsCOMPtr<nsPIDOMWindow> win = aDoc->GetWindow();
2607 if (aDoc->NodePrincipal()->GetAppStatus() >= nsIPrincipal::APP_STATUS_INSTALLED) {
2608 // Request is in a web app and in the same origin as the web app.
2609 // Don't enforce as strict security checks for web apps, the user
2610 // is supposed to have trust in them. However documents cross-origin
2611 // to the web app must still confirm to the normal security checks.
2612 return nullptr;
2613 }
2615 if (!nsContentUtils::IsRequestFullScreenAllowed()) {
2616 return "FullScreenDeniedNotInputDriven";
2617 }
2619 if (nsContentUtils::IsSitePermDeny(aDoc->NodePrincipal(), "fullscreen")) {
2620 return "FullScreenDeniedBlocked";
2621 }
2623 return nullptr;
2624 }
2626 void
2627 Element::MozRequestFullScreen()
2628 {
2629 // Only grant full-screen requests if this is called from inside a trusted
2630 // event handler (i.e. inside an event handler for a user initiated event).
2631 // This stops the full-screen from being abused similar to the popups of old,
2632 // and it also makes it harder for bad guys' script to go full-screen and
2633 // spoof the browser chrome/window and phish logins etc.
2634 // Note that requests for fullscreen inside a web app's origin are exempt
2635 // from this restriction.
2636 const char* error = GetFullScreenError(OwnerDoc());
2637 if (error) {
2638 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2639 NS_LITERAL_CSTRING("DOM"), OwnerDoc(),
2640 nsContentUtils::eDOM_PROPERTIES,
2641 error);
2642 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
2643 new AsyncEventDispatcher(OwnerDoc(),
2644 NS_LITERAL_STRING("mozfullscreenerror"),
2645 true,
2646 false);
2647 asyncDispatcher->PostDOMEvent();
2648 return;
2649 }
2651 OwnerDoc()->AsyncRequestFullScreen(this);
2653 return;
2654 }
2656 void
2657 Element::MozRequestPointerLock()
2658 {
2659 OwnerDoc()->RequestPointerLock(this);
2660 }
2662 NS_IMETHODIMP
2663 Element::GetInnerHTML(nsAString& aInnerHTML)
2664 {
2665 GetMarkup(false, aInnerHTML);
2666 return NS_OK;
2667 }
2669 void
2670 Element::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError)
2671 {
2672 SetInnerHTMLInternal(aInnerHTML, aError);
2673 }
2675 void
2676 Element::GetOuterHTML(nsAString& aOuterHTML)
2677 {
2678 GetMarkup(true, aOuterHTML);
2679 }
2681 void
2682 Element::SetOuterHTML(const nsAString& aOuterHTML, ErrorResult& aError)
2683 {
2684 nsCOMPtr<nsINode> parent = GetParentNode();
2685 if (!parent) {
2686 return;
2687 }
2689 if (parent->NodeType() == nsIDOMNode::DOCUMENT_NODE) {
2690 aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
2691 return;
2692 }
2694 if (OwnerDoc()->IsHTML()) {
2695 nsIAtom* localName;
2696 int32_t namespaceID;
2697 if (parent->IsElement()) {
2698 localName = static_cast<nsIContent*>(parent.get())->Tag();
2699 namespaceID = static_cast<nsIContent*>(parent.get())->GetNameSpaceID();
2700 } else {
2701 NS_ASSERTION(parent->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE,
2702 "How come the parent isn't a document, a fragment or an element?");
2703 localName = nsGkAtoms::body;
2704 namespaceID = kNameSpaceID_XHTML;
2705 }
2706 nsRefPtr<DocumentFragment> fragment =
2707 new DocumentFragment(OwnerDoc()->NodeInfoManager());
2708 nsContentUtils::ParseFragmentHTML(aOuterHTML,
2709 fragment,
2710 localName,
2711 namespaceID,
2712 OwnerDoc()->GetCompatibilityMode() ==
2713 eCompatibility_NavQuirks,
2714 true);
2715 parent->ReplaceChild(*fragment, *this, aError);
2716 return;
2717 }
2719 nsCOMPtr<nsINode> context;
2720 if (parent->IsElement()) {
2721 context = parent;
2722 } else {
2723 NS_ASSERTION(parent->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE,
2724 "How come the parent isn't a document, a fragment or an element?");
2725 nsCOMPtr<nsINodeInfo> info =
2726 OwnerDoc()->NodeInfoManager()->GetNodeInfo(nsGkAtoms::body,
2727 nullptr,
2728 kNameSpaceID_XHTML,
2729 nsIDOMNode::ELEMENT_NODE);
2730 context = NS_NewHTMLBodyElement(info.forget(), FROM_PARSER_FRAGMENT);
2731 }
2733 nsCOMPtr<nsIDOMDocumentFragment> df;
2734 aError = nsContentUtils::CreateContextualFragment(context,
2735 aOuterHTML,
2736 true,
2737 getter_AddRefs(df));
2738 if (aError.Failed()) {
2739 return;
2740 }
2741 nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
2742 parent->ReplaceChild(*fragment, *this, aError);
2743 }
2745 enum nsAdjacentPosition {
2746 eBeforeBegin,
2747 eAfterBegin,
2748 eBeforeEnd,
2749 eAfterEnd
2750 };
2752 void
2753 Element::InsertAdjacentHTML(const nsAString& aPosition, const nsAString& aText,
2754 ErrorResult& aError)
2755 {
2756 nsAdjacentPosition position;
2757 if (aPosition.LowerCaseEqualsLiteral("beforebegin")) {
2758 position = eBeforeBegin;
2759 } else if (aPosition.LowerCaseEqualsLiteral("afterbegin")) {
2760 position = eAfterBegin;
2761 } else if (aPosition.LowerCaseEqualsLiteral("beforeend")) {
2762 position = eBeforeEnd;
2763 } else if (aPosition.LowerCaseEqualsLiteral("afterend")) {
2764 position = eAfterEnd;
2765 } else {
2766 aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
2767 return;
2768 }
2770 nsCOMPtr<nsIContent> destination;
2771 if (position == eBeforeBegin || position == eAfterEnd) {
2772 destination = GetParent();
2773 if (!destination) {
2774 aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
2775 return;
2776 }
2777 } else {
2778 destination = this;
2779 }
2781 nsIDocument* doc = OwnerDoc();
2783 // Needed when insertAdjacentHTML is used in combination with contenteditable
2784 mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
2785 nsAutoScriptLoaderDisabler sld(doc);
2787 // Batch possible DOMSubtreeModified events.
2788 mozAutoSubtreeModified subtree(doc, nullptr);
2790 // Parse directly into destination if possible
2791 if (doc->IsHTML() && !OwnerDoc()->MayHaveDOMMutationObservers() &&
2792 (position == eBeforeEnd ||
2793 (position == eAfterEnd && !GetNextSibling()) ||
2794 (position == eAfterBegin && !GetFirstChild()))) {
2795 int32_t oldChildCount = destination->GetChildCount();
2796 int32_t contextNs = destination->GetNameSpaceID();
2797 nsIAtom* contextLocal = destination->Tag();
2798 if (contextLocal == nsGkAtoms::html && contextNs == kNameSpaceID_XHTML) {
2799 // For compat with IE6 through IE9. Willful violation of HTML5 as of
2800 // 2011-04-06. CreateContextualFragment does the same already.
2801 // Spec bug: http://www.w3.org/Bugs/Public/show_bug.cgi?id=12434
2802 contextLocal = nsGkAtoms::body;
2803 }
2804 aError = nsContentUtils::ParseFragmentHTML(aText,
2805 destination,
2806 contextLocal,
2807 contextNs,
2808 doc->GetCompatibilityMode() ==
2809 eCompatibility_NavQuirks,
2810 true);
2811 // HTML5 parser has notified, but not fired mutation events.
2812 nsContentUtils::FireMutationEventsForDirectParsing(doc, destination,
2813 oldChildCount);
2814 return;
2815 }
2817 // couldn't parse directly
2818 nsCOMPtr<nsIDOMDocumentFragment> df;
2819 aError = nsContentUtils::CreateContextualFragment(destination,
2820 aText,
2821 true,
2822 getter_AddRefs(df));
2823 if (aError.Failed()) {
2824 return;
2825 }
2827 nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
2829 // Suppress assertion about node removal mutation events that can't have
2830 // listeners anyway, because no one has had the chance to register mutation
2831 // listeners on the fragment that comes from the parser.
2832 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
2834 nsAutoMutationBatch mb(destination, true, false);
2835 switch (position) {
2836 case eBeforeBegin:
2837 destination->InsertBefore(*fragment, this, aError);
2838 break;
2839 case eAfterBegin:
2840 static_cast<nsINode*>(this)->InsertBefore(*fragment, GetFirstChild(),
2841 aError);
2842 break;
2843 case eBeforeEnd:
2844 static_cast<nsINode*>(this)->AppendChild(*fragment, aError);
2845 break;
2846 case eAfterEnd:
2847 destination->InsertBefore(*fragment, GetNextSibling(), aError);
2848 break;
2849 }
2850 }
2852 nsIEditor*
2853 Element::GetEditorInternal()
2854 {
2855 nsCOMPtr<nsITextControlElement> textCtrl = do_QueryInterface(this);
2856 return textCtrl ? textCtrl->GetTextEditor() : nullptr;
2857 }
2859 nsresult
2860 Element::SetBoolAttr(nsIAtom* aAttr, bool aValue)
2861 {
2862 if (aValue) {
2863 return SetAttr(kNameSpaceID_None, aAttr, EmptyString(), true);
2864 }
2866 return UnsetAttr(kNameSpaceID_None, aAttr, true);
2867 }
2869 Directionality
2870 Element::GetComputedDirectionality() const
2871 {
2872 nsIFrame* frame = GetPrimaryFrame();
2873 if (frame) {
2874 return frame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR
2875 ? eDir_LTR : eDir_RTL;
2876 }
2878 return GetDirectionality();
2879 }
2881 float
2882 Element::FontSizeInflation()
2883 {
2884 nsIFrame* frame = GetPrimaryFrame();
2885 if (!frame) {
2886 return -1.0;
2887 }
2889 if (nsLayoutUtils::FontSizeInflationEnabled(frame->PresContext())) {
2890 return nsLayoutUtils::FontSizeInflationFor(frame);
2891 }
2893 return 1.0;
2894 }