Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/Element.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
6
7
/*
8
 * Base class for all element classes; this provides an implementation
9
 * of DOM Core's Element, implements nsIContent, provides
10
 * utility methods for subclasses, and so forth.
11
 */
12
13
#include "mozilla/dom/ElementInlines.h"
14
15
#include "AnimationCommon.h"
16
#include "mozilla/DebugOnly.h"
17
#include "mozilla/StaticPrefs.h"
18
#include "mozilla/dom/Animation.h"
19
#include "mozilla/dom/Attr.h"
20
#include "mozilla/dom/Flex.h"
21
#include "mozilla/dom/Grid.h"
22
#include "mozilla/gfx/Matrix.h"
23
#include "nsAtom.h"
24
#include "nsCSSFrameConstructor.h"
25
#include "nsDOMAttributeMap.h"
26
#include "nsIContentInlines.h"
27
#include "mozilla/dom/NodeInfo.h"
28
#include "nsIDocumentInlines.h"
29
#include "mozilla/dom/DocumentTimeline.h"
30
#include "nsIContentIterator.h"
31
#include "nsFlexContainerFrame.h"
32
#include "nsFocusManager.h"
33
#include "nsILinkHandler.h"
34
#include "nsIScriptGlobalObject.h"
35
#include "nsIURL.h"
36
#include "nsContainerFrame.h"
37
#include "nsIAnonymousContentCreator.h"
38
#include "nsIPresShell.h"
39
#include "nsPresContext.h"
40
#include "nsStyleConsts.h"
41
#include "nsString.h"
42
#include "nsUnicharUtils.h"
43
#include "nsDOMCID.h"
44
#include "nsIServiceManager.h"
45
#include "nsDOMCSSAttrDeclaration.h"
46
#include "nsNameSpaceManager.h"
47
#include "nsContentList.h"
48
#include "nsVariant.h"
49
#include "nsDOMTokenList.h"
50
#include "nsXBLPrototypeBinding.h"
51
#include "nsError.h"
52
#include "nsDOMString.h"
53
#include "nsIScriptSecurityManager.h"
54
#include "mozilla/dom/AnimatableBinding.h"
55
#include "mozilla/dom/HTMLDivElement.h"
56
#include "mozilla/dom/HTMLSpanElement.h"
57
#include "mozilla/dom/KeyframeAnimationOptionsBinding.h"
58
#include "mozilla/dom/MutationEventBinding.h"
59
#include "mozilla/AnimationComparator.h"
60
#include "mozilla/AsyncEventDispatcher.h"
61
#include "mozilla/ContentEvents.h"
62
#include "mozilla/DeclarationBlock.h"
63
#include "mozilla/EffectSet.h"
64
#include "mozilla/EventDispatcher.h"
65
#include "mozilla/EventListenerManager.h"
66
#include "mozilla/EventStateManager.h"
67
#include "mozilla/EventStates.h"
68
#include "mozilla/FullscreenChange.h"
69
#include "mozilla/InternalMutationEvent.h"
70
#include "mozilla/MouseEvents.h"
71
#include "mozilla/RestyleManager.h"
72
#include "mozilla/SizeOfState.h"
73
#include "mozilla/TextEditor.h"
74
#include "mozilla/TextEvents.h"
75
#include "nsNodeUtils.h"
76
#include "mozilla/dom/DirectionalityUtils.h"
77
#include "nsDocument.h"
78
#include "nsAttrValueOrString.h"
79
#include "nsAttrValueInlines.h"
80
#include "nsCSSPseudoElements.h"
81
#include "nsWindowSizes.h"
82
#ifdef MOZ_XUL
83
#include "nsXULElement.h"
84
#endif /* MOZ_XUL */
85
#include "nsSVGElement.h"
86
#include "nsFrameSelection.h"
87
#ifdef DEBUG
88
#include "nsRange.h"
89
#endif
90
91
#include "nsBindingManager.h"
92
#include "nsXBLBinding.h"
93
#include "nsPIDOMWindow.h"
94
#include "nsPIBoxObject.h"
95
#include "mozilla/dom/DOMRect.h"
96
#include "nsSVGUtils.h"
97
#include "nsLayoutUtils.h"
98
#include "nsGkAtoms.h"
99
#include "nsContentUtils.h"
100
#include "ChildIterator.h"
101
102
#include "nsIDOMEventListener.h"
103
#include "nsIWebNavigation.h"
104
#include "nsIBaseWindow.h"
105
#include "nsIWidget.h"
106
107
#include "nsNodeInfoManager.h"
108
#include "nsICategoryManager.h"
109
#include "nsGenericHTMLElement.h"
110
#include "nsContentCreatorFunctions.h"
111
#include "nsIControllers.h"
112
#include "nsView.h"
113
#include "nsViewManager.h"
114
#include "nsIScrollableFrame.h"
115
#include "nsTextNode.h"
116
117
#include "nsCycleCollectionParticipant.h"
118
#include "nsCCUncollectableMarker.h"
119
120
#include "mozAutoDocUpdate.h"
121
122
#include "nsDOMMutationObserver.h"
123
#include "nsWrapperCacheInlines.h"
124
#include "xpcpublic.h"
125
#include "nsIScriptError.h"
126
#include "mozilla/Telemetry.h"
127
128
#include "mozilla/CORSMode.h"
129
#include "mozilla/dom/ShadowRoot.h"
130
#include "mozilla/dom/NodeListBinding.h"
131
132
#include "nsStyledElement.h"
133
#include "nsXBLService.h"
134
#include "nsITextControlElement.h"
135
#include "nsITextControlFrame.h"
136
#include "nsISupportsImpl.h"
137
#include "mozilla/dom/CSSPseudoElement.h"
138
#include "mozilla/dom/DocumentFragment.h"
139
#include "mozilla/dom/ElementBinding.h"
140
#include "mozilla/dom/KeyframeEffectBinding.h"
141
#include "mozilla/dom/KeyframeEffect.h"
142
#include "mozilla/dom/MouseEventBinding.h"
143
#include "mozilla/dom/WindowBinding.h"
144
#include "mozilla/dom/VRDisplay.h"
145
#include "mozilla/IntegerPrintfMacros.h"
146
#include "mozilla/Preferences.h"
147
#include "nsComputedDOMStyle.h"
148
#include "nsDOMStringMap.h"
149
#include "DOMIntersectionObserver.h"
150
151
#include "nsISpeculativeConnect.h"
152
#include "nsIIOService.h"
153
154
#include "DOMMatrix.h"
155
156
using namespace mozilla;
157
using namespace mozilla::dom;
158
159
using mozilla::gfx::Matrix4x4;
160
161
//
162
// Verify sizes of elements on 64-bit platforms. This should catch most memory
163
// regressions, and is easy to verify locally since most developers are on
164
// 64-bit machines. We use a template rather than a direct static assert so
165
// that the error message actually displays the sizes.
166
//
167
168
// We need different numbers on certain build types to deal with the owning
169
// thread pointer that comes with the non-threadsafe refcount on
170
// FragmentOrElement.
171
#ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
172
#define EXTRA_DOM_ELEMENT_BYTES 8
173
#else
174
#define EXTRA_DOM_ELEMENT_BYTES 0
175
#endif
176
177
#define ASSERT_ELEMENT_SIZE(type, opt_size) \
178
template<int a, int b> struct Check##type##Size \
179
{ \
180
  static_assert(sizeof(void*) != 8 || a == b, "DOM size changed"); \
181
}; \
182
Check##type##Size<sizeof(type), opt_size + EXTRA_DOM_ELEMENT_BYTES> g##type##CES;
183
184
// Note that mozjemalloc uses a 16 byte quantum, so 128 is a bin/bucket size.
185
ASSERT_ELEMENT_SIZE(Element, 128);
186
ASSERT_ELEMENT_SIZE(HTMLDivElement, 128);
187
ASSERT_ELEMENT_SIZE(HTMLSpanElement, 128);
188
189
#undef ASSERT_ELEMENT_SIZE
190
#undef EXTRA_DOM_ELEMENT_BYTES
191
192
nsAtom*
193
nsIContent::DoGetID() const
194
0
{
195
0
  MOZ_ASSERT(HasID(), "Unexpected call");
196
0
  MOZ_ASSERT(IsElement(), "Only elements can have IDs");
197
0
198
0
  return AsElement()->GetParsedAttr(nsGkAtoms::id)->GetAtomValue();
199
0
}
200
201
const nsAttrValue*
202
Element::GetSVGAnimatedClass() const
203
0
{
204
0
  MOZ_ASSERT(MayHaveClass() && IsSVGElement(), "Unexpected call");
205
0
  return static_cast<const nsSVGElement*>(this)->GetAnimatedClassName();
206
0
}
207
208
NS_IMETHODIMP
209
Element::QueryInterface(REFNSIID aIID, void** aInstancePtr)
210
0
{
211
0
  if (aIID.Equals(NS_GET_IID(Element))) {
212
0
    NS_ADDREF_THIS();
213
0
    *aInstancePtr = this;
214
0
    return NS_OK;
215
0
  }
216
0
217
0
  NS_ASSERTION(aInstancePtr,
218
0
               "QueryInterface requires a non-NULL destination!");
219
0
  nsresult rv = FragmentOrElement::QueryInterface(aIID, aInstancePtr);
220
0
  if (NS_SUCCEEDED(rv)) {
221
0
    return NS_OK;
222
0
  }
223
0
224
0
  // Give the binding manager a chance to get an interface for this element.
225
0
  return OwnerDoc()->BindingManager()->GetBindingImplementation(this, aIID,
226
0
                                                                aInstancePtr);
227
0
}
228
229
EventStates
230
Element::IntrinsicState() const
231
0
{
232
0
  return IsEditable() ? NS_EVENT_STATE_MOZ_READWRITE :
233
0
                        NS_EVENT_STATE_MOZ_READONLY;
234
0
}
235
236
void
237
Element::NotifyStateChange(EventStates aStates)
238
0
{
239
0
  nsIDocument* doc = GetComposedDoc();
240
0
  if (doc) {
241
0
    nsAutoScriptBlocker scriptBlocker;
242
0
    doc->ContentStateChanged(this, aStates);
243
0
  }
244
0
}
245
246
void
247
Element::UpdateLinkState(EventStates aState)
248
0
{
249
0
  MOZ_ASSERT(!aState.HasAtLeastOneOfStates(~(NS_EVENT_STATE_VISITED |
250
0
                                             NS_EVENT_STATE_UNVISITED)),
251
0
             "Unexpected link state bits");
252
0
  mState =
253
0
    (mState & ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) |
254
0
    aState;
255
0
}
256
257
void
258
Element::UpdateState(bool aNotify)
259
0
{
260
0
  EventStates oldState = mState;
261
0
  mState = IntrinsicState() | (oldState & EXTERNALLY_MANAGED_STATES);
262
0
  if (aNotify) {
263
0
    EventStates changedStates = oldState ^ mState;
264
0
    if (!changedStates.IsEmpty()) {
265
0
      nsIDocument* doc = GetComposedDoc();
266
0
      if (doc) {
267
0
        nsAutoScriptBlocker scriptBlocker;
268
0
        doc->ContentStateChanged(this, changedStates);
269
0
      }
270
0
    }
271
0
  }
272
0
}
273
274
void
275
nsIContent::UpdateEditableState(bool aNotify)
276
0
{
277
0
  // Guaranteed to be non-element content
278
0
  NS_ASSERTION(!IsElement(), "What happened here?");
279
0
  nsIContent *parent = GetParent();
280
0
281
0
  // Skip over unknown native anonymous content to avoid setting a flag we
282
0
  // can't clear later
283
0
  bool isUnknownNativeAnon = false;
284
0
  if (IsInNativeAnonymousSubtree()) {
285
0
    isUnknownNativeAnon = true;
286
0
    nsCOMPtr<nsIContent> root = this;
287
0
    while (root && !root->IsRootOfNativeAnonymousSubtree()) {
288
0
      root = root->GetParent();
289
0
    }
290
0
    // root should always be true here, but isn't -- bug 999416
291
0
    if (root) {
292
0
      nsIFrame* rootFrame = root->GetPrimaryFrame();
293
0
      if (rootFrame) {
294
0
        nsContainerFrame* parentFrame = rootFrame->GetParent();
295
0
        nsITextControlFrame* textCtrl = do_QueryFrame(parentFrame);
296
0
        isUnknownNativeAnon = !textCtrl;
297
0
      }
298
0
    }
299
0
  }
300
0
301
0
  SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE) &&
302
0
                  !isUnknownNativeAnon);
303
0
}
304
305
void
306
Element::UpdateEditableState(bool aNotify)
307
0
{
308
0
  nsIContent *parent = GetParent();
309
0
310
0
  SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE));
311
0
  if (aNotify) {
312
0
    UpdateState(aNotify);
313
0
  } else {
314
0
    // Avoid calling UpdateState in this very common case, because
315
0
    // this gets called for pretty much every single element on
316
0
    // insertion into the document and UpdateState can be slow for
317
0
    // some kinds of elements even when not notifying.
318
0
    if (IsEditable()) {
319
0
      RemoveStatesSilently(NS_EVENT_STATE_MOZ_READONLY);
320
0
      AddStatesSilently(NS_EVENT_STATE_MOZ_READWRITE);
321
0
    } else {
322
0
      RemoveStatesSilently(NS_EVENT_STATE_MOZ_READWRITE);
323
0
      AddStatesSilently(NS_EVENT_STATE_MOZ_READONLY);
324
0
    }
325
0
  }
326
0
}
327
328
int32_t
329
Element::TabIndex()
330
0
{
331
0
  const nsAttrValue* attrVal = mAttrs.GetAttr(nsGkAtoms::tabindex);
332
0
  if (attrVal && attrVal->Type() == nsAttrValue::eInteger) {
333
0
    return attrVal->GetIntegerValue();
334
0
  }
335
0
336
0
  return TabIndexDefault();
337
0
}
338
339
void
340
Element::Focus(mozilla::ErrorResult& aError)
341
0
{
342
0
  nsFocusManager* fm = nsFocusManager::GetFocusManager();
343
0
  // Also other browsers seem to have the hack to not re-focus (and flush) when
344
0
  // the element is already focused.
345
0
  if (fm) {
346
0
    if (fm->CanSkipFocus(this)) {
347
0
      fm->NeedsFlushBeforeEventHandling(this);
348
0
    } else {
349
0
      aError = fm->SetFocus(this, 0);
350
0
    }
351
0
  }
352
0
}
353
354
void
355
Element::SetTabIndex(int32_t aTabIndex, mozilla::ErrorResult& aError)
356
0
{
357
0
  nsAutoString value;
358
0
  value.AppendInt(aTabIndex);
359
0
360
0
  SetAttr(nsGkAtoms::tabindex, value, aError);
361
0
}
362
363
void
364
Element::SetXBLBinding(nsXBLBinding* aBinding,
365
                       nsBindingManager* aOldBindingManager)
366
0
{
367
0
  nsBindingManager* bindingManager;
368
0
  if (aOldBindingManager) {
369
0
    MOZ_ASSERT(!aBinding, "aOldBindingManager should only be provided "
370
0
                          "when removing a binding.");
371
0
    bindingManager = aOldBindingManager;
372
0
  } else {
373
0
    bindingManager = OwnerDoc()->BindingManager();
374
0
  }
375
0
376
0
  // After this point, aBinding will be the most-derived binding for aContent.
377
0
  // If we already have a binding for aContent, make sure to
378
0
  // remove it from the attached stack.  Otherwise we might end up firing its
379
0
  // constructor twice (if aBinding inherits from it) or firing its constructor
380
0
  // after aContent has been deleted (if aBinding is null and the content node
381
0
  // dies before we process mAttachedStack).
382
0
  RefPtr<nsXBLBinding> oldBinding = GetXBLBinding();
383
0
  if (oldBinding) {
384
0
    bindingManager->RemoveFromAttachedQueue(oldBinding);
385
0
  }
386
0
387
0
  if (aBinding) {
388
0
    SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
389
0
    nsExtendedDOMSlots* slots = ExtendedDOMSlots();
390
0
    slots->mXBLBinding = aBinding;
391
0
    bindingManager->AddBoundContent(this);
392
0
  } else {
393
0
    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
394
0
    if (slots) {
395
0
      slots->mXBLBinding = nullptr;
396
0
    }
397
0
    bindingManager->RemoveBoundContent(this);
398
0
    if (oldBinding) {
399
0
      oldBinding->SetBoundElement(nullptr);
400
0
    }
401
0
  }
402
0
}
403
404
void
405
Element::SetShadowRoot(ShadowRoot* aShadowRoot)
406
0
{
407
0
  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
408
0
  slots->mShadowRoot = aShadowRoot;
409
0
}
410
411
void
412
Element::Blur(mozilla::ErrorResult& aError)
413
0
{
414
0
  if (!ShouldBlur(this)) {
415
0
    return;
416
0
  }
417
0
418
0
  nsIDocument* doc = GetComposedDoc();
419
0
  if (!doc) {
420
0
    return;
421
0
  }
422
0
423
0
  nsPIDOMWindowOuter* win = doc->GetWindow();
424
0
  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
425
0
  if (win && fm) {
426
0
    aError = fm->ClearFocus(win);
427
0
  }
428
0
}
429
430
EventStates
431
Element::StyleStateFromLocks() const
432
0
{
433
0
  StyleStateLocks locksAndValues = LockedStyleStates();
434
0
  EventStates locks = locksAndValues.mLocks;
435
0
  EventStates values = locksAndValues.mValues;
436
0
  EventStates state = (mState & ~locks) | (locks & values);
437
0
438
0
  if (state.HasState(NS_EVENT_STATE_VISITED)) {
439
0
    return state & ~NS_EVENT_STATE_UNVISITED;
440
0
  }
441
0
  if (state.HasState(NS_EVENT_STATE_UNVISITED)) {
442
0
    return state & ~NS_EVENT_STATE_VISITED;
443
0
  }
444
0
445
0
  return state;
446
0
}
447
448
Element::StyleStateLocks
449
Element::LockedStyleStates() const
450
0
{
451
0
  StyleStateLocks* locks =
452
0
    static_cast<StyleStateLocks*>(GetProperty(nsGkAtoms::lockedStyleStates));
453
0
  if (locks) {
454
0
    return *locks;
455
0
  }
456
0
  return StyleStateLocks();
457
0
}
458
459
void
460
Element::NotifyStyleStateChange(EventStates aStates)
461
0
{
462
0
  nsIDocument* doc = GetComposedDoc();
463
0
  if (doc) {
464
0
    nsIPresShell *presShell = doc->GetShell();
465
0
    if (presShell) {
466
0
      nsAutoScriptBlocker scriptBlocker;
467
0
      presShell->ContentStateChanged(doc, this, aStates);
468
0
    }
469
0
  }
470
0
}
471
472
void
473
Element::LockStyleStates(EventStates aStates, bool aEnabled)
474
0
{
475
0
  StyleStateLocks* locks = new StyleStateLocks(LockedStyleStates());
476
0
477
0
  locks->mLocks |= aStates;
478
0
  if (aEnabled) {
479
0
    locks->mValues |= aStates;
480
0
  } else {
481
0
    locks->mValues &= ~aStates;
482
0
  }
483
0
484
0
  if (aStates.HasState(NS_EVENT_STATE_VISITED)) {
485
0
    locks->mLocks &= ~NS_EVENT_STATE_UNVISITED;
486
0
  }
487
0
  if (aStates.HasState(NS_EVENT_STATE_UNVISITED)) {
488
0
    locks->mLocks &= ~NS_EVENT_STATE_VISITED;
489
0
  }
490
0
491
0
  SetProperty(nsGkAtoms::lockedStyleStates, locks,
492
0
              nsINode::DeleteProperty<StyleStateLocks>);
493
0
  SetHasLockedStyleStates();
494
0
495
0
  NotifyStyleStateChange(aStates);
496
0
}
497
498
void
499
Element::UnlockStyleStates(EventStates aStates)
500
0
{
501
0
  StyleStateLocks* locks = new StyleStateLocks(LockedStyleStates());
502
0
503
0
  locks->mLocks &= ~aStates;
504
0
505
0
  if (locks->mLocks.IsEmpty()) {
506
0
    DeleteProperty(nsGkAtoms::lockedStyleStates);
507
0
    ClearHasLockedStyleStates();
508
0
    delete locks;
509
0
  }
510
0
  else {
511
0
    SetProperty(nsGkAtoms::lockedStyleStates, locks,
512
0
                nsINode::DeleteProperty<StyleStateLocks>);
513
0
  }
514
0
515
0
  NotifyStyleStateChange(aStates);
516
0
}
517
518
void
519
Element::ClearStyleStateLocks()
520
0
{
521
0
  StyleStateLocks locks = LockedStyleStates();
522
0
523
0
  DeleteProperty(nsGkAtoms::lockedStyleStates);
524
0
  ClearHasLockedStyleStates();
525
0
526
0
  NotifyStyleStateChange(locks.mLocks);
527
0
}
528
529
static bool
530
IsLikelyCustomElement(const nsXULElement& aElement)
531
0
{
532
0
  const CustomElementData* data = aElement.GetCustomElementData();
533
0
  if (!data) {
534
0
    return false;
535
0
  }
536
0
537
0
  const CustomElementRegistry* registry =
538
0
    nsContentUtils::GetCustomElementRegistry(aElement.OwnerDoc());
539
0
  if (!registry) {
540
0
    return false;
541
0
  }
542
0
543
0
  return registry->IsLikelyToBeCustomElement(data->GetCustomElementType());
544
0
}
545
546
static bool
547
MayNeedToLoadXBLBinding(const nsIDocument& aDocument, const Element& aElement)
548
0
{
549
0
  // If we have a frame, the frame has already loaded the binding.
550
0
  // Otherwise, don't do anything else here unless we're dealing with
551
0
  // XUL or an HTML element that may have a plugin-related overlay
552
0
  // (i.e. object or embed).
553
0
  if (!aDocument.GetShell() || aElement.GetPrimaryFrame()) {
554
0
    return false;
555
0
  }
556
0
557
0
  if (auto* xulElem = nsXULElement::FromNode(aElement)) {
558
0
    return !IsLikelyCustomElement(*xulElem);
559
0
  }
560
0
561
0
  return aElement.IsAnyOfHTMLElements(nsGkAtoms::object, nsGkAtoms::embed);
562
0
}
563
564
bool
565
Element::GetBindingURL(nsIDocument *aDocument, css::URLValue **aResult)
566
0
{
567
0
  if (!MayNeedToLoadXBLBinding(*aDocument, *this)) {
568
0
    *aResult = nullptr;
569
0
    return true;
570
0
  }
571
0
572
0
  // Get the computed -moz-binding directly from the ComputedStyle
573
0
  RefPtr<ComputedStyle> sc =
574
0
    nsComputedDOMStyle::GetComputedStyleNoFlush(this, nullptr);
575
0
  NS_ENSURE_TRUE(sc, false);
576
0
577
0
  NS_IF_ADDREF(*aResult = sc->StyleDisplay()->mBinding);
578
0
  return true;
579
0
}
580
581
JSObject*
582
Element::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
583
0
{
584
0
  JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx, aGivenProto));
585
0
  if (!obj) {
586
0
    return nullptr;
587
0
  }
588
0
589
0
  nsIDocument* doc = GetComposedDoc();
590
0
  if (!doc) {
591
0
    // There's no baseclass that cares about this call so we just
592
0
    // return here.
593
0
    return obj;
594
0
  }
595
0
596
0
  // We must ensure that the XBL Binding is installed before we hand
597
0
  // back this object.
598
0
599
0
  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) && GetXBLBinding()) {
600
0
    // There's already a binding for this element so nothing left to
601
0
    // be done here.
602
0
603
0
    // In theory we could call ExecuteAttachedHandler here when it's safe to
604
0
    // run script if we also removed the binding from the PAQ queue, but that
605
0
    // seems like a scary change that would mosly just add more
606
0
    // inconsistencies.
607
0
    return obj;
608
0
  }
609
0
610
0
  // Make sure the ComputedStyle goes away _before_ we load the binding
611
0
  // since that can destroy the relevant presshell.
612
0
613
0
  {
614
0
    // Make a scope so that ~nsRefPtr can GC before returning obj.
615
0
    RefPtr<css::URLValue> bindingURL;
616
0
    bool ok = GetBindingURL(doc, getter_AddRefs(bindingURL));
617
0
    if (!ok) {
618
0
      dom::Throw(aCx, NS_ERROR_FAILURE);
619
0
      return nullptr;
620
0
    }
621
0
622
0
    if (bindingURL) {
623
0
      nsCOMPtr<nsIURI> uri = bindingURL->GetURI();
624
0
      nsCOMPtr<nsIPrincipal> principal = bindingURL->mExtraData->GetPrincipal();
625
0
626
0
      // We have a binding that must be installed.
627
0
      bool dummy;
628
0
629
0
      nsXBLService* xblService = nsXBLService::GetInstance();
630
0
      if (!xblService) {
631
0
        dom::Throw(aCx, NS_ERROR_NOT_AVAILABLE);
632
0
        return nullptr;
633
0
      }
634
0
635
0
      RefPtr<nsXBLBinding> binding;
636
0
      xblService->LoadBindings(this, uri, principal, getter_AddRefs(binding),
637
0
                               &dummy);
638
0
639
0
      if (binding) {
640
0
        if (nsContentUtils::IsSafeToRunScript()) {
641
0
          binding->ExecuteAttachedHandler();
642
0
        } else {
643
0
          nsContentUtils::AddScriptRunner(
644
0
            NewRunnableMethod("nsXBLBinding::ExecuteAttachedHandler",
645
0
                              binding,
646
0
                              &nsXBLBinding::ExecuteAttachedHandler));
647
0
        }
648
0
      }
649
0
    }
650
0
  }
651
0
652
0
  return obj;
653
0
}
654
655
/* virtual */
656
nsINode*
657
Element::GetScopeChainParent() const
658
0
{
659
0
  return OwnerDoc();
660
0
}
661
662
nsDOMTokenList*
663
Element::ClassList()
664
0
{
665
0
  Element::nsDOMSlots* slots = DOMSlots();
666
0
667
0
  if (!slots->mClassList) {
668
0
    slots->mClassList = new nsDOMTokenList(this, nsGkAtoms::_class);
669
0
  }
670
0
671
0
  return slots->mClassList;
672
0
}
673
674
void
675
Element::GetAttributeNames(nsTArray<nsString>& aResult)
676
0
{
677
0
  uint32_t count = mAttrs.AttrCount();
678
0
  for (uint32_t i = 0; i < count; ++i) {
679
0
    const nsAttrName* name = mAttrs.AttrNameAt(i);
680
0
    name->GetQualifiedName(*aResult.AppendElement());
681
0
  }
682
0
}
683
684
already_AddRefed<nsIHTMLCollection>
685
Element::GetElementsByTagName(const nsAString& aLocalName)
686
0
{
687
0
  return NS_GetContentList(this, kNameSpaceID_Unknown, aLocalName);
688
0
}
689
690
nsIScrollableFrame*
691
Element::GetScrollFrame(nsIFrame **aFrame, FlushType aFlushType)
692
0
{
693
0
  // it isn't clear what to return for SVG nodes, so just return nothing
694
0
  if (IsSVGElement()) {
695
0
    if (aFrame) {
696
0
      *aFrame = nullptr;
697
0
    }
698
0
    return nullptr;
699
0
  }
700
0
701
0
  nsIFrame* frame = GetPrimaryFrame(aFlushType);
702
0
  if (aFrame) {
703
0
    *aFrame = frame;
704
0
  }
705
0
  if (frame) {
706
0
    // menu frames implement GetScrollTargetFrame but we don't want
707
0
    // to use it here.  Similar for comboboxes.
708
0
    LayoutFrameType type = frame->Type();
709
0
    if (type != LayoutFrameType::Menu &&
710
0
        type != LayoutFrameType::ComboboxControl) {
711
0
      nsIScrollableFrame *scrollFrame = frame->GetScrollTargetFrame();
712
0
      if (scrollFrame) {
713
0
        MOZ_ASSERT(!OwnerDoc()->IsScrollingElement(this),
714
0
                   "How can we have a scrollframe if we're the "
715
0
                   "scrollingElement for our document?");
716
0
        return scrollFrame;
717
0
      }
718
0
    }
719
0
  }
720
0
721
0
  nsIDocument* doc = OwnerDoc();
722
0
  // Note: This IsScrollingElement() call can flush frames, if we're the body of
723
0
  // a quirks mode document.
724
0
  bool isScrollingElement = OwnerDoc()->IsScrollingElement(this);
725
0
  // Now reget *aStyledFrame if the caller asked for it, because that frame
726
0
  // flush can kill it.
727
0
  if (aFrame) {
728
0
    *aFrame = GetPrimaryFrame(FlushType::None);
729
0
  }
730
0
731
0
  if (isScrollingElement) {
732
0
    // Our scroll info should map to the root scrollable frame if there is one.
733
0
    if (nsIPresShell* shell = doc->GetShell()) {
734
0
      return shell->GetRootScrollFrameAsScrollable();
735
0
    }
736
0
  }
737
0
738
0
  return nullptr;
739
0
}
740
741
void
742
Element::ScrollIntoView(const BooleanOrScrollIntoViewOptions& aObject)
743
0
{
744
0
  if (aObject.IsScrollIntoViewOptions()) {
745
0
    return ScrollIntoView(aObject.GetAsScrollIntoViewOptions());
746
0
  }
747
0
748
0
  MOZ_DIAGNOSTIC_ASSERT(aObject.IsBoolean());
749
0
750
0
  ScrollIntoViewOptions options;
751
0
  if (aObject.GetAsBoolean()) {
752
0
    options.mBlock = ScrollLogicalPosition::Start;
753
0
    options.mInline = ScrollLogicalPosition::Nearest;
754
0
  } else {
755
0
    options.mBlock = ScrollLogicalPosition::End;
756
0
    options.mInline = ScrollLogicalPosition::Nearest;
757
0
  }
758
0
  return ScrollIntoView(options);
759
0
}
760
761
void
762
Element::ScrollIntoView(const ScrollIntoViewOptions &aOptions)
763
0
{
764
0
  nsIDocument *document = GetComposedDoc();
765
0
  if (!document) {
766
0
    return;
767
0
  }
768
0
769
0
  // Get the presentation shell
770
0
  nsCOMPtr<nsIPresShell> presShell = document->GetShell();
771
0
  if (!presShell) {
772
0
    return;
773
0
  }
774
0
775
0
  int16_t vpercent = nsIPresShell::SCROLL_CENTER;
776
0
  switch (aOptions.mBlock) {
777
0
    case ScrollLogicalPosition::Start:
778
0
      vpercent = nsIPresShell::SCROLL_TOP;
779
0
      break;
780
0
    case ScrollLogicalPosition::Center:
781
0
      vpercent = nsIPresShell::SCROLL_CENTER;
782
0
      break;
783
0
    case ScrollLogicalPosition::End:
784
0
      vpercent = nsIPresShell::SCROLL_BOTTOM;
785
0
      break;
786
0
    case ScrollLogicalPosition::Nearest:
787
0
      vpercent = nsIPresShell::SCROLL_MINIMUM;
788
0
      break;
789
0
    default:
790
0
      MOZ_ASSERT_UNREACHABLE("Unexpected ScrollLogicalPosition value");
791
0
  }
792
0
793
0
  int16_t hpercent = nsIPresShell::SCROLL_CENTER;
794
0
  switch (aOptions.mInline) {
795
0
    case ScrollLogicalPosition::Start:
796
0
      hpercent = nsIPresShell::SCROLL_LEFT;
797
0
      break;
798
0
    case ScrollLogicalPosition::Center:
799
0
      hpercent = nsIPresShell::SCROLL_CENTER;
800
0
      break;
801
0
    case ScrollLogicalPosition::End:
802
0
      hpercent = nsIPresShell::SCROLL_RIGHT;
803
0
      break;
804
0
    case ScrollLogicalPosition::Nearest:
805
0
      hpercent = nsIPresShell::SCROLL_MINIMUM;
806
0
      break;
807
0
    default:
808
0
      MOZ_ASSERT_UNREACHABLE("Unexpected ScrollLogicalPosition value");
809
0
  }
810
0
811
0
  uint32_t flags = nsIPresShell::SCROLL_OVERFLOW_HIDDEN;
812
0
  if (aOptions.mBehavior == ScrollBehavior::Smooth) {
813
0
    flags |= nsIPresShell::SCROLL_SMOOTH;
814
0
  } else if (aOptions.mBehavior == ScrollBehavior::Auto) {
815
0
    flags |= nsIPresShell::SCROLL_SMOOTH_AUTO;
816
0
  }
817
0
818
0
  presShell->ScrollContentIntoView(this,
819
0
                                   nsIPresShell::ScrollAxis(
820
0
                                     vpercent,
821
0
                                     nsIPresShell::SCROLL_ALWAYS),
822
0
                                   nsIPresShell::ScrollAxis(
823
0
                                     hpercent,
824
0
                                     nsIPresShell::SCROLL_ALWAYS),
825
0
                                   flags);
826
0
}
827
828
void
829
Element::Scroll(const CSSIntPoint& aScroll, const ScrollOptions& aOptions)
830
0
{
831
0
  nsIScrollableFrame* sf = GetScrollFrame();
832
0
  if (sf) {
833
0
    nsIScrollableFrame::ScrollMode scrollMode = nsIScrollableFrame::INSTANT;
834
0
    if (aOptions.mBehavior == ScrollBehavior::Smooth) {
835
0
      scrollMode = nsIScrollableFrame::SMOOTH_MSD;
836
0
    } else if (aOptions.mBehavior == ScrollBehavior::Auto) {
837
0
      ScrollStyles styles = sf->GetScrollStyles();
838
0
      if (styles.mScrollBehavior == NS_STYLE_SCROLL_BEHAVIOR_SMOOTH) {
839
0
        scrollMode = nsIScrollableFrame::SMOOTH_MSD;
840
0
      }
841
0
    }
842
0
843
0
    sf->ScrollToCSSPixels(aScroll, scrollMode);
844
0
  }
845
0
}
846
847
void
848
Element::Scroll(double aXScroll, double aYScroll)
849
0
{
850
0
  // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
851
0
  auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
852
0
                                         mozilla::ToZeroIfNonfinite(aYScroll));
853
0
854
0
  Scroll(scrollPos, ScrollOptions());
855
0
}
856
857
void
858
Element::Scroll(const ScrollToOptions& aOptions)
859
0
{
860
0
  nsIScrollableFrame *sf = GetScrollFrame();
861
0
  if (sf) {
862
0
    CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
863
0
    if (aOptions.mLeft.WasPassed()) {
864
0
      scrollPos.x = mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
865
0
    }
866
0
    if (aOptions.mTop.WasPassed()) {
867
0
      scrollPos.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
868
0
    }
869
0
    Scroll(scrollPos, aOptions);
870
0
  }
871
0
}
872
873
void
874
Element::ScrollTo(double aXScroll, double aYScroll)
875
0
{
876
0
  Scroll(aXScroll, aYScroll);
877
0
}
878
879
void
880
Element::ScrollTo(const ScrollToOptions& aOptions)
881
0
{
882
0
  Scroll(aOptions);
883
0
}
884
885
void
886
Element::ScrollBy(double aXScrollDif, double aYScrollDif)
887
0
{
888
0
  nsIScrollableFrame *sf = GetScrollFrame();
889
0
  if (sf) {
890
0
    CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
891
0
    scrollPos += CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScrollDif),
892
0
                                       mozilla::ToZeroIfNonfinite(aYScrollDif));
893
0
    Scroll(scrollPos, ScrollOptions());
894
0
  }
895
0
}
896
897
void
898
Element::ScrollBy(const ScrollToOptions& aOptions)
899
0
{
900
0
  nsIScrollableFrame *sf = GetScrollFrame();
901
0
  if (sf) {
902
0
    CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
903
0
    if (aOptions.mLeft.WasPassed()) {
904
0
      scrollPos.x += mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
905
0
    }
906
0
    if (aOptions.mTop.WasPassed()) {
907
0
      scrollPos.y += mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
908
0
    }
909
0
    Scroll(scrollPos, aOptions);
910
0
  }
911
0
}
912
913
int32_t
914
Element::ScrollTop()
915
0
{
916
0
  nsIScrollableFrame* sf = GetScrollFrame();
917
0
  return sf ? sf->GetScrollPositionCSSPixels().y : 0;
918
0
}
919
920
void
921
Element::SetScrollTop(int32_t aScrollTop)
922
0
{
923
0
  // When aScrollTop is 0, we don't need to flush layout to scroll to that
924
0
  // point; we know 0 is always in range.  At least we think so...  But we do
925
0
  // need to flush frames so we ensure we find the right scrollable frame if
926
0
  // there is one.
927
0
  //
928
0
  // If aScrollTop is nonzero, we need to flush layout because we need to figure
929
0
  // out what our real scrollTopMax is.
930
0
  FlushType flushType = aScrollTop == 0 ? FlushType::Frames : FlushType::Layout;
931
0
  nsIScrollableFrame* sf = GetScrollFrame(nullptr, flushType);
932
0
  if (sf) {
933
0
    nsIScrollableFrame::ScrollMode scrollMode = nsIScrollableFrame::INSTANT;
934
0
    if (sf->GetScrollStyles().mScrollBehavior == NS_STYLE_SCROLL_BEHAVIOR_SMOOTH) {
935
0
      scrollMode = nsIScrollableFrame::SMOOTH_MSD;
936
0
    }
937
0
    sf->ScrollToCSSPixels(CSSIntPoint(sf->GetScrollPositionCSSPixels().x,
938
0
                                      aScrollTop),
939
0
                          scrollMode);
940
0
  }
941
0
}
942
943
int32_t
944
Element::ScrollLeft()
945
0
{
946
0
  nsIScrollableFrame* sf = GetScrollFrame();
947
0
  return sf ? sf->GetScrollPositionCSSPixels().x : 0;
948
0
}
949
950
void
951
Element::SetScrollLeft(int32_t aScrollLeft)
952
0
{
953
0
  // We can't assume things here based on the value of aScrollLeft, because
954
0
  // depending on our direction and layout 0 may or may not be in our scroll
955
0
  // range.  So we need to flush layout no matter what.
956
0
  nsIScrollableFrame* sf = GetScrollFrame();
957
0
  if (sf) {
958
0
    nsIScrollableFrame::ScrollMode scrollMode = nsIScrollableFrame::INSTANT;
959
0
    if (sf->GetScrollStyles().mScrollBehavior == NS_STYLE_SCROLL_BEHAVIOR_SMOOTH) {
960
0
      scrollMode = nsIScrollableFrame::SMOOTH_MSD;
961
0
    }
962
0
963
0
    sf->ScrollToCSSPixels(CSSIntPoint(aScrollLeft,
964
0
                                      sf->GetScrollPositionCSSPixels().y),
965
0
                          scrollMode);
966
0
  }
967
0
}
968
969
970
bool
971
Element::ScrollByNoFlush(int32_t aDx, int32_t aDy)
972
0
{
973
0
  nsIScrollableFrame* sf = GetScrollFrame(nullptr, FlushType::None);
974
0
  if (!sf) {
975
0
    return false;
976
0
  }
977
0
978
0
  AutoWeakFrame weakRef(sf->GetScrolledFrame());
979
0
980
0
  CSSIntPoint before = sf->GetScrollPositionCSSPixels();
981
0
  sf->ScrollToCSSPixelsApproximate(CSSIntPoint(before.x + aDx, before.y + aDy));
982
0
983
0
  // The frame was destroyed, can't keep on scrolling.
984
0
  if (!weakRef.IsAlive()) {
985
0
    return false;
986
0
  }
987
0
988
0
  CSSIntPoint after = sf->GetScrollPositionCSSPixels();
989
0
  return (before != after);
990
0
}
991
992
void
993
Element::MozScrollSnap()
994
0
{
995
0
  nsIScrollableFrame* sf = GetScrollFrame(nullptr, FlushType::None);
996
0
  if (sf) {
997
0
    sf->ScrollSnap();
998
0
  }
999
0
}
1000
1001
static nsSize GetScrollRectSizeForOverflowVisibleFrame(nsIFrame* aFrame)
1002
0
{
1003
0
  if (!aFrame) {
1004
0
    return nsSize(0,0);
1005
0
  }
1006
0
1007
0
  nsRect paddingRect = aFrame->GetPaddingRectRelativeToSelf();
1008
0
  nsOverflowAreas overflowAreas(paddingRect, paddingRect);
1009
0
  // Add the scrollable overflow areas of children (if any) to the paddingRect.
1010
0
  // It's important to start with the paddingRect, otherwise if there are no
1011
0
  // children the overflow rect will be 0,0,0,0 which will force the point 0,0
1012
0
  // to be included in the final rect.
1013
0
  nsLayoutUtils::UnionChildOverflow(aFrame, overflowAreas);
1014
0
  // Make sure that an empty padding-rect's edges are included, by adding
1015
0
  // the padding-rect in again with UnionEdges.
1016
0
  nsRect overflowRect =
1017
0
    overflowAreas.ScrollableOverflow().UnionEdges(paddingRect);
1018
0
  return nsLayoutUtils::GetScrolledRect(aFrame,
1019
0
      overflowRect, paddingRect.Size(),
1020
0
      aFrame->StyleVisibility()->mDirection).Size();
1021
0
}
1022
1023
int32_t
1024
Element::ScrollHeight()
1025
0
{
1026
0
  if (IsSVGElement())
1027
0
    return 0;
1028
0
1029
0
  nsIFrame* frame;
1030
0
  nsIScrollableFrame* sf = GetScrollFrame(&frame);
1031
0
  nscoord height;
1032
0
  if (sf) {
1033
0
    height = sf->GetScrollRange().Height() + sf->GetScrollPortRect().Height();
1034
0
  } else {
1035
0
    height = GetScrollRectSizeForOverflowVisibleFrame(frame).height;
1036
0
  }
1037
0
1038
0
  return nsPresContext::AppUnitsToIntCSSPixels(height);
1039
0
}
1040
1041
int32_t
1042
Element::ScrollWidth()
1043
0
{
1044
0
  if (IsSVGElement())
1045
0
    return 0;
1046
0
1047
0
  nsIFrame* frame;
1048
0
  nsIScrollableFrame* sf = GetScrollFrame(&frame);
1049
0
  nscoord width;
1050
0
  if (sf) {
1051
0
    width = sf->GetScrollRange().Width() + sf->GetScrollPortRect().Width();
1052
0
  } else {
1053
0
    width = GetScrollRectSizeForOverflowVisibleFrame(frame).width;
1054
0
  }
1055
0
1056
0
  return nsPresContext::AppUnitsToIntCSSPixels(width);
1057
0
}
1058
1059
nsRect
1060
Element::GetClientAreaRect()
1061
0
{
1062
0
  nsIFrame* frame;
1063
0
  nsIScrollableFrame* sf = GetScrollFrame(&frame);
1064
0
1065
0
  if (sf) {
1066
0
    return sf->GetScrollPortRect();
1067
0
  }
1068
0
1069
0
  if (frame &&
1070
0
      // The display check is OK even though we're not looking at the style
1071
0
      // frame, because the style frame only differs from "frame" for tables,
1072
0
      // and table wrappers have the same display as the table itself.
1073
0
      (frame->StyleDisplay()->mDisplay != StyleDisplay::Inline ||
1074
0
       frame->IsFrameOfType(nsIFrame::eReplaced))) {
1075
0
    // Special case code to make client area work even when there isn't
1076
0
    // a scroll view, see bug 180552, bug 227567.
1077
0
    return frame->GetPaddingRect() - frame->GetPositionIgnoringScrolling();
1078
0
  }
1079
0
1080
0
  // SVG nodes reach here and just return 0
1081
0
  return nsRect(0, 0, 0, 0);
1082
0
}
1083
1084
already_AddRefed<DOMRect>
1085
Element::GetBoundingClientRect()
1086
0
{
1087
0
  RefPtr<DOMRect> rect = new DOMRect(this);
1088
0
1089
0
  nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
1090
0
  if (!frame) {
1091
0
    // display:none, perhaps? Return the empty rect
1092
0
    return rect.forget();
1093
0
  }
1094
0
1095
0
  nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(frame,
1096
0
          nsLayoutUtils::GetContainingBlockForClientRect(frame),
1097
0
          nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
1098
0
  rect->SetLayoutRect(r);
1099
0
  return rect.forget();
1100
0
}
1101
1102
already_AddRefed<DOMRectList>
1103
Element::GetClientRects()
1104
0
{
1105
0
  RefPtr<DOMRectList> rectList = new DOMRectList(this);
1106
0
1107
0
  nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
1108
0
  if (!frame) {
1109
0
    // display:none, perhaps? Return an empty list
1110
0
    return rectList.forget();
1111
0
  }
1112
0
1113
0
  nsLayoutUtils::RectListBuilder builder(rectList);
1114
0
  nsLayoutUtils::GetAllInFlowRects(frame,
1115
0
          nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder,
1116
0
          nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
1117
0
  return rectList.forget();
1118
0
}
1119
1120
//----------------------------------------------------------------------
1121
1122
void
1123
Element::AddToIdTable(nsAtom* aId)
1124
0
{
1125
0
  NS_ASSERTION(HasID(), "Node doesn't have an ID?");
1126
0
  if (IsInShadowTree()) {
1127
0
    ShadowRoot* containingShadow = GetContainingShadow();
1128
0
    containingShadow->AddToIdTable(this, aId);
1129
0
  } else {
1130
0
    nsIDocument* doc = GetUncomposedDoc();
1131
0
    if (doc && (!IsInAnonymousSubtree() || doc->IsXULDocument())) {
1132
0
      doc->AddToIdTable(this, aId);
1133
0
    }
1134
0
  }
1135
0
}
1136
1137
void
1138
Element::RemoveFromIdTable()
1139
0
{
1140
0
  if (!HasID()) {
1141
0
    return;
1142
0
  }
1143
0
1144
0
  nsAtom* id = DoGetID();
1145
0
  if (IsInShadowTree()) {
1146
0
    ShadowRoot* containingShadow = GetContainingShadow();
1147
0
    // Check for containingShadow because it may have
1148
0
    // been deleted during unlinking.
1149
0
    if (containingShadow) {
1150
0
      containingShadow->RemoveFromIdTable(this, id);
1151
0
    }
1152
0
  } else {
1153
0
    nsIDocument* doc = GetUncomposedDoc();
1154
0
    if (doc && (!IsInAnonymousSubtree() || doc->IsXULDocument())) {
1155
0
      doc->RemoveFromIdTable(this, id);
1156
0
    }
1157
0
  }
1158
0
}
1159
1160
void
1161
Element::SetSlot(const nsAString& aName, ErrorResult& aError)
1162
0
{
1163
0
  aError = SetAttr(kNameSpaceID_None, nsGkAtoms::slot, aName, true);
1164
0
}
1165
1166
void
1167
Element::GetSlot(nsAString& aName)
1168
0
{
1169
0
  GetAttr(kNameSpaceID_None, nsGkAtoms::slot, aName);
1170
0
}
1171
1172
// https://dom.spec.whatwg.org/#dom-element-shadowroot
1173
ShadowRoot*
1174
Element::GetShadowRootByMode() const
1175
0
{
1176
0
  /**
1177
0
   * 1. Let shadow be context object’s shadow root.
1178
0
   * 2. If shadow is null or its mode is "closed", then return null.
1179
0
   */
1180
0
  ShadowRoot* shadowRoot = GetShadowRoot();
1181
0
  if (!shadowRoot || shadowRoot->IsClosed()) {
1182
0
    return nullptr;
1183
0
  }
1184
0
1185
0
  /**
1186
0
   * 3. Return shadow.
1187
0
   */
1188
0
  return shadowRoot;
1189
0
}
1190
1191
// https://dom.spec.whatwg.org/#dom-element-attachshadow
1192
already_AddRefed<ShadowRoot>
1193
Element::AttachShadow(const ShadowRootInit& aInit, ErrorResult& aError)
1194
0
{
1195
0
  /**
1196
0
   * 1. If context object’s namespace is not the HTML namespace,
1197
0
   *    then throw a "NotSupportedError" DOMException.
1198
0
   */
1199
0
  if (!IsHTMLElement() &&
1200
0
      !(XRE_IsParentProcess() && IsXULElement() && nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal()))) {
1201
0
    aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
1202
0
    return nullptr;
1203
0
  }
1204
0
1205
0
  /**
1206
0
   * 2. If context object’s local name is not
1207
0
   *      a valid custom element name, "article", "aside", "blockquote",
1208
0
   *      "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6",
1209
0
   *      "header", "main" "nav", "p", "section", or "span",
1210
0
   *    then throw a "NotSupportedError" DOMException.
1211
0
   */
1212
0
  nsAtom* nameAtom = NodeInfo()->NameAtom();
1213
0
  if (!(nsContentUtils::IsCustomElementName(nameAtom, NodeInfo()->NamespaceID()) ||
1214
0
        nameAtom == nsGkAtoms::article ||
1215
0
        nameAtom == nsGkAtoms::aside ||
1216
0
        nameAtom == nsGkAtoms::blockquote ||
1217
0
        nameAtom == nsGkAtoms::body ||
1218
0
        nameAtom == nsGkAtoms::div ||
1219
0
        nameAtom == nsGkAtoms::footer ||
1220
0
        nameAtom == nsGkAtoms::h1 ||
1221
0
        nameAtom == nsGkAtoms::h2 ||
1222
0
        nameAtom == nsGkAtoms::h3 ||
1223
0
        nameAtom == nsGkAtoms::h4 ||
1224
0
        nameAtom == nsGkAtoms::h5 ||
1225
0
        nameAtom == nsGkAtoms::h6 ||
1226
0
        nameAtom == nsGkAtoms::header ||
1227
0
        nameAtom == nsGkAtoms::main ||
1228
0
        nameAtom == nsGkAtoms::nav ||
1229
0
        nameAtom == nsGkAtoms::p ||
1230
0
        nameAtom == nsGkAtoms::section ||
1231
0
        nameAtom == nsGkAtoms::span)) {
1232
0
    aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
1233
0
    return nullptr;
1234
0
  }
1235
0
1236
0
  /**
1237
0
   * 3. If context object is a shadow host, then throw
1238
0
   *    an "InvalidStateError" DOMException.
1239
0
   */
1240
0
  if (GetShadowRoot() || GetXBLBinding()) {
1241
0
    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1242
0
    return nullptr;
1243
0
  }
1244
0
1245
0
  if (StaticPrefs::dom_webcomponents_shadowdom_report_usage()) {
1246
0
    OwnerDoc()->ReportShadowDOMUsage();
1247
0
  }
1248
0
1249
0
  return AttachShadowWithoutNameChecks(aInit.mMode);
1250
0
}
1251
1252
already_AddRefed<ShadowRoot>
1253
Element::AttachShadowWithoutNameChecks(ShadowRootMode aMode)
1254
0
{
1255
0
  nsAutoScriptBlocker scriptBlocker;
1256
0
1257
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo =
1258
0
    mNodeInfo->NodeInfoManager()->GetNodeInfo(
1259
0
      nsGkAtoms::documentFragmentNodeName, nullptr, kNameSpaceID_None,
1260
0
      DOCUMENT_FRAGMENT_NODE);
1261
0
1262
0
  if (nsIDocument* doc = GetComposedDoc()) {
1263
0
    if (nsIPresShell* shell = doc->GetShell()) {
1264
0
      shell->DestroyFramesForAndRestyle(this);
1265
0
    }
1266
0
  }
1267
0
  MOZ_ASSERT(!GetPrimaryFrame());
1268
0
1269
0
  /**
1270
0
   * 4. Let shadow be a new shadow root whose node document is
1271
0
   *    context object’s node document, host is context object,
1272
0
   *    and mode is init’s mode.
1273
0
   */
1274
0
  RefPtr<ShadowRoot> shadowRoot =
1275
0
    new ShadowRoot(this, aMode, nodeInfo.forget());
1276
0
1277
0
  if (NodeOrAncestorHasDirAuto()) {
1278
0
    shadowRoot->SetAncestorHasDirAuto();
1279
0
  }
1280
0
1281
0
  /**
1282
0
   * 5. Set context object’s shadow root to shadow.
1283
0
   */
1284
0
  SetShadowRoot(shadowRoot);
1285
0
1286
0
  // Dispatch a "shadowrootattached" event for devtools.
1287
0
  {
1288
0
    AsyncEventDispatcher* dispatcher =
1289
0
      new AsyncEventDispatcher(this,
1290
0
                               NS_LITERAL_STRING("shadowrootattached"),
1291
0
                               CanBubble::eYes,
1292
0
                               ChromeOnlyDispatch::eYes,
1293
0
                               Composed::eYes);
1294
0
    dispatcher->PostDOMEvent();
1295
0
  }
1296
0
1297
0
  /**
1298
0
   * 6. Return shadow.
1299
0
   */
1300
0
  return shadowRoot.forget();
1301
0
}
1302
1303
void
1304
Element::UnattachShadow()
1305
0
{
1306
0
  RefPtr<ShadowRoot> shadowRoot = GetShadowRoot();
1307
0
  if (!shadowRoot) {
1308
0
    return;
1309
0
  }
1310
0
1311
0
  nsAutoScriptBlocker scriptBlocker;
1312
0
1313
0
  nsIDocument* doc = GetComposedDoc();
1314
0
  if (doc) {
1315
0
    if (nsIPresShell* shell = doc->GetShell()) {
1316
0
      shell->DestroyFramesForAndRestyle(this);
1317
0
    }
1318
0
  }
1319
0
  MOZ_ASSERT(!GetPrimaryFrame());
1320
0
1321
0
  // Simply unhook the shadow root from the element.
1322
0
  MOZ_ASSERT(!shadowRoot->HasSlots(), "Won't work when shadow root has slots!");
1323
0
  shadowRoot->Unbind();
1324
0
  SetShadowRoot(nullptr);
1325
0
}
1326
1327
void
1328
Element::GetAttribute(const nsAString& aName, DOMString& aReturn)
1329
0
{
1330
0
  const nsAttrValue* val =
1331
0
    mAttrs.GetAttr(aName,
1332
0
                   IsHTMLElement() && IsInHTMLDocument() ?
1333
0
                     eIgnoreCase : eCaseMatters);
1334
0
  if (val) {
1335
0
    val->ToString(aReturn);
1336
0
  } else {
1337
0
    if (IsXULElement()) {
1338
0
      // XXX should be SetDOMStringToNull(aReturn);
1339
0
      // See bug 232598
1340
0
      // aReturn is already empty
1341
0
    } else {
1342
0
      aReturn.SetNull();
1343
0
    }
1344
0
  }
1345
0
}
1346
1347
bool
1348
Element::ToggleAttribute(const nsAString& aName,
1349
                         const Optional<bool>& aForce,
1350
                         nsIPrincipal* aTriggeringPrincipal,
1351
                         ErrorResult& aError)
1352
0
{
1353
0
  aError = nsContentUtils::CheckQName(aName, false);
1354
0
  if (aError.Failed()) {
1355
0
    return false;
1356
0
  }
1357
0
1358
0
  nsAutoString nameToUse;
1359
0
  const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse);
1360
0
  if (!name) {
1361
0
    if (aForce.WasPassed() && !aForce.Value()) {
1362
0
      return false;
1363
0
    }
1364
0
    RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse);
1365
0
    if (!nameAtom) {
1366
0
      aError.Throw(NS_ERROR_OUT_OF_MEMORY);
1367
0
      return false;
1368
0
    }
1369
0
    aError = SetAttr(kNameSpaceID_None, nameAtom, EmptyString(), aTriggeringPrincipal, true);
1370
0
    return true;
1371
0
  }
1372
0
  if (aForce.WasPassed() && aForce.Value()) {
1373
0
    return true;
1374
0
  }
1375
0
  // Hold a strong reference here so that the atom or nodeinfo doesn't go
1376
0
  // away during UnsetAttr. If it did UnsetAttr would be left with a
1377
0
  // dangling pointer as argument without knowing it.
1378
0
  nsAttrName tmp(*name);
1379
0
1380
0
  aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true);
1381
0
  return false;
1382
0
}
1383
1384
void
1385
Element::SetAttribute(const nsAString& aName,
1386
                      const nsAString& aValue,
1387
                      nsIPrincipal* aTriggeringPrincipal,
1388
                      ErrorResult& aError)
1389
0
{
1390
0
  aError = nsContentUtils::CheckQName(aName, false);
1391
0
  if (aError.Failed()) {
1392
0
    return;
1393
0
  }
1394
0
1395
0
  nsAutoString nameToUse;
1396
0
  const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse);
1397
0
  if (!name) {
1398
0
    RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse);
1399
0
    if (!nameAtom) {
1400
0
      aError.Throw(NS_ERROR_OUT_OF_MEMORY);
1401
0
      return;
1402
0
    }
1403
0
    aError = SetAttr(kNameSpaceID_None, nameAtom, aValue, aTriggeringPrincipal, true);
1404
0
    return;
1405
0
  }
1406
0
1407
0
  aError = SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(),
1408
0
                   aValue, aTriggeringPrincipal, true);
1409
0
}
1410
1411
void
1412
Element::RemoveAttribute(const nsAString& aName, ErrorResult& aError)
1413
0
{
1414
0
  const nsAttrName* name = InternalGetAttrNameFromQName(aName);
1415
0
1416
0
  if (!name) {
1417
0
    // If there is no canonical nsAttrName for this attribute name, then the
1418
0
    // attribute does not exist and we can't get its namespace ID and
1419
0
    // local name below, so we return early.
1420
0
    return;
1421
0
  }
1422
0
1423
0
  // Hold a strong reference here so that the atom or nodeinfo doesn't go
1424
0
  // away during UnsetAttr. If it did UnsetAttr would be left with a
1425
0
  // dangling pointer as argument without knowing it.
1426
0
  nsAttrName tmp(*name);
1427
0
1428
0
  aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true);
1429
0
}
1430
1431
Attr*
1432
Element::GetAttributeNode(const nsAString& aName)
1433
0
{
1434
0
  return Attributes()->GetNamedItem(aName);
1435
0
}
1436
1437
already_AddRefed<Attr>
1438
Element::SetAttributeNode(Attr& aNewAttr, ErrorResult& aError)
1439
0
{
1440
0
  return Attributes()->SetNamedItemNS(aNewAttr, aError);
1441
0
}
1442
1443
already_AddRefed<Attr>
1444
Element::RemoveAttributeNode(Attr& aAttribute,
1445
                             ErrorResult& aError)
1446
0
{
1447
0
  Element *elem = aAttribute.GetElement();
1448
0
  if (elem != this) {
1449
0
    aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
1450
0
    return nullptr;
1451
0
  }
1452
0
1453
0
  nsAutoString nameSpaceURI;
1454
0
  aAttribute.NodeInfo()->GetNamespaceURI(nameSpaceURI);
1455
0
  return Attributes()->RemoveNamedItemNS(nameSpaceURI, aAttribute.NodeInfo()->LocalName(), aError);
1456
0
}
1457
1458
void
1459
Element::GetAttributeNS(const nsAString& aNamespaceURI,
1460
                        const nsAString& aLocalName,
1461
                        nsAString& aReturn)
1462
0
{
1463
0
  int32_t nsid =
1464
0
    nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
1465
0
                                                       nsContentUtils::IsChromeDoc(OwnerDoc()));
1466
0
1467
0
  if (nsid == kNameSpaceID_Unknown) {
1468
0
    // Unknown namespace means no attribute.
1469
0
    SetDOMStringToNull(aReturn);
1470
0
    return;
1471
0
  }
1472
0
1473
0
  RefPtr<nsAtom> name = NS_AtomizeMainThread(aLocalName);
1474
0
  bool hasAttr = GetAttr(nsid, name, aReturn);
1475
0
  if (!hasAttr) {
1476
0
    SetDOMStringToNull(aReturn);
1477
0
  }
1478
0
}
1479
1480
void
1481
Element::SetAttributeNS(const nsAString& aNamespaceURI,
1482
                        const nsAString& aQualifiedName,
1483
                        const nsAString& aValue,
1484
                        nsIPrincipal* aTriggeringPrincipal,
1485
                        ErrorResult& aError)
1486
0
{
1487
0
  RefPtr<mozilla::dom::NodeInfo> ni;
1488
0
  aError =
1489
0
    nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName,
1490
0
                                         mNodeInfo->NodeInfoManager(),
1491
0
                                         ATTRIBUTE_NODE,
1492
0
                                         getter_AddRefs(ni));
1493
0
  if (aError.Failed()) {
1494
0
    return;
1495
0
  }
1496
0
1497
0
  aError = SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(),
1498
0
                   aValue, aTriggeringPrincipal, true);
1499
0
}
1500
1501
void
1502
Element::RemoveAttributeNS(const nsAString& aNamespaceURI,
1503
                           const nsAString& aLocalName,
1504
                           ErrorResult& aError)
1505
0
{
1506
0
  RefPtr<nsAtom> name = NS_AtomizeMainThread(aLocalName);
1507
0
  int32_t nsid =
1508
0
    nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
1509
0
                                                       nsContentUtils::IsChromeDoc(OwnerDoc()));
1510
0
1511
0
  if (nsid == kNameSpaceID_Unknown) {
1512
0
    // If the namespace ID is unknown, it means there can't possibly be an
1513
0
    // existing attribute. We would need a known namespace ID to pass into
1514
0
    // UnsetAttr, so we return early if we don't have one.
1515
0
    return;
1516
0
  }
1517
0
1518
0
  aError = UnsetAttr(nsid, name, true);
1519
0
}
1520
1521
Attr*
1522
Element::GetAttributeNodeNS(const nsAString& aNamespaceURI,
1523
                            const nsAString& aLocalName)
1524
0
{
1525
0
  return GetAttributeNodeNSInternal(aNamespaceURI, aLocalName);
1526
0
}
1527
1528
Attr*
1529
Element::GetAttributeNodeNSInternal(const nsAString& aNamespaceURI,
1530
                                    const nsAString& aLocalName)
1531
0
{
1532
0
  return Attributes()->GetNamedItemNS(aNamespaceURI, aLocalName);
1533
0
}
1534
1535
already_AddRefed<Attr>
1536
Element::SetAttributeNodeNS(Attr& aNewAttr,
1537
                            ErrorResult& aError)
1538
0
{
1539
0
  return Attributes()->SetNamedItemNS(aNewAttr, aError);
1540
0
}
1541
1542
already_AddRefed<nsIHTMLCollection>
1543
Element::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
1544
                                const nsAString& aLocalName,
1545
                                ErrorResult& aError)
1546
0
{
1547
0
  int32_t nameSpaceId = kNameSpaceID_Wildcard;
1548
0
1549
0
  if (!aNamespaceURI.EqualsLiteral("*")) {
1550
0
    aError =
1551
0
      nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
1552
0
                                                            nameSpaceId);
1553
0
    if (aError.Failed()) {
1554
0
      return nullptr;
1555
0
    }
1556
0
  }
1557
0
1558
0
  NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
1559
0
1560
0
  return NS_GetContentList(this, nameSpaceId, aLocalName);
1561
0
}
1562
1563
bool
1564
Element::HasAttributeNS(const nsAString& aNamespaceURI,
1565
                        const nsAString& aLocalName) const
1566
0
{
1567
0
  int32_t nsid =
1568
0
    nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
1569
0
                                                       nsContentUtils::IsChromeDoc(OwnerDoc()));
1570
0
1571
0
  if (nsid == kNameSpaceID_Unknown) {
1572
0
    // Unknown namespace means no attr...
1573
0
    return false;
1574
0
  }
1575
0
1576
0
  RefPtr<nsAtom> name = NS_AtomizeMainThread(aLocalName);
1577
0
  return HasAttr(nsid, name);
1578
0
}
1579
1580
already_AddRefed<nsIHTMLCollection>
1581
Element::GetElementsByClassName(const nsAString& aClassNames)
1582
0
{
1583
0
  return nsContentUtils::GetElementsByClassName(this, aClassNames);
1584
0
}
1585
1586
void
1587
Element::GetElementsWithGrid(nsTArray<RefPtr<Element>>& aElements)
1588
0
{
1589
0
  nsINode* cur = this;
1590
0
  while (cur) {
1591
0
    if (cur->IsElement()) {
1592
0
      Element* elem = cur->AsElement();
1593
0
1594
0
      if (elem->GetPrimaryFrame()) {
1595
0
        // See if this has a GridContainerFrame. Use the same method that
1596
0
        // nsGridContainerFrame uses, which deals with some edge cases.
1597
0
        if (nsGridContainerFrame::GetGridContainerFrame(elem->GetPrimaryFrame())) {
1598
0
          aElements.AppendElement(elem);
1599
0
        }
1600
0
1601
0
        // This element has a frame, so allow the traversal to go through
1602
0
        // the children.
1603
0
        cur = cur->GetNextNode(this);
1604
0
        continue;
1605
0
      }
1606
0
    }
1607
0
1608
0
    // Either this isn't an element, or it has no frame. Continue with the
1609
0
    // traversal but ignore all the children.
1610
0
    cur = cur->GetNextNonChildNode(this);
1611
0
  }
1612
0
}
1613
1614
/**
1615
 * Returns the count of descendants (inclusive of aContent) in
1616
 * the uncomposed document that are explicitly set as editable.
1617
 */
1618
static uint32_t
1619
EditableInclusiveDescendantCount(nsIContent* aContent)
1620
0
{
1621
0
  auto htmlElem = nsGenericHTMLElement::FromNode(aContent);
1622
0
  if (htmlElem) {
1623
0
    return htmlElem->EditableInclusiveDescendantCount();
1624
0
  }
1625
0
1626
0
  return aContent->EditableDescendantCount();
1627
0
}
1628
1629
nsresult
1630
Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
1631
                    nsIContent* aBindingParent)
1632
0
{
1633
0
  MOZ_ASSERT(aParent || aDocument, "Must have document if no parent!");
1634
0
  MOZ_ASSERT((NODE_FROM(aParent, aDocument)->OwnerDoc() == OwnerDoc()),
1635
0
             "Must have the same owner document");
1636
0
  MOZ_ASSERT(!aParent || aDocument == aParent->GetUncomposedDoc(),
1637
0
             "aDocument must be current doc of aParent");
1638
0
  MOZ_ASSERT(!IsInComposedDoc(), "Already have a document.  Unbind first!");
1639
0
  MOZ_ASSERT(!IsInUncomposedDoc(), "Already have a document.  Unbind first!");
1640
0
  // Note that as we recurse into the kids, they'll have a non-null parent.  So
1641
0
  // only assert if our parent is _changing_ while we have a parent.
1642
0
  MOZ_ASSERT(!GetParent() || aParent == GetParent(),
1643
0
             "Already have a parent.  Unbind first!");
1644
0
  MOZ_ASSERT(!GetBindingParent() ||
1645
0
             aBindingParent == GetBindingParent() ||
1646
0
             (!aBindingParent && aParent &&
1647
0
              aParent->GetBindingParent() == GetBindingParent()),
1648
0
             "Already have a binding parent.  Unbind first!");
1649
0
  MOZ_ASSERT(aBindingParent != this,
1650
0
             "Content must not be its own binding parent");
1651
0
  MOZ_ASSERT(!IsRootOfNativeAnonymousSubtree() ||
1652
0
             aBindingParent == aParent,
1653
0
             "Native anonymous content must have its parent as its "
1654
0
             "own binding parent");
1655
0
  MOZ_ASSERT(aBindingParent || !aParent ||
1656
0
             aBindingParent == aParent->GetBindingParent(),
1657
0
             "We should be passed the right binding parent");
1658
0
1659
0
#ifdef MOZ_XUL
1660
0
  // First set the binding parent
1661
0
  nsXULElement* xulElem = nsXULElement::FromNode(this);
1662
0
  if (xulElem) {
1663
0
    xulElem->SetXULBindingParent(aBindingParent);
1664
0
  }
1665
0
  else
1666
0
#endif
1667
0
  {
1668
0
    if (aBindingParent) {
1669
0
      nsExtendedDOMSlots* slots = ExtendedDOMSlots();
1670
0
1671
0
      slots->mBindingParent = aBindingParent; // Weak, so no addref happens.
1672
0
    }
1673
0
  }
1674
0
  NS_ASSERTION(!aBindingParent || IsRootOfNativeAnonymousSubtree() ||
1675
0
               !HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) ||
1676
0
               (aParent && aParent->IsInNativeAnonymousSubtree()),
1677
0
               "Trying to re-bind content from native anonymous subtree to "
1678
0
               "non-native anonymous parent!");
1679
0
  if (aParent) {
1680
0
    if (aParent->IsInNativeAnonymousSubtree()) {
1681
0
      SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
1682
0
    }
1683
0
    if (aParent->HasFlag(NODE_CHROME_ONLY_ACCESS)) {
1684
0
      SetFlags(NODE_CHROME_ONLY_ACCESS);
1685
0
    }
1686
0
    if (HasFlag(NODE_IS_ANONYMOUS_ROOT)) {
1687
0
      aParent->SetMayHaveAnonymousChildren();
1688
0
    }
1689
0
    if (aParent->IsInShadowTree()) {
1690
0
      ClearSubtreeRootPointer();
1691
0
      SetFlags(NODE_IS_IN_SHADOW_TREE);
1692
0
      MOZ_ASSERT(aParent->GetContainingShadow());
1693
0
      ExtendedDOMSlots()->mContainingShadow = aParent->GetContainingShadow();
1694
0
    }
1695
0
  }
1696
0
1697
0
  bool hadParent = !!GetParentNode();
1698
0
1699
0
  // Now set the parent.
1700
0
  if (aParent) {
1701
0
    if (!GetParent()) {
1702
0
      NS_ADDREF(aParent);
1703
0
    }
1704
0
    mParent = aParent;
1705
0
  } else {
1706
0
    mParent = aDocument;
1707
0
  }
1708
0
  SetParentIsContent(aParent);
1709
0
1710
0
  // XXXbz sXBL/XBL2 issue!
1711
0
1712
0
  MOZ_ASSERT(!HasAnyOfFlags(Element::kAllServoDescendantBits));
1713
0
1714
0
  // Finally, set the document
1715
0
  if (aDocument) {
1716
0
    // Notify XBL- & nsIAnonymousContentCreator-generated
1717
0
    // anonymous content that the document is changing.
1718
0
    // XXXbz ordering issues here?  Probably not, since ChangeDocumentFor is
1719
0
    // just pretty broken anyway....  Need to get it working.
1720
0
    // XXXbz XBL doesn't handle this (asserts), and we don't really want
1721
0
    // to be doing this during parsing anyway... sort this out.
1722
0
    //    aDocument->BindingManager()->ChangeDocumentFor(this, nullptr,
1723
0
    //                                                   aDocument);
1724
0
1725
0
    // We no longer need to track the subtree pointer (and in fact we'll assert
1726
0
    // if we do this any later).
1727
0
    ClearSubtreeRootPointer();
1728
0
1729
0
    // Being added to a document.
1730
0
    SetIsInDocument();
1731
0
    SetIsConnected(true);
1732
0
1733
0
    // Clear the lazy frame construction bits.
1734
0
    UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
1735
0
  } else if (IsInShadowTree()) {
1736
0
    SetIsConnected(aParent->IsInComposedDoc());
1737
0
    // We're not in a document, but we did get inserted into a shadow tree.
1738
0
    // Since we won't have any restyle data in the document's restyle trackers,
1739
0
    // don't let us get inserted with restyle bits set incorrectly.
1740
0
    //
1741
0
    // Also clear all the other flags that are cleared above when we do get
1742
0
    // inserted into a document.
1743
0
    //
1744
0
    // See the comment about the restyle bits above, it also applies.
1745
0
    UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
1746
0
  } else {
1747
0
    // If we're not in the doc and not in a shadow tree,
1748
0
    // update our subtree pointer.
1749
0
    SetSubtreeRootPointer(aParent->SubtreeRoot());
1750
0
  }
1751
0
1752
0
  if (CustomElementRegistry::IsCustomElementEnabled(OwnerDoc()) && IsInComposedDoc()) {
1753
0
    // Connected callback must be enqueued whenever a custom element becomes
1754
0
    // connected.
1755
0
    CustomElementData* data = GetCustomElementData();
1756
0
    if (data) {
1757
0
      if (data->mState == CustomElementData::State::eCustom) {
1758
0
        nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eConnected, this);
1759
0
      } else {
1760
0
        // Step 7.7.2.2 https://dom.spec.whatwg.org/#concept-node-insert
1761
0
        nsContentUtils::TryToUpgradeElement(this);
1762
0
      }
1763
0
    }
1764
0
  }
1765
0
1766
0
  // This has to be here, rather than in nsGenericHTMLElement::BindToTree,
1767
0
  //  because it has to happen after updating the parent pointer, but before
1768
0
  //  recursively binding the kids.
1769
0
  if (IsHTMLElement()) {
1770
0
    SetDirOnBind(this, aParent);
1771
0
  }
1772
0
1773
0
  uint32_t editableDescendantCount = 0;
1774
0
1775
0
  UpdateEditableState(false);
1776
0
1777
0
  // If we had a pre-existing XBL binding, we might have anonymous children that
1778
0
  // also need to be told that they are moving.
1779
0
  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
1780
0
    nsXBLBinding* binding =
1781
0
      OwnerDoc()->BindingManager()->GetBindingWithContent(this);
1782
0
1783
0
    if (binding) {
1784
0
      binding->BindAnonymousContent(
1785
0
        binding->GetAnonymousContent(),
1786
0
        this,
1787
0
        binding->PrototypeBinding()->ChromeOnlyContent());
1788
0
    }
1789
0
  }
1790
0
1791
0
  // Now recurse into our kids
1792
0
  nsresult rv;
1793
0
  for (nsIContent* child = GetFirstChild(); child;
1794
0
       child = child->GetNextSibling()) {
1795
0
    rv = child->BindToTree(aDocument, this, aBindingParent);
1796
0
    NS_ENSURE_SUCCESS(rv, rv);
1797
0
1798
0
    editableDescendantCount += EditableInclusiveDescendantCount(child);
1799
0
  }
1800
0
1801
0
  if (aDocument) {
1802
0
    // Update our editable descendant count because we don't keep track of it
1803
0
    // for content that is not in the uncomposed document.
1804
0
    MOZ_ASSERT(EditableDescendantCount() == 0);
1805
0
    ChangeEditableDescendantCount(editableDescendantCount);
1806
0
1807
0
    if (!hadParent) {
1808
0
      uint32_t editableDescendantChange = EditableInclusiveDescendantCount(this);
1809
0
      if (editableDescendantChange != 0) {
1810
0
        // If we are binding a subtree root to the document, we need to update
1811
0
        // the editable descendant count of all the ancestors.
1812
0
        // But we don't cross Shadow DOM boundary.
1813
0
        // (The expected behavior with Shadow DOM is unclear)
1814
0
        nsIContent* parent = GetParent();
1815
0
        while (parent && parent->IsElement()) {
1816
0
          parent->ChangeEditableDescendantCount(editableDescendantChange);
1817
0
          parent = parent->GetParent();
1818
0
        }
1819
0
      }
1820
0
    }
1821
0
  }
1822
0
1823
0
  nsNodeUtils::ParentChainChanged(this);
1824
0
  if (!hadParent && IsRootOfNativeAnonymousSubtree()) {
1825
0
    nsNodeUtils::NativeAnonymousChildListChange(this, false);
1826
0
  }
1827
0
1828
0
  if (HasID()) {
1829
0
    AddToIdTable(DoGetID());
1830
0
  }
1831
0
1832
0
  if (MayHaveStyle() && !IsXULElement()) {
1833
0
    // XXXbz if we already have a style attr parsed, this won't do
1834
0
    // anything... need to fix that.
1835
0
    // If MayHaveStyle() is true, we must be an nsStyledElement
1836
0
    static_cast<nsStyledElement*>(this)->ReparseStyleAttribute(false, false);
1837
0
  }
1838
0
1839
0
  // Call BindToTree on shadow root children.
1840
0
  if (ShadowRoot* shadowRoot = GetShadowRoot()) {
1841
0
    rv = shadowRoot->Bind();
1842
0
    NS_ENSURE_SUCCESS(rv, rv);
1843
0
  }
1844
0
1845
0
  // FIXME(emilio): Why is this needed? The element shouldn't even be styled in
1846
0
  // the first place, we should style it properly eventually.
1847
0
  //
1848
0
  // Also, if this _is_ needed, then it's wrong and should use GetComposedDoc()
1849
0
  // to account for Shadow DOM.
1850
0
  if (aDocument && MayHaveAnimations()) {
1851
0
    CSSPseudoElementType pseudoType = GetPseudoElementType();
1852
0
    if ((pseudoType == CSSPseudoElementType::NotPseudo ||
1853
0
         pseudoType == CSSPseudoElementType::before ||
1854
0
         pseudoType == CSSPseudoElementType::after) &&
1855
0
        EffectSet::GetEffectSet(this, pseudoType)) {
1856
0
      if (nsPresContext* presContext = aDocument->GetPresContext()) {
1857
0
        presContext->EffectCompositor()->
1858
0
          RequestRestyle(this, pseudoType,
1859
0
                         EffectCompositor::RestyleType::Standard,
1860
0
                         EffectCompositor::CascadeLevel::Animations);
1861
0
      }
1862
0
    }
1863
0
  }
1864
0
1865
0
  // XXXbz script execution during binding can trigger some of these
1866
0
  // postcondition asserts....  But we do want that, since things will
1867
0
  // generally be quite broken when that happens.
1868
0
  MOZ_ASSERT(aDocument == GetUncomposedDoc(), "Bound to wrong document");
1869
0
  MOZ_ASSERT(aParent == GetParent(), "Bound to wrong parent");
1870
0
  MOZ_ASSERT(aBindingParent == GetBindingParent(),
1871
0
             "Bound to wrong binding parent");
1872
0
1873
0
  return NS_OK;
1874
0
}
1875
1876
RemoveFromBindingManagerRunnable::RemoveFromBindingManagerRunnable(
1877
  nsBindingManager* aManager,
1878
  nsIContent* aContent,
1879
  nsIDocument* aDoc)
1880
  : mozilla::Runnable("dom::RemoveFromBindingManagerRunnable")
1881
  , mManager(aManager)
1882
  , mContent(aContent)
1883
  , mDoc(aDoc)
1884
0
{}
1885
1886
0
RemoveFromBindingManagerRunnable::~RemoveFromBindingManagerRunnable() {}
1887
1888
NS_IMETHODIMP
1889
RemoveFromBindingManagerRunnable::Run()
1890
0
{
1891
0
  // It may be the case that the element was removed from the
1892
0
  // DOM, causing this runnable to be created, then inserted back
1893
0
  // into the document before the this runnable had a chance to
1894
0
  // tear down the binding. Only tear down the binding if the element
1895
0
  // is still no longer in the DOM. nsXBLService::LoadBinding tears
1896
0
  // down the old binding if the element is inserted back into the
1897
0
  // DOM and loads a different binding.
1898
0
  if (!mContent->IsInComposedDoc()) {
1899
0
    mManager->RemovedFromDocumentInternal(mContent, mDoc,
1900
0
                                          nsBindingManager::eRunDtor);
1901
0
  }
1902
0
1903
0
  return NS_OK;
1904
0
}
1905
1906
static bool
1907
ShouldRemoveFromIdTableOnUnbind(const Element& aElement, bool aNullParent)
1908
0
{
1909
0
  if (aElement.IsInUncomposedDoc()) {
1910
0
    return true;
1911
0
  }
1912
0
1913
0
  if (!aElement.IsInShadowTree()) {
1914
0
    return false;
1915
0
  }
1916
0
1917
0
  return aNullParent || !aElement.GetParent()->IsInShadowTree();
1918
0
}
1919
1920
void
1921
Element::UnbindFromTree(bool aDeep, bool aNullParent)
1922
0
{
1923
0
  MOZ_ASSERT(aDeep || (!GetUncomposedDoc() && !GetBindingParent()),
1924
0
                  "Shallow unbind won't clear document and binding parent on "
1925
0
                  "kids!");
1926
0
1927
0
  // Make sure to only remove from the ID table if our subtree root is actually
1928
0
  // changing.
1929
0
  if (ShouldRemoveFromIdTableOnUnbind(*this, aNullParent)) {
1930
0
    RemoveFromIdTable();
1931
0
  }
1932
0
1933
0
  // Make sure to unbind this node before doing the kids
1934
0
  nsIDocument* document = GetComposedDoc();
1935
0
1936
0
  if (HasPointerLock()) {
1937
0
    nsIDocument::UnlockPointer();
1938
0
  }
1939
0
  if (mState.HasState(NS_EVENT_STATE_FULLSCREEN)) {
1940
0
    // The element being removed is an ancestor of the fullscreen element,
1941
0
    // exit fullscreen state.
1942
0
    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
1943
0
                                    NS_LITERAL_CSTRING("DOM"), OwnerDoc(),
1944
0
                                    nsContentUtils::eDOM_PROPERTIES,
1945
0
                                    "RemovedFullscreenElement");
1946
0
    // Fully exit fullscreen.
1947
0
    nsIDocument::ExitFullscreenInDocTree(OwnerDoc());
1948
0
  }
1949
0
1950
0
  if (HasServoData()) {
1951
0
    MOZ_ASSERT(document);
1952
0
    MOZ_ASSERT(IsInAnonymousSubtree());
1953
0
  }
1954
0
1955
0
  if (document) {
1956
0
    ClearServoData(document);
1957
0
  }
1958
0
1959
0
  if (aNullParent) {
1960
0
    if (GetParent() && GetParent()->IsInUncomposedDoc()) {
1961
0
      // Update the editable descendant count in the ancestors before we
1962
0
      // lose the reference to the parent.
1963
0
      int32_t editableDescendantChange = -1 * EditableInclusiveDescendantCount(this);
1964
0
      if (editableDescendantChange != 0) {
1965
0
        nsIContent* parent = GetParent();
1966
0
        while (parent) {
1967
0
          parent->ChangeEditableDescendantCount(editableDescendantChange);
1968
0
          parent = parent->GetParent();
1969
0
        }
1970
0
      }
1971
0
    }
1972
0
1973
0
    if (IsRootOfNativeAnonymousSubtree()) {
1974
0
      nsNodeUtils::NativeAnonymousChildListChange(this, true);
1975
0
    }
1976
0
1977
0
    if (GetParent()) {
1978
0
      RefPtr<nsINode> p;
1979
0
      p.swap(mParent);
1980
0
    } else {
1981
0
      mParent = nullptr;
1982
0
    }
1983
0
    SetParentIsContent(false);
1984
0
  }
1985
0
1986
#ifdef DEBUG
1987
  // If we can get access to the PresContext, then we sanity-check that
1988
  // we're not leaving behind a pointer to ourselves as the PresContext's
1989
  // cached provider of the viewport's scrollbar styles.
1990
  if (document) {
1991
    nsPresContext* presContext = document->GetPresContext();
1992
    if (presContext) {
1993
      MOZ_ASSERT(this !=
1994
                 presContext->GetViewportScrollStylesOverrideElement(),
1995
                 "Leaving behind a raw pointer to this element (as having "
1996
                 "propagated scrollbar styles) - that's dangerous...");
1997
    }
1998
  }
1999
#endif
2000
2001
0
  ClearInDocument();
2002
0
  SetIsConnected(false);
2003
0
2004
0
  // Ensure that CSS transitions don't continue on an element at a
2005
0
  // different place in the tree (even if reinserted before next
2006
0
  // animation refresh).
2007
0
  // We need to delete the properties while we're still in document
2008
0
  // (if we were in document).
2009
0
  // FIXME (Bug 522599): Need a test for this.
2010
0
  if (MayHaveAnimations()) {
2011
0
    DeleteProperty(nsGkAtoms::transitionsOfBeforeProperty);
2012
0
    DeleteProperty(nsGkAtoms::transitionsOfAfterProperty);
2013
0
    DeleteProperty(nsGkAtoms::transitionsProperty);
2014
0
    DeleteProperty(nsGkAtoms::animationsOfBeforeProperty);
2015
0
    DeleteProperty(nsGkAtoms::animationsOfAfterProperty);
2016
0
    DeleteProperty(nsGkAtoms::animationsProperty);
2017
0
    if (document) {
2018
0
      if (nsPresContext* presContext = document->GetPresContext()) {
2019
0
        // We have to clear all pending restyle requests for the animations on
2020
0
        // this element to avoid unnecessary restyles when we re-attached this
2021
0
        // element.
2022
0
        presContext->EffectCompositor()->ClearRestyleRequestsFor(this);
2023
0
      }
2024
0
    }
2025
0
  }
2026
0
2027
0
  // Editable descendant count only counts descendants that
2028
0
  // are in the uncomposed document.
2029
0
  ResetEditableDescendantCount();
2030
0
2031
0
  if (aNullParent || !mParent->IsInShadowTree()) {
2032
0
    UnsetFlags(NODE_IS_IN_SHADOW_TREE);
2033
0
2034
0
    // Begin keeping track of our subtree root.
2035
0
    SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot());
2036
0
  }
2037
0
2038
0
  bool clearBindingParent = true;
2039
0
2040
0
#ifdef MOZ_XUL
2041
0
  if (nsXULElement* xulElem = nsXULElement::FromNode(this)) {;
2042
0
    xulElem->SetXULBindingParent(nullptr);
2043
0
    clearBindingParent = false;
2044
0
  }
2045
0
#endif
2046
0
2047
0
  if (nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
2048
0
    if (clearBindingParent) {
2049
0
      slots->mBindingParent = nullptr;
2050
0
    }
2051
0
    if (aNullParent || !mParent->IsInShadowTree()) {
2052
0
      slots->mContainingShadow = nullptr;
2053
0
    }
2054
0
  }
2055
0
2056
0
  if (document) {
2057
0
    if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
2058
0
      // Notify XBL- & nsIAnonymousContentCreator-generated anonymous content
2059
0
      // that the document is changing.
2060
0
      nsContentUtils::AddScriptRunner(
2061
0
        new RemoveFromBindingManagerRunnable(
2062
0
          document->BindingManager(), this, document));
2063
0
      nsXBLBinding* binding =
2064
0
        document->BindingManager()->GetBindingWithContent(this);
2065
0
      if (binding) {
2066
0
        nsXBLBinding::UnbindAnonymousContent(
2067
0
          document,
2068
0
          binding->GetAnonymousContent(),
2069
0
          /* aNullParent */ false);
2070
0
      }
2071
0
    }
2072
0
2073
0
    document->ClearBoxObjectFor(this);
2074
0
2075
0
     // Disconnected must be enqueued whenever a connected custom element becomes
2076
0
     // disconnected.
2077
0
    if (CustomElementRegistry::IsCustomElementEnabled(OwnerDoc())) {
2078
0
      CustomElementData* data  = GetCustomElementData();
2079
0
      if (data) {
2080
0
        if (data->mState == CustomElementData::State::eCustom) {
2081
0
          nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eDisconnected,
2082
0
                                                   this);
2083
0
        } else {
2084
0
          // Remove an unresolved custom element that is a candidate for upgrade
2085
0
          // when a custom element is disconnected.
2086
0
          nsContentUtils::UnregisterUnresolvedElement(this);
2087
0
        }
2088
0
      }
2089
0
    }
2090
0
  }
2091
0
2092
0
  // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree,
2093
0
  //  because it has to happen after unsetting the parent pointer, but before
2094
0
  //  recursively unbinding the kids.
2095
0
  if (IsHTMLElement()) {
2096
0
    ResetDir(this);
2097
0
  }
2098
0
2099
0
  if (aDeep) {
2100
0
    for (nsIContent* child = GetFirstChild(); child;
2101
0
         child = child->GetNextSibling()) {
2102
0
      // Note that we pass false for aNullParent here, since we don't want
2103
0
      // the kids to forget us.  We _do_ want them to forget their binding
2104
0
      // parent, though, since this only walks non-anonymous kids.
2105
0
      child->UnbindFromTree(true, false);
2106
0
    }
2107
0
  }
2108
0
2109
0
  nsNodeUtils::ParentChainChanged(this);
2110
0
2111
0
  // Unbind children of shadow root.
2112
0
  if (ShadowRoot* shadowRoot = GetShadowRoot()) {
2113
0
    shadowRoot->Unbind();
2114
0
  }
2115
0
2116
0
  MOZ_ASSERT(!HasAnyOfFlags(kAllServoDescendantBits));
2117
0
  MOZ_ASSERT(!document || document->GetServoRestyleRoot() != this);
2118
0
}
2119
2120
nsDOMCSSAttributeDeclaration*
2121
Element::SMILOverrideStyle()
2122
0
{
2123
0
  Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots();
2124
0
2125
0
  if (!slots->mSMILOverrideStyle) {
2126
0
    slots->mSMILOverrideStyle = new nsDOMCSSAttributeDeclaration(this, true);
2127
0
  }
2128
0
2129
0
  return slots->mSMILOverrideStyle;
2130
0
}
2131
2132
DeclarationBlock*
2133
Element::GetSMILOverrideStyleDeclaration()
2134
0
{
2135
0
  Element::nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
2136
0
  return slots ? slots->mSMILOverrideStyleDeclaration.get() : nullptr;
2137
0
}
2138
2139
nsresult
2140
Element::SetSMILOverrideStyleDeclaration(DeclarationBlock* aDeclaration,
2141
                                         bool aNotify)
2142
0
{
2143
0
  Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots();
2144
0
2145
0
  slots->mSMILOverrideStyleDeclaration = aDeclaration;
2146
0
2147
0
  if (aNotify) {
2148
0
    nsIDocument* doc = GetComposedDoc();
2149
0
    // Only need to request a restyle if we're in a document.  (We might not
2150
0
    // be in a document, if we're clearing animation effects on a target node
2151
0
    // that's been detached since the previous animation sample.)
2152
0
    if (doc) {
2153
0
      nsCOMPtr<nsIPresShell> shell = doc->GetShell();
2154
0
      if (shell) {
2155
0
        shell->RestyleForAnimation(this, eRestyle_StyleAttribute_Animations);
2156
0
      }
2157
0
    }
2158
0
  }
2159
0
2160
0
  return NS_OK;
2161
0
}
2162
2163
bool
2164
Element::IsLabelable() const
2165
0
{
2166
0
  return false;
2167
0
}
2168
2169
bool
2170
Element::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
2171
0
{
2172
0
  return false;
2173
0
}
2174
2175
DeclarationBlock*
2176
Element::GetInlineStyleDeclaration() const
2177
0
{
2178
0
  if (!MayHaveStyle()) {
2179
0
    return nullptr;
2180
0
  }
2181
0
  const nsAttrValue* attrVal = mAttrs.GetAttr(nsGkAtoms::style);
2182
0
2183
0
  if (attrVal && attrVal->Type() == nsAttrValue::eCSSDeclaration) {
2184
0
    return attrVal->GetCSSDeclarationValue();
2185
0
  }
2186
0
2187
0
  return nullptr;
2188
0
}
2189
2190
const nsMappedAttributes*
2191
Element::GetMappedAttributes() const
2192
0
{
2193
0
  return mAttrs.GetMapped();
2194
0
}
2195
2196
void
2197
Element::InlineStyleDeclarationWillChange(MutationClosureData& aData)
2198
0
{
2199
0
  MOZ_ASSERT_UNREACHABLE("Element::InlineStyleDeclarationWillChange");
2200
0
}
2201
2202
nsresult
2203
Element::SetInlineStyleDeclaration(DeclarationBlock& aDeclaration,
2204
                                   MutationClosureData& aData)
2205
0
{
2206
0
  MOZ_ASSERT_UNREACHABLE("Element::SetInlineStyleDeclaration");
2207
0
  return NS_ERROR_NOT_IMPLEMENTED;
2208
0
}
2209
2210
NS_IMETHODIMP_(bool)
2211
Element::IsAttributeMapped(const nsAtom* aAttribute) const
2212
0
{
2213
0
  return false;
2214
0
}
2215
2216
nsChangeHint
2217
Element::GetAttributeChangeHint(const nsAtom* aAttribute,
2218
                                int32_t aModType) const
2219
0
{
2220
0
  return nsChangeHint(0);
2221
0
}
2222
2223
bool
2224
Element::FindAttributeDependence(const nsAtom* aAttribute,
2225
                                 const MappedAttributeEntry* const aMaps[],
2226
                                 uint32_t aMapCount)
2227
0
{
2228
0
  for (uint32_t mapindex = 0; mapindex < aMapCount; ++mapindex) {
2229
0
    for (const MappedAttributeEntry* map = aMaps[mapindex];
2230
0
         map->attribute; ++map) {
2231
0
      if (aAttribute == *map->attribute) {
2232
0
        return true;
2233
0
      }
2234
0
    }
2235
0
  }
2236
0
2237
0
  return false;
2238
0
}
2239
2240
already_AddRefed<mozilla::dom::NodeInfo>
2241
Element::GetExistingAttrNameFromQName(const nsAString& aStr) const
2242
0
{
2243
0
  const nsAttrName* name = InternalGetAttrNameFromQName(aStr);
2244
0
  if (!name) {
2245
0
    return nullptr;
2246
0
  }
2247
0
2248
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
2249
0
  if (name->IsAtom()) {
2250
0
    nodeInfo = mNodeInfo->NodeInfoManager()->
2251
0
      GetNodeInfo(name->Atom(), nullptr, kNameSpaceID_None, ATTRIBUTE_NODE);
2252
0
  }
2253
0
  else {
2254
0
    nodeInfo = name->NodeInfo();
2255
0
  }
2256
0
2257
0
  return nodeInfo.forget();
2258
0
}
2259
2260
// static
2261
bool
2262
Element::ShouldBlur(nsIContent *aContent)
2263
0
{
2264
0
  // Determine if the current element is focused, if it is not focused
2265
0
  // then we should not try to blur
2266
0
  nsIDocument* document = aContent->GetComposedDoc();
2267
0
  if (!document)
2268
0
    return false;
2269
0
2270
0
  nsCOMPtr<nsPIDOMWindowOuter> window = document->GetWindow();
2271
0
  if (!window)
2272
0
    return false;
2273
0
2274
0
  nsCOMPtr<nsPIDOMWindowOuter> focusedFrame;
2275
0
  nsIContent* contentToBlur =
2276
0
    nsFocusManager::GetFocusedDescendant(window,
2277
0
                                         nsFocusManager::eOnlyCurrentWindow,
2278
0
                                         getter_AddRefs(focusedFrame));
2279
0
  if (contentToBlur == aContent)
2280
0
    return true;
2281
0
2282
0
  // if focus on this element would get redirected, then check the redirected
2283
0
  // content as well when blurring.
2284
0
  return (contentToBlur && nsFocusManager::GetRedirectedFocus(aContent) == contentToBlur);
2285
0
}
2286
2287
bool
2288
Element::IsNodeOfType(uint32_t aFlags) const
2289
0
{
2290
0
  return false;
2291
0
}
2292
2293
/* static */
2294
nsresult
2295
Element::DispatchEvent(nsPresContext* aPresContext,
2296
                       WidgetEvent* aEvent,
2297
                       nsIContent* aTarget,
2298
                       bool aFullDispatch,
2299
                       nsEventStatus* aStatus)
2300
0
{
2301
0
  MOZ_ASSERT(aTarget, "Must have target");
2302
0
  MOZ_ASSERT(aEvent, "Must have source event");
2303
0
  MOZ_ASSERT(aStatus, "Null out param?");
2304
0
2305
0
  if (!aPresContext) {
2306
0
    return NS_OK;
2307
0
  }
2308
0
2309
0
  nsCOMPtr<nsIPresShell> shell = aPresContext->GetPresShell();
2310
0
  if (!shell) {
2311
0
    return NS_OK;
2312
0
  }
2313
0
2314
0
  if (aFullDispatch) {
2315
0
    return shell->HandleEventWithTarget(aEvent, nullptr, aTarget, aStatus);
2316
0
  }
2317
0
2318
0
  return shell->HandleDOMEventWithTarget(aTarget, aEvent, aStatus);
2319
0
}
2320
2321
/* static */
2322
nsresult
2323
Element::DispatchClickEvent(nsPresContext* aPresContext,
2324
                            WidgetInputEvent* aSourceEvent,
2325
                            nsIContent* aTarget,
2326
                            bool aFullDispatch,
2327
                            const EventFlags* aExtraEventFlags,
2328
                            nsEventStatus* aStatus)
2329
0
{
2330
0
  MOZ_ASSERT(aTarget, "Must have target");
2331
0
  MOZ_ASSERT(aSourceEvent, "Must have source event");
2332
0
  MOZ_ASSERT(aStatus, "Null out param?");
2333
0
2334
0
  WidgetMouseEvent event(aSourceEvent->IsTrusted(), eMouseClick,
2335
0
                         aSourceEvent->mWidget, WidgetMouseEvent::eReal);
2336
0
  event.mRefPoint = aSourceEvent->mRefPoint;
2337
0
  uint32_t clickCount = 1;
2338
0
  float pressure = 0;
2339
0
  uint32_t pointerId = 0; // Use the default value here.
2340
0
  uint16_t inputSource = 0;
2341
0
  WidgetMouseEvent* sourceMouseEvent = aSourceEvent->AsMouseEvent();
2342
0
  if (sourceMouseEvent) {
2343
0
    clickCount = sourceMouseEvent->mClickCount;
2344
0
    pressure = sourceMouseEvent->pressure;
2345
0
    pointerId = sourceMouseEvent->pointerId;
2346
0
    inputSource = sourceMouseEvent->inputSource;
2347
0
  } else if (aSourceEvent->mClass == eKeyboardEventClass) {
2348
0
    event.mFlags.mIsPositionless = true;
2349
0
    inputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD;
2350
0
  }
2351
0
  event.pressure = pressure;
2352
0
  event.mClickCount = clickCount;
2353
0
  event.pointerId = pointerId;
2354
0
  event.inputSource = inputSource;
2355
0
  event.mModifiers = aSourceEvent->mModifiers;
2356
0
  if (aExtraEventFlags) {
2357
0
    // Be careful not to overwrite existing flags!
2358
0
    event.mFlags.Union(*aExtraEventFlags);
2359
0
  }
2360
0
2361
0
  return DispatchEvent(aPresContext, &event, aTarget, aFullDispatch, aStatus);
2362
0
}
2363
2364
nsIFrame*
2365
Element::GetPrimaryFrame(FlushType aType)
2366
0
{
2367
0
  nsIDocument* doc = GetComposedDoc();
2368
0
  if (!doc) {
2369
0
    return nullptr;
2370
0
  }
2371
0
2372
0
  // Cause a flush, so we get up-to-date frame
2373
0
  // information
2374
0
  if (aType != FlushType::None) {
2375
0
    doc->FlushPendingNotifications(aType);
2376
0
  }
2377
0
2378
0
  return GetPrimaryFrame();
2379
0
}
2380
2381
//----------------------------------------------------------------------
2382
nsresult
2383
Element::LeaveLink(nsPresContext* aPresContext)
2384
0
{
2385
0
  nsILinkHandler *handler = aPresContext->GetLinkHandler();
2386
0
  if (!handler) {
2387
0
    return NS_OK;
2388
0
  }
2389
0
2390
0
  return handler->OnLeaveLink();
2391
0
}
2392
2393
nsresult
2394
Element::SetEventHandler(nsAtom* aEventName,
2395
                         const nsAString& aValue,
2396
                         bool aDefer)
2397
0
{
2398
0
  nsIDocument *ownerDoc = OwnerDoc();
2399
0
  if (ownerDoc->IsLoadedAsData()) {
2400
0
    // Make this a no-op rather than throwing an error to avoid
2401
0
    // the error causing problems setting the attribute.
2402
0
    return NS_OK;
2403
0
  }
2404
0
2405
0
  MOZ_ASSERT(aEventName, "Must have event name!");
2406
0
  bool defer = true;
2407
0
  EventListenerManager* manager =
2408
0
    GetEventListenerManagerForAttr(aEventName, &defer);
2409
0
  if (!manager) {
2410
0
    return NS_OK;
2411
0
  }
2412
0
2413
0
  defer = defer && aDefer; // only defer if everyone agrees...
2414
0
  manager->SetEventHandler(aEventName, aValue,
2415
0
                           defer, !nsContentUtils::IsChromeDoc(ownerDoc),
2416
0
                           this);
2417
0
  return NS_OK;
2418
0
}
2419
2420
2421
//----------------------------------------------------------------------
2422
2423
const nsAttrName*
2424
Element::InternalGetAttrNameFromQName(const nsAString& aStr,
2425
                                      nsAutoString* aNameToUse) const
2426
0
{
2427
0
  MOZ_ASSERT(!aNameToUse || aNameToUse->IsEmpty());
2428
0
  const nsAttrName* val = nullptr;
2429
0
  if (IsHTMLElement() && IsInHTMLDocument()) {
2430
0
    nsAutoString lower;
2431
0
    nsAutoString& outStr = aNameToUse ? *aNameToUse : lower;
2432
0
    nsContentUtils::ASCIIToLower(aStr, outStr);
2433
0
    val = mAttrs.GetExistingAttrNameFromQName(outStr);
2434
0
    if (val) {
2435
0
      outStr.Truncate();
2436
0
    }
2437
0
  } else {
2438
0
    val = mAttrs.GetExistingAttrNameFromQName(aStr);
2439
0
    if (!val && aNameToUse) {
2440
0
      *aNameToUse = aStr;
2441
0
    }
2442
0
  }
2443
0
2444
0
  return val;
2445
0
}
2446
2447
bool
2448
Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
2449
                               nsAtom* aName,
2450
                               nsAtom* aPrefix,
2451
                               const nsAttrValueOrString& aValue,
2452
                               bool aNotify,
2453
                               nsAttrValue& aOldValue,
2454
                               uint8_t* aModType,
2455
                               bool* aHasListeners,
2456
                               bool* aOldValueSet)
2457
0
{
2458
0
  bool modification = false;
2459
0
  *aHasListeners = aNotify &&
2460
0
    nsContentUtils::HasMutationListeners(this,
2461
0
                                         NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
2462
0
                                         this);
2463
0
  *aOldValueSet = false;
2464
0
2465
0
  // If we have no listeners and aNotify is false, we are almost certainly
2466
0
  // coming from the content sink and will almost certainly have no previous
2467
0
  // value.  Even if we do, setting the value is cheap when we have no
2468
0
  // listeners and don't plan to notify.  The check for aNotify here is an
2469
0
  // optimization, the check for *aHasListeners is a correctness issue.
2470
0
  if (*aHasListeners || aNotify) {
2471
0
    BorrowedAttrInfo info(GetAttrInfo(aNamespaceID, aName));
2472
0
    if (info.mValue) {
2473
0
      // Check whether the old value is the same as the new one.  Note that we
2474
0
      // only need to actually _get_ the old value if we have listeners or
2475
0
      // if the element is a custom element (because it may have an
2476
0
      // attribute changed callback).
2477
0
      if (*aHasListeners || GetCustomElementData()) {
2478
0
        // Need to store the old value.
2479
0
        //
2480
0
        // If the current attribute value contains a pointer to some other data
2481
0
        // structure that gets updated in the process of setting the attribute
2482
0
        // we'll no longer have the old value of the attribute. Therefore, we
2483
0
        // should serialize the attribute value now to keep a snapshot.
2484
0
        //
2485
0
        // We have to serialize the value anyway in order to create the
2486
0
        // mutation event so there's no cost in doing it now.
2487
0
        aOldValue.SetToSerialized(*info.mValue);
2488
0
        *aOldValueSet = true;
2489
0
      }
2490
0
      bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
2491
0
      if (valueMatches && aPrefix == info.mName->GetPrefix()) {
2492
0
        return true;
2493
0
      }
2494
0
      modification = true;
2495
0
    }
2496
0
  }
2497
0
  *aModType = modification ?
2498
0
    static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION) :
2499
0
    static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
2500
0
  return false;
2501
0
}
2502
2503
bool
2504
Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsAtom* aName,
2505
                                nsAtom* aPrefix,
2506
                                const nsAttrValueOrString& aValue,
2507
                                bool aNotify, nsAttrValue& aOldValue,
2508
                                uint8_t* aModType, bool* aHasListeners,
2509
                                bool* aOldValueSet)
2510
0
{
2511
0
  if (!MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, aValue, aNotify,
2512
0
                             aOldValue, aModType, aHasListeners,
2513
0
                             aOldValueSet)) {
2514
0
    return false;
2515
0
  }
2516
0
2517
0
  nsAutoScriptBlocker scriptBlocker;
2518
0
  nsNodeUtils::AttributeSetToCurrentValue(this, aNamespaceID, aName);
2519
0
  return true;
2520
0
}
2521
2522
nsresult
2523
Element::SetSingleClassFromParser(nsAtom* aSingleClassName)
2524
0
{
2525
0
  // Keep this in sync with SetAttr and SetParsedAttr below.
2526
0
2527
0
  nsAttrValue value(aSingleClassName);
2528
0
2529
0
  nsIDocument* document = GetComposedDoc();
2530
0
  mozAutoDocUpdate updateBatch(document, false);
2531
0
2532
0
  // In principle, BeforeSetAttr should be called here if a node type
2533
0
  // existed that wanted to do something special for class, but there
2534
0
  // is no such node type, so calling SetMayHaveClass() directly.
2535
0
  SetMayHaveClass();
2536
0
2537
0
  return SetAttrAndNotify(kNameSpaceID_None,
2538
0
                          nsGkAtoms::_class,
2539
0
                          nullptr, // prefix
2540
0
                          nullptr, // old value
2541
0
                          value,
2542
0
                          nullptr,
2543
0
                          static_cast<uint8_t>(MutationEvent_Binding::ADDITION),
2544
0
                          false, // hasListeners
2545
0
                          false, // notify
2546
0
                          kCallAfterSetAttr,
2547
0
                          document,
2548
0
                          updateBatch);
2549
0
}
2550
2551
nsresult
2552
Element::SetAttr(int32_t aNamespaceID, nsAtom* aName,
2553
                 nsAtom* aPrefix, const nsAString& aValue,
2554
                 nsIPrincipal* aSubjectPrincipal,
2555
                 bool aNotify)
2556
0
{
2557
0
  // Keep this in sync with SetParsedAttr below and SetSingleClassFromParser
2558
0
  // above.
2559
0
2560
0
  NS_ENSURE_ARG_POINTER(aName);
2561
0
  NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
2562
0
               "Don't call SetAttr with unknown namespace");
2563
0
2564
0
  uint8_t modType;
2565
0
  bool hasListeners;
2566
0
  // We don't want to spend time preparsing class attributes if the value is not
2567
0
  // changing, so just init our nsAttrValueOrString with aValue for the
2568
0
  // OnlyNotifySameValueSet call.
2569
0
  nsAttrValueOrString value(aValue);
2570
0
  nsAttrValue oldValue;
2571
0
  bool oldValueSet;
2572
0
2573
0
  if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
2574
0
                             oldValue, &modType, &hasListeners, &oldValueSet)) {
2575
0
    return OnAttrSetButNotChanged(aNamespaceID, aName, value, aNotify);
2576
0
  }
2577
0
2578
0
  nsAttrValue attrValue;
2579
0
  nsAttrValue* preparsedAttrValue;
2580
0
  if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::_class) {
2581
0
    attrValue.ParseAtomArray(aValue);
2582
0
    value.ResetToAttrValue(attrValue);
2583
0
    preparsedAttrValue = &attrValue;
2584
0
  } else {
2585
0
    preparsedAttrValue = nullptr;
2586
0
  }
2587
0
2588
0
  if (aNotify) {
2589
0
    nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType,
2590
0
                                     preparsedAttrValue);
2591
0
  }
2592
0
2593
0
  // Hold a script blocker while calling ParseAttribute since that can call
2594
0
  // out to id-observers
2595
0
  nsIDocument* document = GetComposedDoc();
2596
0
  mozAutoDocUpdate updateBatch(document, aNotify);
2597
0
2598
0
  nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
2599
0
  NS_ENSURE_SUCCESS(rv, rv);
2600
0
2601
0
  if (!preparsedAttrValue &&
2602
0
      !ParseAttribute(aNamespaceID, aName, aValue, aSubjectPrincipal,
2603
0
                      attrValue)) {
2604
0
    attrValue.SetTo(aValue);
2605
0
  }
2606
0
2607
0
  PreIdMaybeChange(aNamespaceID, aName, &value);
2608
0
2609
0
  return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
2610
0
                          oldValueSet ? &oldValue : nullptr,
2611
0
                          attrValue, aSubjectPrincipal, modType,
2612
0
                          hasListeners, aNotify,
2613
0
                          kCallAfterSetAttr, document, updateBatch);
2614
0
}
2615
2616
nsresult
2617
Element::SetParsedAttr(int32_t aNamespaceID, nsAtom* aName,
2618
                       nsAtom* aPrefix, nsAttrValue& aParsedValue,
2619
                       bool aNotify)
2620
0
{
2621
0
  // Keep this in sync with SetAttr and SetSingleClassFromParser above
2622
0
2623
0
  NS_ENSURE_ARG_POINTER(aName);
2624
0
  NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
2625
0
               "Don't call SetAttr with unknown namespace");
2626
0
2627
0
  uint8_t modType;
2628
0
  bool hasListeners;
2629
0
  nsAttrValueOrString value(aParsedValue);
2630
0
  nsAttrValue oldValue;
2631
0
  bool oldValueSet;
2632
0
2633
0
  if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
2634
0
                             oldValue, &modType, &hasListeners, &oldValueSet)) {
2635
0
    return OnAttrSetButNotChanged(aNamespaceID, aName, value, aNotify);
2636
0
  }
2637
0
2638
0
  if (aNotify) {
2639
0
    nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType,
2640
0
                                     &aParsedValue);
2641
0
  }
2642
0
2643
0
  nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
2644
0
  NS_ENSURE_SUCCESS(rv, rv);
2645
0
2646
0
  PreIdMaybeChange(aNamespaceID, aName, &value);
2647
0
2648
0
  nsIDocument* document = GetComposedDoc();
2649
0
  mozAutoDocUpdate updateBatch(document, aNotify);
2650
0
  return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
2651
0
                          oldValueSet ? &oldValue : nullptr,
2652
0
                          aParsedValue, nullptr, modType, hasListeners, aNotify,
2653
0
                          kCallAfterSetAttr, document, updateBatch);
2654
0
}
2655
2656
nsresult
2657
Element::SetAttrAndNotify(int32_t aNamespaceID,
2658
                          nsAtom* aName,
2659
                          nsAtom* aPrefix,
2660
                          const nsAttrValue* aOldValue,
2661
                          nsAttrValue& aParsedValue,
2662
                          nsIPrincipal* aSubjectPrincipal,
2663
                          uint8_t aModType,
2664
                          bool aFireMutation,
2665
                          bool aNotify,
2666
                          bool aCallAfterSetAttr,
2667
                          nsIDocument* aComposedDocument,
2668
                          const mozAutoDocUpdate&)
2669
0
{
2670
0
  nsresult rv;
2671
0
  nsMutationGuard::DidMutate();
2672
0
2673
0
  // Copy aParsedValue for later use since it will be lost when we call
2674
0
  // SetAndSwapMappedAttr below
2675
0
  nsAttrValue valueForAfterSetAttr;
2676
0
  if (aCallAfterSetAttr || GetCustomElementData()) {
2677
0
    valueForAfterSetAttr.SetTo(aParsedValue);
2678
0
  }
2679
0
2680
0
  bool hadValidDir = false;
2681
0
  bool hadDirAuto = false;
2682
0
  bool oldValueSet;
2683
0
2684
0
  if (aNamespaceID == kNameSpaceID_None) {
2685
0
    if (aName == nsGkAtoms::dir) {
2686
0
      hadValidDir = HasValidDir() || IsHTMLElement(nsGkAtoms::bdi);
2687
0
      hadDirAuto = HasDirAuto(); // already takes bdi into account
2688
0
    }
2689
0
2690
0
    // XXXbz Perhaps we should push up the attribute mapping function
2691
0
    // stuff to Element?
2692
0
    if (!IsAttributeMapped(aName) ||
2693
0
        !SetAndSwapMappedAttribute(aName, aParsedValue, &oldValueSet, &rv)) {
2694
0
      rv = mAttrs.SetAndSwapAttr(aName, aParsedValue, &oldValueSet);
2695
0
    }
2696
0
  }
2697
0
  else {
2698
0
    RefPtr<mozilla::dom::NodeInfo> ni;
2699
0
    ni = mNodeInfo->NodeInfoManager()->GetNodeInfo(aName, aPrefix,
2700
0
                                                   aNamespaceID,
2701
0
                                                   ATTRIBUTE_NODE);
2702
0
2703
0
    rv = mAttrs.SetAndSwapAttr(ni, aParsedValue, &oldValueSet);
2704
0
  }
2705
0
  NS_ENSURE_SUCCESS(rv, rv);
2706
0
2707
0
  PostIdMaybeChange(aNamespaceID, aName, &valueForAfterSetAttr);
2708
0
2709
0
  // If the old value owns its own data, we know it is OK to keep using it.
2710
0
  // oldValue will be null if there was no previously set value
2711
0
  const nsAttrValue* oldValue;
2712
0
  if (aParsedValue.StoresOwnData()) {
2713
0
    if (oldValueSet) {
2714
0
      oldValue = &aParsedValue;
2715
0
    } else {
2716
0
      oldValue = nullptr;
2717
0
    }
2718
0
  } else {
2719
0
    // No need to conditionally assign null here. If there was no previously
2720
0
    // set value for the attribute, aOldValue will already be null.
2721
0
    oldValue = aOldValue;
2722
0
  }
2723
0
2724
0
  if (aComposedDocument) {
2725
0
    RefPtr<nsXBLBinding> binding = GetXBLBinding();
2726
0
    if (binding) {
2727
0
      binding->AttributeChanged(aName, aNamespaceID, false, aNotify);
2728
0
    }
2729
0
  }
2730
0
2731
0
  if (CustomElementRegistry::IsCustomElementEnabled(OwnerDoc())) {
2732
0
    CustomElementDefinition* definition = GetCustomElementDefinition();
2733
0
    // Only custom element which is in `custom` state could get the
2734
0
    // CustomElementDefinition.
2735
0
    if (definition && definition->IsInObservedAttributeList(aName)) {
2736
0
      RefPtr<nsAtom> oldValueAtom;
2737
0
      if (oldValue) {
2738
0
        oldValueAtom = oldValue->GetAsAtom();
2739
0
      } else {
2740
0
        // If there is no old value, get the value of the uninitialized
2741
0
        // attribute that was swapped with aParsedValue.
2742
0
        oldValueAtom = aParsedValue.GetAsAtom();
2743
0
      }
2744
0
      RefPtr<nsAtom> newValueAtom = valueForAfterSetAttr.GetAsAtom();
2745
0
      nsAutoString ns;
2746
0
      nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
2747
0
2748
0
      LifecycleCallbackArgs args = {
2749
0
        nsDependentAtomString(aName),
2750
0
        aModType == MutationEvent_Binding::ADDITION ?
2751
0
          VoidString() : nsDependentAtomString(oldValueAtom),
2752
0
        nsDependentAtomString(newValueAtom),
2753
0
        (ns.IsEmpty() ? VoidString() : ns)
2754
0
      };
2755
0
2756
0
      nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
2757
0
        this, &args, nullptr, definition);
2758
0
    }
2759
0
  }
2760
0
2761
0
  if (aCallAfterSetAttr) {
2762
0
    rv = AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, oldValue,
2763
0
                      aSubjectPrincipal, aNotify);
2764
0
    NS_ENSURE_SUCCESS(rv, rv);
2765
0
2766
0
    if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
2767
0
      OnSetDirAttr(this, &valueForAfterSetAttr,
2768
0
                   hadValidDir, hadDirAuto, aNotify);
2769
0
    }
2770
0
  }
2771
0
2772
0
  UpdateState(aNotify);
2773
0
2774
0
  if (aNotify) {
2775
0
    // Don't pass aOldValue to AttributeChanged since it may not be reliable.
2776
0
    // Callers only compute aOldValue under certain conditions which may not
2777
0
    // be triggered by all nsIMutationObservers.
2778
0
    nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType,
2779
0
        aParsedValue.StoresOwnData() ? &aParsedValue : nullptr);
2780
0
  }
2781
0
2782
0
  if (aFireMutation) {
2783
0
    InternalMutationEvent mutation(true, eLegacyAttrModified);
2784
0
2785
0
    nsAutoString ns;
2786
0
    nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
2787
0
    Attr* attrNode =
2788
0
      GetAttributeNodeNSInternal(ns, nsDependentAtomString(aName));
2789
0
    mutation.mRelatedNode = attrNode;
2790
0
2791
0
    mutation.mAttrName = aName;
2792
0
    nsAutoString newValue;
2793
0
    GetAttr(aNamespaceID, aName, newValue);
2794
0
    if (!newValue.IsEmpty()) {
2795
0
      mutation.mNewAttrValue = NS_Atomize(newValue);
2796
0
    }
2797
0
    if (oldValue && !oldValue->IsEmptyString()) {
2798
0
      mutation.mPrevAttrValue = oldValue->GetAsAtom();
2799
0
    }
2800
0
    mutation.mAttrChange = aModType;
2801
0
2802
0
    mozAutoSubtreeModified subtree(OwnerDoc(), this);
2803
0
    (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe();
2804
0
  }
2805
0
2806
0
  return NS_OK;
2807
0
}
2808
2809
bool
2810
Element::ParseAttribute(int32_t aNamespaceID,
2811
                        nsAtom* aAttribute,
2812
                        const nsAString& aValue,
2813
                        nsIPrincipal* aMaybeScriptedPrincipal,
2814
                        nsAttrValue& aResult)
2815
0
{
2816
0
  if (aAttribute == nsGkAtoms::lang) {
2817
0
    aResult.ParseAtom(aValue);
2818
0
    return true;
2819
0
  }
2820
0
2821
0
  if (aNamespaceID == kNameSpaceID_None) {
2822
0
    MOZ_ASSERT(aAttribute != nsGkAtoms::_class,
2823
0
               "The class attribute should be preparsed and therefore should "
2824
0
               "never be passed to Element::ParseAttribute");
2825
0
    if (aAttribute == nsGkAtoms::id) {
2826
0
      // Store id as an atom.  id="" means that the element has no id,
2827
0
      // not that it has an emptystring as the id.
2828
0
      if (aValue.IsEmpty()) {
2829
0
        return false;
2830
0
      }
2831
0
      aResult.ParseAtom(aValue);
2832
0
      return true;
2833
0
    }
2834
0
  }
2835
0
2836
0
  return false;
2837
0
}
2838
2839
bool
2840
Element::SetAndSwapMappedAttribute(nsAtom* aName,
2841
                                   nsAttrValue& aValue,
2842
                                   bool* aValueWasSet,
2843
                                   nsresult* aRetval)
2844
0
{
2845
0
  *aRetval = NS_OK;
2846
0
  return false;
2847
0
}
2848
2849
nsresult
2850
Element::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
2851
                       const nsAttrValueOrString* aValue, bool aNotify)
2852
0
{
2853
0
  if (aNamespaceID == kNameSpaceID_None) {
2854
0
    if (aName == nsGkAtoms::_class) {
2855
0
      if (aValue) {
2856
0
        // Note: This flag is asymmetrical. It is never unset and isn't exact.
2857
0
        // If it is ever made to be exact, we probably need to handle this
2858
0
        // similarly to how ids are handled in PreIdMaybeChange and
2859
0
        // PostIdMaybeChange.
2860
0
        // Note that SetSingleClassFromParser inlines BeforeSetAttr and
2861
0
        // calls SetMayHaveClass directly. Making a subclass take action
2862
0
        // on the class attribute in a BeforeSetAttr override would
2863
0
        // require revising SetSingleClassFromParser.
2864
0
        SetMayHaveClass();
2865
0
      }
2866
0
    }
2867
0
  }
2868
0
2869
0
  return NS_OK;
2870
0
}
2871
2872
void
2873
Element::PreIdMaybeChange(int32_t aNamespaceID, nsAtom* aName,
2874
                          const nsAttrValueOrString* aValue)
2875
0
{
2876
0
  if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) {
2877
0
    return;
2878
0
  }
2879
0
  RemoveFromIdTable();
2880
0
}
2881
2882
void
2883
Element::PostIdMaybeChange(int32_t aNamespaceID, nsAtom* aName,
2884
                           const nsAttrValue* aValue)
2885
0
{
2886
0
  if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) {
2887
0
    return;
2888
0
  }
2889
0
2890
0
  // id="" means that the element has no id, not that it has an empty
2891
0
  // string as the id.
2892
0
  if (aValue && !aValue->IsEmptyString()) {
2893
0
    SetHasID();
2894
0
    AddToIdTable(aValue->GetAtomValue());
2895
0
  } else {
2896
0
    ClearHasID();
2897
0
  }
2898
0
}
2899
2900
nsresult
2901
Element::OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
2902
                                const nsAttrValueOrString& aValue,
2903
                                bool aNotify)
2904
0
{
2905
0
  if (CustomElementRegistry::IsCustomElementEnabled(OwnerDoc())) {
2906
0
    // Only custom element which is in `custom` state could get the
2907
0
    // CustomElementDefinition.
2908
0
    CustomElementDefinition* definition = GetCustomElementDefinition();
2909
0
    if (definition && definition->IsInObservedAttributeList(aName)) {
2910
0
      nsAutoString ns;
2911
0
      nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
2912
0
2913
0
      nsAutoString value(aValue.String());
2914
0
      LifecycleCallbackArgs args = {
2915
0
        nsDependentAtomString(aName),
2916
0
        value,
2917
0
        value,
2918
0
        (ns.IsEmpty() ? VoidString() : ns)
2919
0
      };
2920
0
2921
0
      nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
2922
0
        this, &args, nullptr, definition);
2923
0
    }
2924
0
  }
2925
0
2926
0
  return NS_OK;
2927
0
}
2928
2929
EventListenerManager*
2930
Element::GetEventListenerManagerForAttr(nsAtom* aAttrName,
2931
                                        bool* aDefer)
2932
0
{
2933
0
  *aDefer = true;
2934
0
  return GetOrCreateListenerManager();
2935
0
}
2936
2937
bool
2938
Element::GetAttr(int32_t aNameSpaceID, nsAtom* aName,
2939
                 nsAString& aResult) const
2940
0
{
2941
0
  DOMString str;
2942
0
  bool haveAttr = GetAttr(aNameSpaceID, aName, str);
2943
0
  str.ToString(aResult);
2944
0
  return haveAttr;
2945
0
}
2946
2947
int32_t
2948
Element::FindAttrValueIn(int32_t aNameSpaceID,
2949
                         nsAtom* aName,
2950
                         AttrValuesArray* aValues,
2951
                         nsCaseTreatment aCaseSensitive) const
2952
0
{
2953
0
  NS_ASSERTION(aName, "Must have attr name");
2954
0
  NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown, "Must have namespace");
2955
0
  NS_ASSERTION(aValues, "Null value array");
2956
0
2957
0
  const nsAttrValue* val = mAttrs.GetAttr(aName, aNameSpaceID);
2958
0
  if (val) {
2959
0
    for (int32_t i = 0; aValues[i]; ++i) {
2960
0
      if (val->Equals(*aValues[i], aCaseSensitive)) {
2961
0
        return i;
2962
0
      }
2963
0
    }
2964
0
    return ATTR_VALUE_NO_MATCH;
2965
0
  }
2966
0
  return ATTR_MISSING;
2967
0
}
2968
2969
nsresult
2970
Element::UnsetAttr(int32_t aNameSpaceID, nsAtom* aName,
2971
                   bool aNotify)
2972
0
{
2973
0
  NS_ASSERTION(nullptr != aName, "must have attribute name");
2974
0
2975
0
  int32_t index = mAttrs.IndexOfAttr(aName, aNameSpaceID);
2976
0
  if (index < 0) {
2977
0
    return NS_OK;
2978
0
  }
2979
0
2980
0
  nsIDocument *document = GetComposedDoc();
2981
0
  mozAutoDocUpdate updateBatch(document, aNotify);
2982
0
2983
0
  if (aNotify) {
2984
0
    nsNodeUtils::AttributeWillChange(this, aNameSpaceID, aName,
2985
0
                                     MutationEvent_Binding::REMOVAL,
2986
0
                                     nullptr);
2987
0
  }
2988
0
2989
0
  nsresult rv = BeforeSetAttr(aNameSpaceID, aName, nullptr, aNotify);
2990
0
  NS_ENSURE_SUCCESS(rv, rv);
2991
0
2992
0
  bool hasMutationListeners = aNotify &&
2993
0
    nsContentUtils::HasMutationListeners(this,
2994
0
                                         NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
2995
0
                                         this);
2996
0
2997
0
  PreIdMaybeChange(aNameSpaceID, aName, nullptr);
2998
0
2999
0
  // Grab the attr node if needed before we remove it from the attr map
3000
0
  RefPtr<Attr> attrNode;
3001
0
  if (hasMutationListeners) {
3002
0
    nsAutoString ns;
3003
0
    nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, ns);
3004
0
    attrNode = GetAttributeNodeNSInternal(ns, nsDependentAtomString(aName));
3005
0
  }
3006
0
3007
0
  // Clear the attribute out from attribute map.
3008
0
  nsDOMSlots *slots = GetExistingDOMSlots();
3009
0
  if (slots && slots->mAttributeMap) {
3010
0
    slots->mAttributeMap->DropAttribute(aNameSpaceID, aName);
3011
0
  }
3012
0
3013
0
  // The id-handling code, and in the future possibly other code, need to
3014
0
  // react to unexpected attribute changes.
3015
0
  nsMutationGuard::DidMutate();
3016
0
3017
0
  bool hadValidDir = false;
3018
0
  bool hadDirAuto = false;
3019
0
3020
0
  if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
3021
0
    hadValidDir = HasValidDir() || IsHTMLElement(nsGkAtoms::bdi);
3022
0
    hadDirAuto = HasDirAuto(); // already takes bdi into account
3023
0
  }
3024
0
3025
0
  nsAttrValue oldValue;
3026
0
  rv = mAttrs.RemoveAttrAt(index, oldValue);
3027
0
  NS_ENSURE_SUCCESS(rv, rv);
3028
0
3029
0
  PostIdMaybeChange(aNameSpaceID, aName, nullptr);
3030
0
3031
0
  if (document) {
3032
0
    RefPtr<nsXBLBinding> binding = GetXBLBinding();
3033
0
    if (binding) {
3034
0
      binding->AttributeChanged(aName, aNameSpaceID, true, aNotify);
3035
0
    }
3036
0
  }
3037
0
3038
0
  if (CustomElementRegistry::IsCustomElementEnabled(OwnerDoc())) {
3039
0
    CustomElementDefinition* definition = GetCustomElementDefinition();
3040
0
    // Only custom element which is in `custom` state could get the
3041
0
    // CustomElementDefinition.
3042
0
    if (definition && definition->IsInObservedAttributeList(aName)) {
3043
0
      nsAutoString ns;
3044
0
      nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, ns);
3045
0
3046
0
      RefPtr<nsAtom> oldValueAtom = oldValue.GetAsAtom();
3047
0
      LifecycleCallbackArgs args = {
3048
0
        nsDependentAtomString(aName),
3049
0
        nsDependentAtomString(oldValueAtom),
3050
0
        VoidString(),
3051
0
        (ns.IsEmpty() ? VoidString() : ns)
3052
0
      };
3053
0
3054
0
      nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
3055
0
        this, &args, nullptr, definition);
3056
0
    }
3057
0
  }
3058
0
3059
0
  rv = AfterSetAttr(aNameSpaceID, aName, nullptr, &oldValue, nullptr, aNotify);
3060
0
  NS_ENSURE_SUCCESS(rv, rv);
3061
0
3062
0
  UpdateState(aNotify);
3063
0
3064
0
  if (aNotify) {
3065
0
    // We can always pass oldValue here since there is no new value which could
3066
0
    // have corrupted it.
3067
0
    nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
3068
0
                                  MutationEvent_Binding::REMOVAL, &oldValue);
3069
0
  }
3070
0
3071
0
  if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
3072
0
    OnSetDirAttr(this, nullptr, hadValidDir, hadDirAuto, aNotify);
3073
0
  }
3074
0
3075
0
  if (hasMutationListeners) {
3076
0
    InternalMutationEvent mutation(true, eLegacyAttrModified);
3077
0
3078
0
    mutation.mRelatedNode = attrNode;
3079
0
    mutation.mAttrName = aName;
3080
0
3081
0
    nsAutoString value;
3082
0
    oldValue.ToString(value);
3083
0
    if (!value.IsEmpty())
3084
0
      mutation.mPrevAttrValue = NS_Atomize(value);
3085
0
    mutation.mAttrChange = MutationEvent_Binding::REMOVAL;
3086
0
3087
0
    mozAutoSubtreeModified subtree(OwnerDoc(), this);
3088
0
    (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe();
3089
0
  }
3090
0
3091
0
  return NS_OK;
3092
0
}
3093
3094
void
3095
Element::DescribeAttribute(uint32_t index, nsAString& aOutDescription) const
3096
0
{
3097
0
  // name
3098
0
  mAttrs.AttrNameAt(index)->GetQualifiedName(aOutDescription);
3099
0
3100
0
  // value
3101
0
  aOutDescription.AppendLiteral("=\"");
3102
0
  nsAutoString value;
3103
0
  mAttrs.AttrAt(index)->ToString(value);
3104
0
  for (uint32_t i = value.Length(); i > 0; --i) {
3105
0
    if (value[i - 1] == char16_t('"'))
3106
0
      value.Insert(char16_t('\\'), i - 1);
3107
0
  }
3108
0
  aOutDescription.Append(value);
3109
0
  aOutDescription.Append('"');
3110
0
}
3111
3112
#ifdef DEBUG
3113
void
3114
Element::ListAttributes(FILE* out) const
3115
{
3116
  uint32_t index, count = mAttrs.AttrCount();
3117
  for (index = 0; index < count; index++) {
3118
    nsAutoString attributeDescription;
3119
    DescribeAttribute(index, attributeDescription);
3120
3121
    fputs(" ", out);
3122
    fputs(NS_LossyConvertUTF16toASCII(attributeDescription).get(), out);
3123
  }
3124
}
3125
3126
void
3127
Element::List(FILE* out, int32_t aIndent,
3128
              const nsCString& aPrefix) const
3129
{
3130
  int32_t indent;
3131
  for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
3132
3133
  fputs(aPrefix.get(), out);
3134
3135
  fputs(NS_LossyConvertUTF16toASCII(mNodeInfo->QualifiedName()).get(), out);
3136
3137
  fprintf(out, "@%p", (void *)this);
3138
3139
  ListAttributes(out);
3140
3141
  fprintf(out, " state=[%llx]",
3142
          static_cast<unsigned long long>(State().GetInternalValue()));
3143
  fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
3144
  if (IsCommonAncestorForRangeInSelection()) {
3145
    const LinkedList<nsRange>* ranges = GetExistingCommonAncestorRanges();
3146
    int32_t count = 0;
3147
    if (ranges) {
3148
      // Can't use range-based iteration on a const LinkedList, unfortunately.
3149
      for (const nsRange* r = ranges->getFirst(); r; r = r->getNext()) {
3150
        ++count;
3151
      }
3152
    }
3153
    fprintf(out, " ranges:%d", count);
3154
  }
3155
  fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));
3156
  fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get());
3157
3158
  nsIContent* child = GetFirstChild();
3159
  if (child) {
3160
    fputs("\n", out);
3161
3162
    for (; child; child = child->GetNextSibling()) {
3163
      child->List(out, aIndent + 1);
3164
    }
3165
3166
    for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
3167
  }
3168
3169
  fputs(">\n", out);
3170
3171
  Element* nonConstThis = const_cast<Element*>(this);
3172
3173
  // XXX sXBL/XBL2 issue! Owner or current document?
3174
  nsIDocument *document = OwnerDoc();
3175
3176
  // Note: not listing nsIAnonymousContentCreator-created content...
3177
3178
  nsBindingManager* bindingManager = document->BindingManager();
3179
  nsINodeList* anonymousChildren =
3180
    bindingManager->GetAnonymousNodesFor(nonConstThis);
3181
3182
  if (anonymousChildren) {
3183
    uint32_t length = anonymousChildren->Length();
3184
3185
    for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
3186
    fputs("anonymous-children<\n", out);
3187
3188
    for (uint32_t i = 0; i < length; ++i) {
3189
      nsIContent* child = anonymousChildren->Item(i);
3190
      child->List(out, aIndent + 1);
3191
    }
3192
3193
    for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
3194
    fputs(">\n", out);
3195
3196
    bool outHeader = false;
3197
    ExplicitChildIterator iter(nonConstThis);
3198
    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
3199
      if (!outHeader) {
3200
        outHeader = true;
3201
3202
        for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
3203
        fputs("content-list<\n", out);
3204
      }
3205
3206
      child->List(out, aIndent + 1);
3207
    }
3208
3209
    if (outHeader) {
3210
      for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
3211
      fputs(">\n", out);
3212
    }
3213
  }
3214
}
3215
3216
void
3217
Element::DumpContent(FILE* out, int32_t aIndent,
3218
                     bool aDumpAll) const
3219
{
3220
  int32_t indent;
3221
  for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
3222
3223
  const nsString& buf = mNodeInfo->QualifiedName();
3224
  fputs("<", out);
3225
  fputs(NS_LossyConvertUTF16toASCII(buf).get(), out);
3226
3227
  if(aDumpAll) ListAttributes(out);
3228
3229
  fputs(">", out);
3230
3231
  if(aIndent) fputs("\n", out);
3232
3233
  for (nsIContent* child = GetFirstChild();
3234
       child;
3235
       child = child->GetNextSibling()) {
3236
    int32_t indent = aIndent ? aIndent + 1 : 0;
3237
    child->DumpContent(out, indent, aDumpAll);
3238
  }
3239
  for (indent = aIndent; --indent >= 0; ) fputs("  ", out);
3240
  fputs("</", out);
3241
  fputs(NS_LossyConvertUTF16toASCII(buf).get(), out);
3242
  fputs(">", out);
3243
3244
  if(aIndent) fputs("\n", out);
3245
}
3246
#endif
3247
3248
void
3249
Element::Describe(nsAString& aOutDescription) const
3250
0
{
3251
0
  aOutDescription.Append(mNodeInfo->QualifiedName());
3252
0
  aOutDescription.AppendPrintf("@%p", (void *)this);
3253
0
3254
0
  uint32_t index, count = mAttrs.AttrCount();
3255
0
  for (index = 0; index < count; index++) {
3256
0
    aOutDescription.Append(' ');
3257
0
    nsAutoString attributeDescription;
3258
0
    DescribeAttribute(index, attributeDescription);
3259
0
    aOutDescription.Append(attributeDescription);
3260
0
  }
3261
0
}
3262
3263
bool
3264
Element::CheckHandleEventForLinksPrecondition(EventChainVisitor& aVisitor,
3265
                                              nsIURI** aURI) const
3266
0
{
3267
0
  if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault ||
3268
0
      (!aVisitor.mEvent->IsTrusted() &&
3269
0
       (aVisitor.mEvent->mMessage != eMouseClick) &&
3270
0
       (aVisitor.mEvent->mMessage != eKeyPress) &&
3271
0
       (aVisitor.mEvent->mMessage != eLegacyDOMActivate)) ||
3272
0
      !aVisitor.mPresContext ||
3273
0
      aVisitor.mEvent->mFlags.mMultipleActionsPrevented) {
3274
0
    return false;
3275
0
  }
3276
0
3277
0
  // Make sure we actually are a link
3278
0
  return IsLink(aURI);
3279
0
}
3280
3281
void
3282
Element::GetEventTargetParentForLinks(EventChainPreVisitor& aVisitor)
3283
0
{
3284
0
  // Optimisation: return early if this event doesn't interest us.
3285
0
  // IMPORTANT: this switch and the switch below it must be kept in sync!
3286
0
  switch (aVisitor.mEvent->mMessage) {
3287
0
  case eMouseOver:
3288
0
  case eFocus:
3289
0
  case eMouseOut:
3290
0
  case eBlur:
3291
0
    break;
3292
0
  default:
3293
0
    return;
3294
0
  }
3295
0
3296
0
  // Make sure we meet the preconditions before continuing
3297
0
  nsCOMPtr<nsIURI> absURI;
3298
0
  if (!CheckHandleEventForLinksPrecondition(aVisitor, getter_AddRefs(absURI))) {
3299
0
    return;
3300
0
  }
3301
0
3302
0
  // We do the status bar updates in GetEventTargetParent so that the status bar
3303
0
  // gets updated even if the event is consumed before we have a chance to set
3304
0
  // it.
3305
0
  switch (aVisitor.mEvent->mMessage) {
3306
0
  // Set the status bar similarly for mouseover and focus
3307
0
  case eMouseOver:
3308
0
    aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
3309
0
    MOZ_FALLTHROUGH;
3310
0
  case eFocus: {
3311
0
    InternalFocusEvent* focusEvent = aVisitor.mEvent->AsFocusEvent();
3312
0
    if (!focusEvent || !focusEvent->mIsRefocus) {
3313
0
      nsAutoString target;
3314
0
      GetLinkTarget(target);
3315
0
      nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
3316
0
                                  /* click */ false, /* isTrusted */ true);
3317
0
      // Make sure any ancestor links don't also TriggerLink
3318
0
      aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
3319
0
    }
3320
0
    break;
3321
0
  }
3322
0
  case eMouseOut:
3323
0
    aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
3324
0
    MOZ_FALLTHROUGH;
3325
0
  case eBlur:
3326
0
    {
3327
0
      nsresult rv = LeaveLink(aVisitor.mPresContext);
3328
0
      if (NS_SUCCEEDED(rv)) {
3329
0
        aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
3330
0
      }
3331
0
      break;
3332
0
    }
3333
0
3334
0
  default:
3335
0
    // switch not in sync with the optimization switch earlier in this function
3336
0
    MOZ_ASSERT_UNREACHABLE("switch statements not in sync");
3337
0
  }
3338
0
}
3339
3340
nsresult
3341
Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor)
3342
0
{
3343
0
  // Optimisation: return early if this event doesn't interest us.
3344
0
  // IMPORTANT: this switch and the switch below it must be kept in sync!
3345
0
  switch (aVisitor.mEvent->mMessage) {
3346
0
  case eMouseDown:
3347
0
  case eMouseClick:
3348
0
  case eLegacyDOMActivate:
3349
0
  case eKeyPress:
3350
0
    break;
3351
0
  default:
3352
0
    return NS_OK;
3353
0
  }
3354
0
3355
0
  // Make sure we meet the preconditions before continuing
3356
0
  nsCOMPtr<nsIURI> absURI;
3357
0
  if (!CheckHandleEventForLinksPrecondition(aVisitor, getter_AddRefs(absURI))) {
3358
0
    return NS_OK;
3359
0
  }
3360
0
3361
0
  nsresult rv = NS_OK;
3362
0
3363
0
  switch (aVisitor.mEvent->mMessage) {
3364
0
  case eMouseDown:
3365
0
    {
3366
0
      if (aVisitor.mEvent->AsMouseEvent()->button ==
3367
0
            WidgetMouseEvent::eLeftButton) {
3368
0
        // don't make the link grab the focus if there is no link handler
3369
0
        nsILinkHandler *handler = aVisitor.mPresContext->GetLinkHandler();
3370
0
        nsIDocument *document = GetComposedDoc();
3371
0
        if (handler && document) {
3372
0
          nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3373
0
          if (fm) {
3374
0
            aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
3375
0
            RefPtr<Element> kungFuDeathGrip(this);
3376
0
            fm->SetFocus(kungFuDeathGrip, nsIFocusManager::FLAG_BYMOUSE |
3377
0
                                          nsIFocusManager::FLAG_NOSCROLL);
3378
0
          }
3379
0
3380
0
          EventStateManager::SetActiveManager(
3381
0
            aVisitor.mPresContext->EventStateManager(), this);
3382
0
3383
0
          // OK, we're pretty sure we're going to load, so warm up a speculative
3384
0
          // connection to be sure we have one ready when we open the channel.
3385
0
          nsCOMPtr<nsISpeculativeConnect> sc =
3386
0
            do_QueryInterface(nsContentUtils::GetIOService());
3387
0
          nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(handler);
3388
0
          sc->SpeculativeConnect2(absURI, NodePrincipal(), ir);
3389
0
        }
3390
0
      }
3391
0
    }
3392
0
    break;
3393
0
3394
0
  case eMouseClick: {
3395
0
    WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
3396
0
    if (mouseEvent->IsLeftClickEvent()) {
3397
0
      if (mouseEvent->IsControl() || mouseEvent->IsMeta() ||
3398
0
          mouseEvent->IsAlt() ||mouseEvent->IsShift()) {
3399
0
        break;
3400
0
      }
3401
0
3402
0
      // The default action is simply to dispatch DOMActivate
3403
0
      nsCOMPtr<nsIPresShell> shell = aVisitor.mPresContext->GetPresShell();
3404
0
      if (shell) {
3405
0
        // single-click
3406
0
        nsEventStatus status = nsEventStatus_eIgnore;
3407
0
        // DOMActive event should be trusted since the activation is actually
3408
0
        // occurred even if the cause is an untrusted click event.
3409
0
        InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent);
3410
0
        actEvent.mDetail = 1;
3411
0
3412
0
        rv = shell->HandleDOMEventWithTarget(this, &actEvent, &status);
3413
0
        if (NS_SUCCEEDED(rv)) {
3414
0
          aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
3415
0
        }
3416
0
      }
3417
0
    }
3418
0
    break;
3419
0
  }
3420
0
  case eLegacyDOMActivate:
3421
0
    {
3422
0
      if (aVisitor.mEvent->mOriginalTarget == this) {
3423
0
        nsAutoString target;
3424
0
        GetLinkTarget(target);
3425
0
        const InternalUIEvent* activeEvent = aVisitor.mEvent->AsUIEvent();
3426
0
        MOZ_ASSERT(activeEvent);
3427
0
        nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
3428
0
                                    /* click */ true, activeEvent->IsTrustable());
3429
0
        aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
3430
0
      }
3431
0
    }
3432
0
    break;
3433
0
3434
0
  case eKeyPress:
3435
0
    {
3436
0
      WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
3437
0
      if (keyEvent && keyEvent->mKeyCode == NS_VK_RETURN) {
3438
0
        nsEventStatus status = nsEventStatus_eIgnore;
3439
0
        rv = DispatchClickEvent(aVisitor.mPresContext, keyEvent, this,
3440
0
                                false, nullptr, &status);
3441
0
        if (NS_SUCCEEDED(rv)) {
3442
0
          aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
3443
0
        }
3444
0
      }
3445
0
    }
3446
0
    break;
3447
0
3448
0
  default:
3449
0
    // switch not in sync with the optimization switch earlier in this function
3450
0
    MOZ_ASSERT_UNREACHABLE("switch statements not in sync");
3451
0
    return NS_ERROR_UNEXPECTED;
3452
0
  }
3453
0
3454
0
  return rv;
3455
0
}
3456
3457
void
3458
Element::GetLinkTarget(nsAString& aTarget)
3459
0
{
3460
0
  aTarget.Truncate();
3461
0
}
3462
3463
static void
3464
nsDOMTokenListPropertyDestructor(void *aObject, nsAtom *aProperty,
3465
                                 void *aPropertyValue, void *aData)
3466
0
{
3467
0
  nsDOMTokenList* list =
3468
0
    static_cast<nsDOMTokenList*>(aPropertyValue);
3469
0
  NS_RELEASE(list);
3470
0
}
3471
3472
static nsStaticAtom** sPropertiesToTraverseAndUnlink[] =
3473
  {
3474
    &nsGkAtoms::sandbox,
3475
    &nsGkAtoms::sizes,
3476
    &nsGkAtoms::dirAutoSetBy,
3477
    nullptr
3478
  };
3479
3480
// static
3481
nsStaticAtom***
3482
Element::HTMLSVGPropertiesToTraverseAndUnlink()
3483
0
{
3484
0
  return sPropertiesToTraverseAndUnlink;
3485
0
}
3486
3487
nsDOMTokenList*
3488
Element::GetTokenList(nsAtom* aAtom,
3489
                      const DOMTokenListSupportedTokenArray aSupportedTokens)
3490
0
{
3491
#ifdef DEBUG
3492
  nsStaticAtom*** props = HTMLSVGPropertiesToTraverseAndUnlink();
3493
  bool found = false;
3494
  for (uint32_t i = 0; props[i]; ++i) {
3495
    if (*props[i] == aAtom) {
3496
      found = true;
3497
      break;
3498
    }
3499
  }
3500
  MOZ_ASSERT(found, "Trying to use an unknown tokenlist!");
3501
#endif
3502
3503
0
  nsDOMTokenList* list = nullptr;
3504
0
  if (HasProperties()) {
3505
0
    list = static_cast<nsDOMTokenList*>(GetProperty(aAtom));
3506
0
  }
3507
0
  if (!list) {
3508
0
    list = new nsDOMTokenList(this, aAtom, aSupportedTokens);
3509
0
    NS_ADDREF(list);
3510
0
    SetProperty(aAtom, list, nsDOMTokenListPropertyDestructor);
3511
0
  }
3512
0
  return list;
3513
0
}
3514
3515
Element*
3516
Element::Closest(const nsAString& aSelector, ErrorResult& aResult)
3517
0
{
3518
0
  const RawServoSelectorList* list = ParseSelectorList(aSelector, aResult);
3519
0
  if (!list) {
3520
0
    return nullptr;
3521
0
  }
3522
0
3523
0
  return const_cast<Element*>(Servo_SelectorList_Closest(this, list));
3524
0
}
3525
3526
bool
3527
Element::Matches(const nsAString& aSelector, ErrorResult& aResult)
3528
0
{
3529
0
  const RawServoSelectorList* list = ParseSelectorList(aSelector, aResult);
3530
0
  if (!list) {
3531
0
    return false;
3532
0
  }
3533
0
  return Servo_SelectorList_Matches(this, list);
3534
0
}
3535
3536
static const nsAttrValue::EnumTable kCORSAttributeTable[] = {
3537
  // Order matters here
3538
  // See ParseCORSValue
3539
  { "anonymous",       CORS_ANONYMOUS       },
3540
  { "use-credentials", CORS_USE_CREDENTIALS },
3541
  { nullptr,           0 }
3542
};
3543
3544
/* static */ void
3545
Element::ParseCORSValue(const nsAString& aValue,
3546
                        nsAttrValue& aResult)
3547
0
{
3548
0
  DebugOnly<bool> success =
3549
0
    aResult.ParseEnumValue(aValue, kCORSAttributeTable, false,
3550
0
                           // default value is anonymous if aValue is
3551
0
                           // not a value we understand
3552
0
                           &kCORSAttributeTable[0]);
3553
0
  MOZ_ASSERT(success);
3554
0
}
3555
3556
/* static */ CORSMode
3557
Element::StringToCORSMode(const nsAString& aValue)
3558
0
{
3559
0
  if (aValue.IsVoid()) {
3560
0
    return CORS_NONE;
3561
0
  }
3562
0
3563
0
  nsAttrValue val;
3564
0
  Element::ParseCORSValue(aValue, val);
3565
0
  return CORSMode(val.GetEnumValue());
3566
0
}
3567
3568
/* static */ CORSMode
3569
Element::AttrValueToCORSMode(const nsAttrValue* aValue)
3570
0
{
3571
0
  if (!aValue) {
3572
0
    return CORS_NONE;
3573
0
  }
3574
0
3575
0
  return CORSMode(aValue->GetEnumValue());
3576
0
}
3577
3578
static const char*
3579
GetFullscreenError(CallerType aCallerType)
3580
0
{
3581
0
  if (!nsContentUtils::IsRequestFullscreenAllowed(aCallerType)) {
3582
0
    return "FullscreenDeniedNotInputDriven";
3583
0
  }
3584
0
3585
0
  return nullptr;
3586
0
}
3587
3588
already_AddRefed<Promise>
3589
Element::RequestFullscreen(CallerType aCallerType, ErrorResult& aRv)
3590
0
{
3591
0
  auto request = FullscreenRequest::Create(this, aCallerType, aRv);
3592
0
  RefPtr<Promise> promise = request->GetPromise();
3593
0
  // Only grant fullscreen requests if this is called from inside a trusted
3594
0
  // event handler (i.e. inside an event handler for a user initiated event).
3595
0
  // This stops the fullscreen from being abused similar to the popups of old,
3596
0
  // and it also makes it harder for bad guys' script to go fullscreen and
3597
0
  // spoof the browser chrome/window and phish logins etc.
3598
0
  // Note that requests for fullscreen inside a web app's origin are exempt
3599
0
  // from this restriction.
3600
0
  if (const char* error = GetFullscreenError(aCallerType)) {
3601
0
    request->Reject(error);
3602
0
  } else {
3603
0
    OwnerDoc()->AsyncRequestFullscreen(std::move(request));
3604
0
  }
3605
0
  return promise.forget();
3606
0
}
3607
3608
void
3609
Element::RequestPointerLock(CallerType aCallerType)
3610
0
{
3611
0
  OwnerDoc()->RequestPointerLock(this, aCallerType);
3612
0
}
3613
3614
already_AddRefed<Flex>
3615
Element::GetAsFlexContainer()
3616
0
{
3617
0
  nsIFrame* frame = GetPrimaryFrame();
3618
0
3619
0
  // We need the flex frame to compute additional info, and use
3620
0
  // that annotated version of the frame.
3621
0
  nsFlexContainerFrame* flexFrame =
3622
0
    nsFlexContainerFrame::GetFlexFrameWithComputedInfo(frame);
3623
0
3624
0
  if (flexFrame) {
3625
0
    RefPtr<Flex> flex = new Flex(this, flexFrame);
3626
0
    return flex.forget();
3627
0
  }
3628
0
  return nullptr;
3629
0
}
3630
3631
void
3632
Element::GetGridFragments(nsTArray<RefPtr<Grid>>& aResult)
3633
0
{
3634
0
  nsGridContainerFrame* frame =
3635
0
    nsGridContainerFrame::GetGridFrameWithComputedInfo(GetPrimaryFrame());
3636
0
3637
0
  // If we get a nsGridContainerFrame from the prior call,
3638
0
  // all the next-in-flow frames will also be nsGridContainerFrames.
3639
0
  while (frame) {
3640
0
    aResult.AppendElement(
3641
0
      new Grid(this, frame)
3642
0
    );
3643
0
    frame = static_cast<nsGridContainerFrame*>(frame->GetNextInFlow());
3644
0
  }
3645
0
}
3646
3647
already_AddRefed<DOMMatrixReadOnly>
3648
Element::GetTransformToAncestor(Element& aAncestor)
3649
0
{
3650
0
  nsIFrame* primaryFrame = GetPrimaryFrame();
3651
0
  nsIFrame* ancestorFrame = aAncestor.GetPrimaryFrame();
3652
0
3653
0
  Matrix4x4 transform;
3654
0
  if (primaryFrame) {
3655
0
    // If aAncestor is not actually an ancestor of this (including nullptr),
3656
0
    // then the call to GetTransformToAncestor will return the transform
3657
0
    // all the way up through the parent chain.
3658
0
    transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame,
3659
0
      ancestorFrame, nsIFrame::IN_CSS_UNITS).GetMatrix();
3660
0
  }
3661
0
3662
0
  DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
3663
0
  RefPtr<DOMMatrixReadOnly> result(matrix);
3664
0
  return result.forget();
3665
0
}
3666
3667
already_AddRefed<DOMMatrixReadOnly>
3668
Element::GetTransformToParent()
3669
0
{
3670
0
  nsIFrame* primaryFrame = GetPrimaryFrame();
3671
0
3672
0
  Matrix4x4 transform;
3673
0
  if (primaryFrame) {
3674
0
    nsIFrame* parentFrame = primaryFrame->GetParent();
3675
0
    transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame,
3676
0
      parentFrame, nsIFrame::IN_CSS_UNITS).GetMatrix();
3677
0
  }
3678
0
3679
0
  DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
3680
0
  RefPtr<DOMMatrixReadOnly> result(matrix);
3681
0
  return result.forget();
3682
0
}
3683
3684
already_AddRefed<DOMMatrixReadOnly>
3685
Element::GetTransformToViewport()
3686
0
{
3687
0
  nsIFrame* primaryFrame = GetPrimaryFrame();
3688
0
  Matrix4x4 transform;
3689
0
  if (primaryFrame) {
3690
0
    transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame,
3691
0
      nsLayoutUtils::GetDisplayRootFrame(primaryFrame), nsIFrame::IN_CSS_UNITS).GetMatrix();
3692
0
  }
3693
0
3694
0
  DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
3695
0
  RefPtr<DOMMatrixReadOnly> result(matrix);
3696
0
  return result.forget();
3697
0
}
3698
3699
already_AddRefed<Animation>
3700
Element::Animate(JSContext* aContext,
3701
                 JS::Handle<JSObject*> aKeyframes,
3702
                 const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
3703
                 ErrorResult& aError)
3704
0
{
3705
0
  Nullable<ElementOrCSSPseudoElement> target;
3706
0
  target.SetValue().SetAsElement() = this;
3707
0
  return Animate(target, aContext, aKeyframes, aOptions, aError);
3708
0
}
3709
3710
/* static */ already_AddRefed<Animation>
3711
Element::Animate(const Nullable<ElementOrCSSPseudoElement>& aTarget,
3712
                 JSContext* aContext,
3713
                 JS::Handle<JSObject*> aKeyframes,
3714
                 const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
3715
                 ErrorResult& aError)
3716
0
{
3717
0
  MOZ_ASSERT(!aTarget.IsNull() &&
3718
0
             (aTarget.Value().IsElement() ||
3719
0
              aTarget.Value().IsCSSPseudoElement()),
3720
0
             "aTarget should be initialized");
3721
0
3722
0
  RefPtr<Element> referenceElement;
3723
0
  if (aTarget.Value().IsElement()) {
3724
0
    referenceElement = &aTarget.Value().GetAsElement();
3725
0
  } else {
3726
0
    referenceElement = aTarget.Value().GetAsCSSPseudoElement().ParentElement();
3727
0
  }
3728
0
3729
0
  nsCOMPtr<nsIGlobalObject> ownerGlobal = referenceElement->GetOwnerGlobal();
3730
0
  if (!ownerGlobal) {
3731
0
    aError.Throw(NS_ERROR_FAILURE);
3732
0
    return nullptr;
3733
0
  }
3734
0
  GlobalObject global(aContext, ownerGlobal->GetGlobalJSObject());
3735
0
  MOZ_ASSERT(!global.Failed());
3736
0
3737
0
  // KeyframeEffect constructor doesn't follow the standard Xray calling
3738
0
  // convention and needs to be called in caller's compartment.
3739
0
  // This should match to RunConstructorInCallerCompartment attribute in
3740
0
  // KeyframeEffect.webidl.
3741
0
  RefPtr<KeyframeEffect> effect =
3742
0
    KeyframeEffect::Constructor(global, aTarget, aKeyframes, aOptions,
3743
0
                                aError);
3744
0
  if (aError.Failed()) {
3745
0
    return nullptr;
3746
0
  }
3747
0
3748
0
  // Animation constructor follows the standard Xray calling convention and
3749
0
  // needs to be called in the target element's realm.
3750
0
  Maybe<JSAutoRealm> ar;
3751
0
  if (js::GetContextCompartment(aContext) !=
3752
0
      js::GetObjectCompartment(ownerGlobal->GetGlobalJSObject())) {
3753
0
    ar.emplace(aContext, ownerGlobal->GetGlobalJSObject());
3754
0
  }
3755
0
3756
0
  AnimationTimeline* timeline = referenceElement->OwnerDoc()->Timeline();
3757
0
  RefPtr<Animation> animation =
3758
0
    Animation::Constructor(global, effect,
3759
0
                           Optional<AnimationTimeline*>(timeline), aError);
3760
0
  if (aError.Failed()) {
3761
0
    return nullptr;
3762
0
  }
3763
0
3764
0
  if (aOptions.IsKeyframeAnimationOptions()) {
3765
0
    animation->SetId(aOptions.GetAsKeyframeAnimationOptions().mId);
3766
0
  }
3767
0
3768
0
  animation->Play(aError, Animation::LimitBehavior::AutoRewind);
3769
0
  if (aError.Failed()) {
3770
0
    return nullptr;
3771
0
  }
3772
0
3773
0
  return animation.forget();
3774
0
}
3775
3776
void
3777
Element::GetAnimations(const AnimationFilter& filter,
3778
                       nsTArray<RefPtr<Animation>>& aAnimations)
3779
0
{
3780
0
  nsIDocument* doc = GetComposedDoc();
3781
0
  if (doc) {
3782
0
    // We don't need to explicitly flush throttled animations here, since
3783
0
    // updating the animation style of elements will never affect the set of
3784
0
    // running animations and it's only the set of running animations that is
3785
0
    // important here.
3786
0
    doc->FlushPendingNotifications(
3787
0
      ChangesToFlush(FlushType::Style, false /* flush animations */));
3788
0
  }
3789
0
3790
0
  Element* elem = this;
3791
0
  CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
3792
0
  // For animations on generated-content elements, the animations are stored
3793
0
  // on the parent element.
3794
0
  if (IsGeneratedContentContainerForBefore()) {
3795
0
    elem = GetParentElement();
3796
0
    pseudoType = CSSPseudoElementType::before;
3797
0
  } else if (IsGeneratedContentContainerForAfter()) {
3798
0
    elem = GetParentElement();
3799
0
    pseudoType = CSSPseudoElementType::after;
3800
0
  }
3801
0
3802
0
  if (!elem) {
3803
0
    return;
3804
0
  }
3805
0
3806
0
  if (!filter.mSubtree ||
3807
0
      pseudoType == CSSPseudoElementType::before ||
3808
0
      pseudoType == CSSPseudoElementType::after) {
3809
0
    GetAnimationsUnsorted(elem, pseudoType, aAnimations);
3810
0
  } else {
3811
0
    for (nsIContent* node = this;
3812
0
         node;
3813
0
         node = node->GetNextNode(this)) {
3814
0
      if (!node->IsElement()) {
3815
0
        continue;
3816
0
      }
3817
0
      Element* element = node->AsElement();
3818
0
      Element::GetAnimationsUnsorted(element, CSSPseudoElementType::NotPseudo,
3819
0
                                     aAnimations);
3820
0
      Element::GetAnimationsUnsorted(element, CSSPseudoElementType::before,
3821
0
                                     aAnimations);
3822
0
      Element::GetAnimationsUnsorted(element, CSSPseudoElementType::after,
3823
0
                                     aAnimations);
3824
0
    }
3825
0
  }
3826
0
  aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
3827
0
}
3828
3829
/* static */ void
3830
Element::GetAnimationsUnsorted(Element* aElement,
3831
                               CSSPseudoElementType aPseudoType,
3832
                               nsTArray<RefPtr<Animation>>& aAnimations)
3833
0
{
3834
0
  MOZ_ASSERT(aPseudoType == CSSPseudoElementType::NotPseudo ||
3835
0
             aPseudoType == CSSPseudoElementType::after ||
3836
0
             aPseudoType == CSSPseudoElementType::before,
3837
0
             "Unsupported pseudo type");
3838
0
  MOZ_ASSERT(aElement, "Null element");
3839
0
3840
0
  EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
3841
0
  if (!effects) {
3842
0
    return;
3843
0
  }
3844
0
3845
0
  for (KeyframeEffect* effect : *effects) {
3846
0
    MOZ_ASSERT(effect && effect->GetAnimation(),
3847
0
               "Only effects associated with an animation should be "
3848
0
               "added to an element's effect set");
3849
0
    Animation* animation = effect->GetAnimation();
3850
0
3851
0
    MOZ_ASSERT(animation->IsRelevant(),
3852
0
               "Only relevant animations should be added to an element's "
3853
0
               "effect set");
3854
0
    aAnimations.AppendElement(animation);
3855
0
  }
3856
0
}
3857
3858
void
3859
Element::GetInnerHTML(nsAString& aInnerHTML, OOMReporter& aError)
3860
0
{
3861
0
  GetMarkup(false, aInnerHTML);
3862
0
}
3863
3864
void
3865
Element::SetInnerHTML(const nsAString& aInnerHTML, nsIPrincipal* aSubjectPrincipal, ErrorResult& aError)
3866
0
{
3867
0
  SetInnerHTMLInternal(aInnerHTML, aError);
3868
0
}
3869
3870
void
3871
Element::GetOuterHTML(nsAString& aOuterHTML)
3872
0
{
3873
0
  GetMarkup(true, aOuterHTML);
3874
0
}
3875
3876
void
3877
Element::SetOuterHTML(const nsAString& aOuterHTML, ErrorResult& aError)
3878
0
{
3879
0
  nsCOMPtr<nsINode> parent = GetParentNode();
3880
0
  if (!parent) {
3881
0
    return;
3882
0
  }
3883
0
3884
0
  if (parent->NodeType() == DOCUMENT_NODE) {
3885
0
    aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
3886
0
    return;
3887
0
  }
3888
0
3889
0
  if (OwnerDoc()->IsHTMLDocument()) {
3890
0
    nsAtom* localName;
3891
0
    int32_t namespaceID;
3892
0
    if (parent->IsElement()) {
3893
0
      localName = parent->NodeInfo()->NameAtom();
3894
0
      namespaceID = parent->NodeInfo()->NamespaceID();
3895
0
    } else {
3896
0
      NS_ASSERTION(parent->NodeType() == DOCUMENT_FRAGMENT_NODE,
3897
0
        "How come the parent isn't a document, a fragment or an element?");
3898
0
      localName = nsGkAtoms::body;
3899
0
      namespaceID = kNameSpaceID_XHTML;
3900
0
    }
3901
0
    RefPtr<DocumentFragment> fragment =
3902
0
      new DocumentFragment(OwnerDoc()->NodeInfoManager());
3903
0
    nsContentUtils::ParseFragmentHTML(aOuterHTML,
3904
0
                                      fragment,
3905
0
                                      localName,
3906
0
                                      namespaceID,
3907
0
                                      OwnerDoc()->GetCompatibilityMode() ==
3908
0
                                        eCompatibility_NavQuirks,
3909
0
                                      true);
3910
0
    parent->ReplaceChild(*fragment, *this, aError);
3911
0
    return;
3912
0
  }
3913
0
3914
0
  nsCOMPtr<nsINode> context;
3915
0
  if (parent->IsElement()) {
3916
0
    context = parent;
3917
0
  } else {
3918
0
    NS_ASSERTION(parent->NodeType() == DOCUMENT_FRAGMENT_NODE,
3919
0
      "How come the parent isn't a document, a fragment or an element?");
3920
0
    RefPtr<mozilla::dom::NodeInfo> info =
3921
0
      OwnerDoc()->NodeInfoManager()->GetNodeInfo(nsGkAtoms::body,
3922
0
                                                 nullptr,
3923
0
                                                 kNameSpaceID_XHTML,
3924
0
                                                 ELEMENT_NODE);
3925
0
    context = NS_NewHTMLBodyElement(info.forget(), FROM_PARSER_FRAGMENT);
3926
0
  }
3927
0
3928
0
  RefPtr<DocumentFragment> fragment =
3929
0
    nsContentUtils::CreateContextualFragment(context, aOuterHTML, true, aError);
3930
0
  if (aError.Failed()) {
3931
0
    return;
3932
0
  }
3933
0
  parent->ReplaceChild(*fragment, *this, aError);
3934
0
}
3935
3936
enum nsAdjacentPosition {
3937
  eBeforeBegin,
3938
  eAfterBegin,
3939
  eBeforeEnd,
3940
  eAfterEnd
3941
};
3942
3943
void
3944
Element::InsertAdjacentHTML(const nsAString& aPosition, const nsAString& aText,
3945
                            ErrorResult& aError)
3946
0
{
3947
0
  nsAdjacentPosition position;
3948
0
  if (aPosition.LowerCaseEqualsLiteral("beforebegin")) {
3949
0
    position = eBeforeBegin;
3950
0
  } else if (aPosition.LowerCaseEqualsLiteral("afterbegin")) {
3951
0
    position = eAfterBegin;
3952
0
  } else if (aPosition.LowerCaseEqualsLiteral("beforeend")) {
3953
0
    position = eBeforeEnd;
3954
0
  } else if (aPosition.LowerCaseEqualsLiteral("afterend")) {
3955
0
    position = eAfterEnd;
3956
0
  } else {
3957
0
    aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
3958
0
    return;
3959
0
  }
3960
0
3961
0
  nsCOMPtr<nsIContent> destination;
3962
0
  if (position == eBeforeBegin || position == eAfterEnd) {
3963
0
    destination = GetParent();
3964
0
    if (!destination) {
3965
0
      aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
3966
0
      return;
3967
0
    }
3968
0
  } else {
3969
0
    destination = this;
3970
0
  }
3971
0
3972
0
  nsIDocument* doc = OwnerDoc();
3973
0
3974
0
  // Needed when insertAdjacentHTML is used in combination with contenteditable
3975
0
  mozAutoDocUpdate updateBatch(doc, true);
3976
0
  nsAutoScriptLoaderDisabler sld(doc);
3977
0
3978
0
  // Batch possible DOMSubtreeModified events.
3979
0
  mozAutoSubtreeModified subtree(doc, nullptr);
3980
0
3981
0
  // Parse directly into destination if possible
3982
0
  if (doc->IsHTMLDocument() && !OwnerDoc()->MayHaveDOMMutationObservers() &&
3983
0
      (position == eBeforeEnd ||
3984
0
       (position == eAfterEnd && !GetNextSibling()) ||
3985
0
       (position == eAfterBegin && !GetFirstChild()))) {
3986
0
    int32_t oldChildCount = destination->GetChildCount();
3987
0
    int32_t contextNs = destination->GetNameSpaceID();
3988
0
    nsAtom* contextLocal = destination->NodeInfo()->NameAtom();
3989
0
    if (contextLocal == nsGkAtoms::html && contextNs == kNameSpaceID_XHTML) {
3990
0
      // For compat with IE6 through IE9. Willful violation of HTML5 as of
3991
0
      // 2011-04-06. CreateContextualFragment does the same already.
3992
0
      // Spec bug: http://www.w3.org/Bugs/Public/show_bug.cgi?id=12434
3993
0
      contextLocal = nsGkAtoms::body;
3994
0
    }
3995
0
    aError = nsContentUtils::ParseFragmentHTML(aText,
3996
0
                                               destination,
3997
0
                                               contextLocal,
3998
0
                                               contextNs,
3999
0
                                               doc->GetCompatibilityMode() ==
4000
0
                                                 eCompatibility_NavQuirks,
4001
0
                                               true);
4002
0
    // HTML5 parser has notified, but not fired mutation events.
4003
0
    nsContentUtils::FireMutationEventsForDirectParsing(doc, destination,
4004
0
                                                       oldChildCount);
4005
0
    return;
4006
0
  }
4007
0
4008
0
  // couldn't parse directly
4009
0
  RefPtr<DocumentFragment> fragment =
4010
0
    nsContentUtils::CreateContextualFragment(destination, aText, true, aError);
4011
0
  if (aError.Failed()) {
4012
0
    return;
4013
0
  }
4014
0
4015
0
  // Suppress assertion about node removal mutation events that can't have
4016
0
  // listeners anyway, because no one has had the chance to register mutation
4017
0
  // listeners on the fragment that comes from the parser.
4018
0
  nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
4019
0
4020
0
  nsAutoMutationBatch mb(destination, true, false);
4021
0
  switch (position) {
4022
0
    case eBeforeBegin:
4023
0
      destination->InsertBefore(*fragment, this, aError);
4024
0
      break;
4025
0
    case eAfterBegin:
4026
0
      static_cast<nsINode*>(this)->InsertBefore(*fragment, GetFirstChild(),
4027
0
                                                aError);
4028
0
      break;
4029
0
    case eBeforeEnd:
4030
0
      static_cast<nsINode*>(this)->AppendChild(*fragment, aError);
4031
0
      break;
4032
0
    case eAfterEnd:
4033
0
      destination->InsertBefore(*fragment, GetNextSibling(), aError);
4034
0
      break;
4035
0
  }
4036
0
}
4037
4038
nsINode*
4039
Element::InsertAdjacent(const nsAString& aWhere,
4040
                        nsINode* aNode,
4041
                        ErrorResult& aError)
4042
0
{
4043
0
  if (aWhere.LowerCaseEqualsLiteral("beforebegin")) {
4044
0
    nsCOMPtr<nsINode> parent = GetParentNode();
4045
0
    if (!parent) {
4046
0
      return nullptr;
4047
0
    }
4048
0
    parent->InsertBefore(*aNode, this, aError);
4049
0
  } else if (aWhere.LowerCaseEqualsLiteral("afterbegin")) {
4050
0
    nsCOMPtr<nsINode> refNode = GetFirstChild();
4051
0
    static_cast<nsINode*>(this)->InsertBefore(*aNode, refNode, aError);
4052
0
  } else if (aWhere.LowerCaseEqualsLiteral("beforeend")) {
4053
0
    static_cast<nsINode*>(this)->AppendChild(*aNode, aError);
4054
0
  } else if (aWhere.LowerCaseEqualsLiteral("afterend")) {
4055
0
    nsCOMPtr<nsINode> parent = GetParentNode();
4056
0
    if (!parent) {
4057
0
      return nullptr;
4058
0
    }
4059
0
    nsCOMPtr<nsINode> refNode = GetNextSibling();
4060
0
    parent->InsertBefore(*aNode, refNode, aError);
4061
0
  } else {
4062
0
    aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
4063
0
    return nullptr;
4064
0
  }
4065
0
4066
0
  return aError.Failed() ? nullptr : aNode;
4067
0
}
4068
4069
Element*
4070
Element::InsertAdjacentElement(const nsAString& aWhere,
4071
                               Element& aElement,
4072
0
                               ErrorResult& aError) {
4073
0
  nsINode* newNode = InsertAdjacent(aWhere, &aElement, aError);
4074
0
  MOZ_ASSERT(!newNode || newNode->IsElement());
4075
0
4076
0
  return newNode ? newNode->AsElement() : nullptr;
4077
0
}
4078
4079
void
4080
Element::InsertAdjacentText(
4081
  const nsAString& aWhere, const nsAString& aData, ErrorResult& aError)
4082
0
{
4083
0
  RefPtr<nsTextNode> textNode = OwnerDoc()->CreateTextNode(aData);
4084
0
  InsertAdjacent(aWhere, textNode, aError);
4085
0
}
4086
4087
TextEditor*
4088
Element::GetTextEditorInternal()
4089
0
{
4090
0
  nsCOMPtr<nsITextControlElement> textCtrl = do_QueryInterface(this);
4091
0
  return textCtrl ? textCtrl->GetTextEditor() : nullptr;
4092
0
}
4093
4094
nsresult
4095
Element::SetBoolAttr(nsAtom* aAttr, bool aValue)
4096
0
{
4097
0
  if (aValue) {
4098
0
    return SetAttr(kNameSpaceID_None, aAttr, EmptyString(), true);
4099
0
  }
4100
0
4101
0
  return UnsetAttr(kNameSpaceID_None, aAttr, true);
4102
0
}
4103
4104
void
4105
Element::GetEnumAttr(nsAtom* aAttr,
4106
                     const char* aDefault,
4107
                     nsAString& aResult) const
4108
0
{
4109
0
  GetEnumAttr(aAttr, aDefault, aDefault, aResult);
4110
0
}
4111
4112
void
4113
Element::GetEnumAttr(nsAtom* aAttr,
4114
                     const char* aDefaultMissing,
4115
                     const char* aDefaultInvalid,
4116
                     nsAString& aResult) const
4117
0
{
4118
0
  const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr);
4119
0
4120
0
  aResult.Truncate();
4121
0
4122
0
  if (!attrVal) {
4123
0
    if (aDefaultMissing) {
4124
0
      AppendASCIItoUTF16(nsDependentCString(aDefaultMissing), aResult);
4125
0
    } else {
4126
0
      SetDOMStringToNull(aResult);
4127
0
    }
4128
0
  } else {
4129
0
    if (attrVal->Type() == nsAttrValue::eEnum) {
4130
0
      attrVal->GetEnumString(aResult, true);
4131
0
    } else if (aDefaultInvalid) {
4132
0
      AppendASCIItoUTF16(nsDependentCString(aDefaultInvalid), aResult);
4133
0
    }
4134
0
  }
4135
0
}
4136
4137
void
4138
Element::SetOrRemoveNullableStringAttr(nsAtom* aName, const nsAString& aValue,
4139
                                       ErrorResult& aError)
4140
0
{
4141
0
  if (DOMStringIsNull(aValue)) {
4142
0
    UnsetAttr(aName, aError);
4143
0
  } else {
4144
0
    SetAttr(aName, aValue, aError);
4145
0
  }
4146
0
}
4147
4148
Directionality
4149
Element::GetComputedDirectionality() const
4150
0
{
4151
0
  nsIFrame* frame = GetPrimaryFrame();
4152
0
  if (frame) {
4153
0
    return frame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR
4154
0
             ? eDir_LTR : eDir_RTL;
4155
0
  }
4156
0
4157
0
  return GetDirectionality();
4158
0
}
4159
4160
float
4161
Element::FontSizeInflation()
4162
0
{
4163
0
  nsIFrame* frame = GetPrimaryFrame();
4164
0
  if (!frame) {
4165
0
    return -1.0;
4166
0
  }
4167
0
4168
0
  if (nsLayoutUtils::FontSizeInflationEnabled(frame->PresContext())) {
4169
0
    return nsLayoutUtils::FontSizeInflationFor(frame);
4170
0
  }
4171
0
4172
0
  return 1.0;
4173
0
}
4174
4175
net::ReferrerPolicy
4176
Element::GetReferrerPolicyAsEnum()
4177
0
{
4178
0
  if (IsHTMLElement()) {
4179
0
    const nsAttrValue* referrerValue = GetParsedAttr(nsGkAtoms::referrerpolicy);
4180
0
    return ReferrerPolicyFromAttr(referrerValue);
4181
0
  }
4182
0
  return net::RP_Unset;
4183
0
}
4184
4185
net::ReferrerPolicy
4186
Element::ReferrerPolicyFromAttr(const nsAttrValue* aValue)
4187
0
{
4188
0
  if (aValue && aValue->Type() == nsAttrValue::eEnum) {
4189
0
    return net::ReferrerPolicy(aValue->GetEnumValue());
4190
0
  }
4191
0
  return net::RP_Unset;
4192
0
}
4193
4194
already_AddRefed<nsDOMStringMap>
4195
Element::Dataset()
4196
0
{
4197
0
  nsDOMSlots *slots = DOMSlots();
4198
0
4199
0
  if (!slots->mDataset) {
4200
0
    // mDataset is a weak reference so assignment will not AddRef.
4201
0
    // AddRef is called before returning the pointer.
4202
0
    slots->mDataset = new nsDOMStringMap(this);
4203
0
  }
4204
0
4205
0
  RefPtr<nsDOMStringMap> ret = slots->mDataset;
4206
0
  return ret.forget();
4207
0
}
4208
4209
void
4210
Element::ClearDataset()
4211
0
{
4212
0
  nsDOMSlots *slots = GetExistingDOMSlots();
4213
0
4214
0
  MOZ_ASSERT(slots && slots->mDataset,
4215
0
             "Slots should exist and dataset should not be null.");
4216
0
  slots->mDataset = nullptr;
4217
0
}
4218
4219
enum nsPreviousIntersectionThreshold {
4220
  eUninitialized = -2,
4221
  eNonIntersecting = -1
4222
};
4223
4224
static void
4225
IntersectionObserverPropertyDtor(void* aObject, nsAtom* aPropertyName,
4226
                                 void* aPropertyValue, void* aData)
4227
0
{
4228
0
  Element* element = static_cast<Element*>(aObject);
4229
0
  IntersectionObserverList* observers =
4230
0
    static_cast<IntersectionObserverList*>(aPropertyValue);
4231
0
  for (auto iter = observers->Iter(); !iter.Done(); iter.Next()) {
4232
0
    DOMIntersectionObserver* observer = iter.Key();
4233
0
    observer->UnlinkTarget(*element);
4234
0
  }
4235
0
  delete observers;
4236
0
}
4237
4238
void
4239
Element::RegisterIntersectionObserver(DOMIntersectionObserver* aObserver)
4240
0
{
4241
0
  IntersectionObserverList* observers =
4242
0
    static_cast<IntersectionObserverList*>(
4243
0
      GetProperty(nsGkAtoms::intersectionobserverlist)
4244
0
    );
4245
0
4246
0
  if (!observers) {
4247
0
    observers = new IntersectionObserverList();
4248
0
    observers->Put(aObserver, eUninitialized);
4249
0
    SetProperty(nsGkAtoms::intersectionobserverlist, observers,
4250
0
                IntersectionObserverPropertyDtor, true);
4251
0
    return;
4252
0
  }
4253
0
4254
0
  observers->LookupForAdd(aObserver).OrInsert([]() {
4255
0
    // Value can be:
4256
0
    //   -2:   Makes sure next calculated threshold always differs, leading to a
4257
0
    //         notification task being scheduled.
4258
0
    //   -1:   Non-intersecting.
4259
0
    //   >= 0: Intersecting, valid index of aObserver->mThresholds.
4260
0
    return eUninitialized;
4261
0
  });
4262
0
}
4263
4264
void
4265
Element::UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver)
4266
0
{
4267
0
  IntersectionObserverList* observers =
4268
0
    static_cast<IntersectionObserverList*>(
4269
0
      GetProperty(nsGkAtoms::intersectionobserverlist)
4270
0
    );
4271
0
  if (observers) {
4272
0
    observers->Remove(aObserver);
4273
0
  }
4274
0
}
4275
4276
void
4277
Element::UnlinkIntersectionObservers()
4278
0
{
4279
0
  IntersectionObserverList* observers =
4280
0
    static_cast<IntersectionObserverList*>(
4281
0
      GetProperty(nsGkAtoms::intersectionobserverlist)
4282
0
    );
4283
0
  if (!observers) {
4284
0
    return;
4285
0
  }
4286
0
  for (auto iter = observers->Iter(); !iter.Done(); iter.Next()) {
4287
0
    DOMIntersectionObserver* observer = iter.Key();
4288
0
    observer->UnlinkTarget(*this);
4289
0
  }
4290
0
  observers->Clear();
4291
0
}
4292
4293
bool
4294
Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32_t aThreshold)
4295
0
{
4296
0
  IntersectionObserverList* observers =
4297
0
    static_cast<IntersectionObserverList*>(
4298
0
      GetProperty(nsGkAtoms::intersectionobserverlist)
4299
0
    );
4300
0
  if (!observers) {
4301
0
    return false;
4302
0
  }
4303
0
  bool updated = false;
4304
0
  if (auto entry = observers->Lookup(aObserver)) {
4305
0
    updated = entry.Data() != aThreshold;
4306
0
    entry.Data() = aThreshold;
4307
0
  }
4308
0
  return updated;
4309
0
}
4310
4311
template<class T> void
4312
Element::GetCustomInterface(nsGetterAddRefs<T> aResult)
4313
{
4314
  nsCOMPtr<nsISupports> iface =
4315
    CustomElementRegistry::CallGetCustomInterface(this, NS_GET_TEMPLATE_IID(T));
4316
  if (iface) {
4317
    CallQueryInterface(iface, static_cast<T**>(aResult));
4318
  }
4319
}
4320
4321
void
4322
0
Element::ClearServoData(nsIDocument* aDoc) {
4323
0
  MOZ_ASSERT(aDoc);
4324
0
  if (HasServoData()) {
4325
0
    Servo_Element_ClearData(this);
4326
0
  } else {
4327
0
    UnsetFlags(kAllServoDescendantBits | NODE_NEEDS_FRAME);
4328
0
  }
4329
0
  // Since this element is losing its servo data, nothing under it may have
4330
0
  // servo data either, so we can forget restyles rooted at this element. This
4331
0
  // is necessary for correctness, since we invoke ClearServoData in various
4332
0
  // places where an element's flattened tree parent changes, and such a change
4333
0
  // may also make an element invalid to be used as a restyle root.
4334
0
  if (aDoc->GetServoRestyleRoot() == this) {
4335
0
    aDoc->ClearServoRestyleRoot();
4336
0
  }
4337
0
}
4338
4339
void
4340
Element::SetCustomElementData(CustomElementData* aData)
4341
0
{
4342
0
  SetHasCustomElementData();
4343
0
4344
0
  if (aData->mState != CustomElementData::State::eCustom) {
4345
0
    SetDefined(false);
4346
0
  }
4347
0
4348
0
  nsExtendedDOMSlots *slots = ExtendedDOMSlots();
4349
0
  MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
4350
  #if DEBUG
4351
    // We assert only XUL usage, since web may pass whatever as 'is' value
4352
    if (NodeInfo()->NamespaceID() == kNameSpaceID_XUL) {
4353
      nsAtom* name = NodeInfo()->NameAtom();
4354
      nsAtom* type = aData->GetCustomElementType();
4355
      // Check to see if the tag name is a dashed name.
4356
      if (nsContentUtils::IsNameWithDash(name)) {
4357
        // Assert that a tag name with dashes is always an autonomous custom
4358
        // element.
4359
        MOZ_ASSERT(type == name);
4360
      } else {
4361
        // Could still be an autonomous custom element with a non-dashed tag name.
4362
        // Need the check below for sure.
4363
        if (type != name) {
4364
          // Assert that the name of the built-in custom element type is always
4365
          // a dashed name.
4366
          MOZ_ASSERT(nsContentUtils::IsNameWithDash(type));
4367
        }
4368
      }
4369
    }
4370
  #endif
4371
  slots->mCustomElementData = aData;
4372
0
}
4373
4374
CustomElementDefinition*
4375
Element::GetCustomElementDefinition() const
4376
0
{
4377
0
  CustomElementData* data = GetCustomElementData();
4378
0
  if (!data) {
4379
0
    return nullptr;
4380
0
  }
4381
0
4382
0
  return data->GetCustomElementDefinition();
4383
0
}
4384
4385
void
4386
Element::SetCustomElementDefinition(CustomElementDefinition* aDefinition)
4387
0
{
4388
0
  CustomElementData* data = GetCustomElementData();
4389
0
  MOZ_ASSERT(data);
4390
0
4391
0
  data->SetCustomElementDefinition(aDefinition);
4392
0
}
4393
4394
MOZ_DEFINE_MALLOC_SIZE_OF(ServoElementMallocSizeOf)
4395
MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoElementMallocEnclosingSizeOf)
4396
4397
void
4398
Element::AddSizeOfExcludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const
4399
0
{
4400
0
  FragmentOrElement::AddSizeOfExcludingThis(aSizes, aNodeSize);
4401
0
4402
0
  if (HasServoData()) {
4403
0
    // Measure the ElementData object itself.
4404
0
    aSizes.mLayoutElementDataObjects +=
4405
0
      aSizes.mState.mMallocSizeOf(mServoData.Get());
4406
0
4407
0
    // Measure mServoData, excluding the ComputedValues. This measurement
4408
0
    // counts towards the element's size. We use ServoElementMallocSizeOf and
4409
0
    // ServoElementMallocEnclosingSizeOf rather than |aState.mMallocSizeOf| to
4410
0
    // better distinguish in DMD's output the memory measured within Servo
4411
0
    // code.
4412
0
    *aNodeSize +=
4413
0
      Servo_Element_SizeOfExcludingThisAndCVs(ServoElementMallocSizeOf,
4414
0
                                              ServoElementMallocEnclosingSizeOf,
4415
0
                                              &aSizes.mState.mSeenPtrs, this);
4416
0
4417
0
    // Now measure just the ComputedValues (and style structs) under
4418
0
    // mServoData. This counts towards the relevant fields in |aSizes|.
4419
0
    RefPtr<ComputedStyle> sc;
4420
0
    if (Servo_Element_HasPrimaryComputedValues(this)) {
4421
0
      sc = Servo_Element_GetPrimaryComputedValues(this).Consume();
4422
0
      if (!aSizes.mState.HaveSeenPtr(sc.get())) {
4423
0
        sc->AddSizeOfIncludingThis(aSizes, &aSizes.mLayoutComputedValuesDom);
4424
0
      }
4425
0
4426
0
      for (size_t i = 0; i < nsCSSPseudoElements::kEagerPseudoCount; i++) {
4427
0
        if (Servo_Element_HasPseudoComputedValues(this, i)) {
4428
0
          sc = Servo_Element_GetPseudoComputedValues(this, i).Consume();
4429
0
          if (!aSizes.mState.HaveSeenPtr(sc.get())) {
4430
0
            sc->AddSizeOfIncludingThis(aSizes,
4431
0
                                       &aSizes.mLayoutComputedValuesDom);
4432
0
          }
4433
0
        }
4434
0
      }
4435
0
    }
4436
0
  }
4437
0
}
4438
4439
#ifdef DEBUG
4440
static bool
4441
BitsArePropagated(const Element* aElement, uint32_t aBits, nsINode* aRestyleRoot)
4442
{
4443
  const Element* curr = aElement;
4444
  while (curr) {
4445
    if (curr == aRestyleRoot) {
4446
      return true;
4447
    }
4448
    if (!curr->HasAllFlags(aBits)) {
4449
      return false;
4450
    }
4451
    nsINode* parentNode = curr->GetParentNode();
4452
    curr = curr->GetFlattenedTreeParentElementForStyle();
4453
    MOZ_ASSERT_IF(!curr,
4454
                  parentNode == aElement->OwnerDoc() ||
4455
                  parentNode == parentNode->OwnerDoc()->GetRootElement());
4456
  }
4457
  return true;
4458
}
4459
#endif
4460
4461
static inline void
4462
AssertNoBitsPropagatedFrom(nsINode* aRoot)
4463
0
{
4464
#ifdef DEBUG
4465
  if (!aRoot || !aRoot->IsElement()) {
4466
    return;
4467
  }
4468
4469
  auto* element = aRoot->GetFlattenedTreeParentElementForStyle();
4470
  while (element) {
4471
    MOZ_ASSERT(!element->HasAnyOfFlags(Element::kAllServoDescendantBits));
4472
    element = element->GetFlattenedTreeParentElementForStyle();
4473
  }
4474
#endif
4475
}
4476
4477
// Sets `aBits` on `aElement` and all of its flattened-tree ancestors up to and
4478
// including aStopAt or the root element (whichever is encountered first), and
4479
// as long as `aBitsToStopAt` isn't found anywhere in the chain.
4480
static inline Element*
4481
PropagateBits(Element* aElement, uint32_t aBits, nsINode* aStopAt, uint32_t aBitsToStopAt)
4482
0
{
4483
0
  Element* curr = aElement;
4484
0
  while (curr && !curr->HasAllFlags(aBitsToStopAt)) {
4485
0
    curr->SetFlags(aBits);
4486
0
    if (curr == aStopAt) {
4487
0
      break;
4488
0
    }
4489
0
    curr = curr->GetFlattenedTreeParentElementForStyle();
4490
0
  }
4491
0
4492
0
  if (aBitsToStopAt != aBits && curr) {
4493
0
    curr->SetFlags(aBits);
4494
0
  }
4495
0
4496
0
  return curr;
4497
0
}
4498
4499
// Notes that a given element is "dirty" with respect to the given descendants
4500
// bit (which may be one of dirty descendants, dirty animation descendants, or
4501
// need frame construction for descendants).
4502
//
4503
// This function operates on the dirty element itself, despite the fact that the
4504
// bits are generally used to describe descendants. This allows restyle roots
4505
// to be scoped as tightly as possible. On the first call to NoteDirtyElement
4506
// since the last restyle, we don't set any descendant bits at all, and just set
4507
// the element as the restyle root.
4508
//
4509
// Because the style traversal handles multiple tasks (styling, animation-ticking,
4510
// and lazy frame construction), there are potentially three separate kinds of
4511
// dirtiness to track. Rather than maintaining three separate restyle roots, we
4512
// use a single root, and always bubble it up to be the nearest common ancestor
4513
// of all the dirty content in the tree. This means that we need to track the
4514
// types of dirtiness that the restyle root corresponds to, so
4515
// SetServoRestyleRoot accepts a bitfield along with an element.
4516
//
4517
// The overall algorithm is as follows:
4518
// * When the first dirty element is noted, we just set as the restyle root.
4519
// * When additional dirty elements are noted, we propagate the given bit up
4520
//   the tree, until we either reach the restyle root or the document root.
4521
// * If we reach the document root, we then propagate the bits associated with
4522
//   the restyle root up the tree until we cross the path of the new root. Once
4523
//   we find this common ancestor, we record it as the restyle root, and then
4524
//   clear the bits between the new restyle root and the document root.
4525
// * If we have dirty content beneath multiple "document style traversal roots"
4526
//   (which are the main DOM + each piece of document-level native-anoymous
4527
//   content), we set the restyle root to the nsINode of the document itself.
4528
//   This is the bail-out case where we traverse everything.
4529
//
4530
// Note that, since we track a root, we try to optimize the case where an
4531
// element under the current root is dirtied, that's why we don't trivially use
4532
// `nsContentUtils::GetCommonFlattenedTreeAncestorForStyle`.
4533
static void
4534
NoteDirtyElement(Element* aElement, uint32_t aBits)
4535
0
{
4536
0
  MOZ_ASSERT(aElement->IsInComposedDoc());
4537
0
4538
0
  // Check the existing root early on, since it may allow us to short-circuit
4539
0
  // before examining the parent chain.
4540
0
  nsIDocument* doc = aElement->GetComposedDoc();
4541
0
  nsINode* existingRoot = doc->GetServoRestyleRoot();
4542
0
  if (existingRoot == aElement) {
4543
0
    doc->SetServoRestyleRootDirtyBits(doc->GetServoRestyleRootDirtyBits() | aBits);
4544
0
    return;
4545
0
  }
4546
0
4547
0
  nsINode* parent = aElement->GetFlattenedTreeParentNodeForStyle();
4548
0
  if (!parent) {
4549
0
    // The element is not in the flattened tree, bail.
4550
0
    return;
4551
0
  }
4552
0
4553
0
  if (MOZ_LIKELY(parent->IsElement())) {
4554
0
    // If our parent is unstyled, we can inductively assume that it will be
4555
0
    // traversed when the time is right, and that the traversal will reach us
4556
0
    // when it happens. Nothing left to do.
4557
0
    if (!parent->AsElement()->HasServoData()) {
4558
0
      return;
4559
0
    }
4560
0
4561
0
    // Similarly, if our parent already has the bit we're propagating, we can
4562
0
    // assume everything is already set up.
4563
0
    if (parent->HasAllFlags(aBits)) {
4564
0
      return;
4565
0
    }
4566
0
4567
0
    // If the parent is styled but is display:none, we're done.
4568
0
    //
4569
0
    // We can't check for a frame here, since <frame> elements inside <frameset>
4570
0
    // still need to generate a frame, even if they're display: none. :(
4571
0
    //
4572
0
    // The servo traversal doesn't keep style data under display: none subtrees,
4573
0
    // so in order for it to not need to cleanup each time anything happens in a
4574
0
    // display: none subtree, we keep it clean.
4575
0
    //
4576
0
    // Also, we can't be much more smarter about using the parent's frame in
4577
0
    // order to avoid work here, because since the style system keeps style data
4578
0
    // in, e.g., subtrees under a leaf frame, missing restyles and such in there
4579
0
    // has observable behavior via getComputedStyle, for example.
4580
0
    if (Servo_Element_IsDisplayNone(parent->AsElement())) {
4581
0
      return;
4582
0
    }
4583
0
  }
4584
0
4585
0
  if (nsIPresShell* shell = doc->GetShell()) {
4586
0
    shell->EnsureStyleFlush();
4587
0
  }
4588
0
4589
0
  MOZ_ASSERT(parent->IsElement() || parent == doc);
4590
0
4591
0
  // The bit checks below rely on this to arrive to useful conclusions about the
4592
0
  // shape of the tree.
4593
0
  AssertNoBitsPropagatedFrom(existingRoot);
4594
0
4595
0
  // If there's no existing restyle root, or if the root is already aElement,
4596
0
  // just note the root+bits and return.
4597
0
  if (!existingRoot) {
4598
0
    doc->SetServoRestyleRoot(aElement, aBits);
4599
0
    return;
4600
0
  }
4601
0
4602
0
  // There is an existing restyle root - walk up the tree from our element,
4603
0
  // propagating bits as we go.
4604
0
  const bool reachedDocRoot =
4605
0
    !parent->IsElement() ||
4606
0
    !PropagateBits(parent->AsElement(), aBits, existingRoot, aBits);
4607
0
4608
0
  uint32_t existingBits = doc->GetServoRestyleRootDirtyBits();
4609
0
  if (!reachedDocRoot || existingRoot == doc) {
4610
0
      // We're a descendant of the existing root. All that's left to do is to
4611
0
      // make sure the bit we propagated is also registered on the root.
4612
0
      doc->SetServoRestyleRoot(existingRoot, existingBits | aBits);
4613
0
  } else {
4614
0
    // We reached the root without crossing the pre-existing restyle root. We
4615
0
    // now need to find the nearest common ancestor, so climb up from the
4616
0
    // existing root, extending bits along the way.
4617
0
    Element* rootParent = existingRoot->GetFlattenedTreeParentElementForStyle();
4618
0
    // We can stop at the first occurrence of `aBits` in order to find the
4619
0
    // common ancestor.
4620
0
    if (Element* commonAncestor = PropagateBits(rootParent, existingBits, aElement, aBits)) {
4621
0
      MOZ_ASSERT(commonAncestor == aElement ||
4622
0
                 commonAncestor == nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(aElement, rootParent));
4623
0
4624
0
      // We found a common ancestor. Make that the new style root, and clear the
4625
0
      // bits between the new style root and the document root.
4626
0
      doc->SetServoRestyleRoot(commonAncestor, existingBits | aBits);
4627
0
      Element* curr = commonAncestor;
4628
0
      while ((curr = curr->GetFlattenedTreeParentElementForStyle())) {
4629
0
        MOZ_ASSERT(curr->HasAllFlags(aBits));
4630
0
        curr->UnsetFlags(aBits);
4631
0
      }
4632
0
      AssertNoBitsPropagatedFrom(commonAncestor);
4633
0
    } else {
4634
0
      // We didn't find a common ancestor element. That means we're descended
4635
0
      // from two different document style roots, so the common ancestor is the
4636
0
      // document.
4637
0
      doc->SetServoRestyleRoot(doc, existingBits | aBits);
4638
0
    }
4639
0
  }
4640
0
4641
0
  // See the comment in nsIDocument::SetServoRestyleRoot about the !IsElement()
4642
0
  // check there. Same justification here.
4643
0
  MOZ_ASSERT(aElement == doc->GetServoRestyleRoot() ||
4644
0
             !doc->GetServoRestyleRoot()->IsElement() ||
4645
0
             nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
4646
0
               aElement, doc->GetServoRestyleRoot()));
4647
0
  MOZ_ASSERT(aElement == doc->GetServoRestyleRoot() ||
4648
0
             !doc->GetServoRestyleRoot()->IsElement() ||
4649
0
             !parent->IsElement() ||
4650
0
             BitsArePropagated(parent->AsElement(), aBits, doc->GetServoRestyleRoot()));
4651
0
  MOZ_ASSERT(doc->GetServoRestyleRootDirtyBits() & aBits);
4652
0
}
4653
4654
void
4655
Element::NoteDirtySubtreeForServo()
4656
0
{
4657
0
  MOZ_ASSERT(IsInComposedDoc());
4658
0
  MOZ_ASSERT(HasServoData());
4659
0
4660
0
  nsIDocument* doc = GetComposedDoc();
4661
0
  nsINode* existingRoot = doc->GetServoRestyleRoot();
4662
0
  uint32_t existingBits = existingRoot ? doc->GetServoRestyleRootDirtyBits() : 0;
4663
0
4664
0
  if (existingRoot &&
4665
0
      existingRoot->IsElement() &&
4666
0
      existingRoot != this &&
4667
0
      nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
4668
0
        existingRoot->AsElement(), this)) {
4669
0
    PropagateBits(existingRoot->AsElement()->GetFlattenedTreeParentElementForStyle(),
4670
0
                  existingBits,
4671
0
                  this,
4672
0
                  existingBits);
4673
0
4674
0
    doc->ClearServoRestyleRoot();
4675
0
  }
4676
0
4677
0
  NoteDirtyElement(this, existingBits | ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
4678
0
}
4679
4680
void
4681
Element::NoteDirtyForServo()
4682
0
{
4683
0
  NoteDirtyElement(this, ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
4684
0
}
4685
4686
void
4687
Element::NoteAnimationOnlyDirtyForServo()
4688
0
{
4689
0
  NoteDirtyElement(this, ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO);
4690
0
}
4691
4692
void
4693
Element::NoteDescendantsNeedFramesForServo()
4694
0
{
4695
0
  // Since lazy frame construction can be required for non-element nodes, this
4696
0
  // Note() method operates on the parent of the frame-requiring content, unlike
4697
0
  // the other Note() methods above (which operate directly on the element that
4698
0
  // needs processing).
4699
0
  NoteDirtyElement(this, NODE_DESCENDANTS_NEED_FRAMES);
4700
0
  SetFlags(NODE_DESCENDANTS_NEED_FRAMES);
4701
0
}