tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HTMLElement.cpp (16687B)


      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 #include "mozilla/dom/HTMLElement.h"
      8 
      9 #include "mozilla/EventDispatcher.h"
     10 #include "mozilla/PresState.h"
     11 #include "mozilla/dom/CustomElementRegistry.h"
     12 #include "mozilla/dom/ElementInternalsBinding.h"
     13 #include "mozilla/dom/FormData.h"
     14 #include "mozilla/dom/FromParser.h"
     15 #include "mozilla/dom/HTMLElementBinding.h"
     16 #include "nsContentUtils.h"
     17 #include "nsGenericHTMLElement.h"
     18 #include "nsILayoutHistoryState.h"
     19 
     20 namespace mozilla::dom {
     21 
     22 HTMLElement::HTMLElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
     23                         FromParser aFromParser)
     24    : nsGenericHTMLFormElement(std::move(aNodeInfo)) {
     25  if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
     26    AddStatesSilently(ElementState::HAS_DIR_ATTR_LIKE_AUTO);
     27  }
     28 
     29  InhibitRestoration(!(aFromParser & FROM_PARSER_NETWORK));
     30 }
     31 
     32 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLElement, nsGenericHTMLFormElement)
     33 
     34 // QueryInterface implementation for HTMLElement
     35 
     36 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLElement)
     37  NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIFormControl, GetElementInternals())
     38  NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIConstraintValidation, GetElementInternals())
     39 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLFormElement)
     40 
     41 NS_IMPL_ADDREF_INHERITED(HTMLElement, nsGenericHTMLFormElement)
     42 NS_IMPL_RELEASE_INHERITED(HTMLElement, nsGenericHTMLFormElement)
     43 
     44 NS_IMPL_ELEMENT_CLONE(HTMLElement)
     45 
     46 JSObject* HTMLElement::WrapNode(JSContext* aCx,
     47                                JS::Handle<JSObject*> aGivenProto) {
     48  return dom::HTMLElement_Binding::Wrap(aCx, this, aGivenProto);
     49 }
     50 
     51 void HTMLElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
     52  if (IsDisabledForEvents(aVisitor.mEvent)) {
     53    // Do not process any DOM events if the element is disabled
     54    aVisitor.mCanHandle = false;
     55    return;
     56  }
     57 
     58  nsGenericHTMLFormElement::GetEventTargetParent(aVisitor);
     59 }
     60 
     61 nsINode* HTMLElement::GetScopeChainParent() const {
     62  if (IsFormAssociatedCustomElement()) {
     63    if (auto* form = GetFormInternal()) {
     64      return form;
     65    }
     66  }
     67  return nsGenericHTMLFormElement::GetScopeChainParent();
     68 }
     69 
     70 nsresult HTMLElement::BindToTree(BindContext& aContext, nsINode& aParent) {
     71  nsresult rv = nsGenericHTMLFormElement::BindToTree(aContext, aParent);
     72  NS_ENSURE_SUCCESS(rv, rv);
     73 
     74  UpdateBarredFromConstraintValidation();
     75  UpdateValidityElementStates(false);
     76  return rv;
     77 }
     78 
     79 void HTMLElement::UnbindFromTree(UnbindContext& aContext) {
     80  nsGenericHTMLFormElement::UnbindFromTree(aContext);
     81 
     82  UpdateBarredFromConstraintValidation();
     83  UpdateValidityElementStates(false);
     84 }
     85 
     86 void HTMLElement::DoneCreatingElement() {
     87  if (MOZ_UNLIKELY(IsFormAssociatedElement())) {
     88    MaybeRestoreFormAssociatedCustomElementState();
     89  }
     90 }
     91 
     92 void HTMLElement::SaveState() {
     93  if (MOZ_LIKELY(!IsFormAssociatedElement())) {
     94    return;
     95  }
     96 
     97  auto* internals = GetElementInternals();
     98 
     99  nsCString stateKey = internals->GetStateKey();
    100  if (stateKey.IsEmpty()) {
    101    return;
    102  }
    103 
    104  nsCOMPtr<nsILayoutHistoryState> history = GetLayoutHistory(false);
    105  if (!history) {
    106    return;
    107  }
    108 
    109  // Get the pres state for this key, if it doesn't exist, create one.
    110  PresState* result = history->GetState(stateKey);
    111  if (!result) {
    112    UniquePtr<PresState> newState = NewPresState();
    113    result = newState.get();
    114    history->AddState(stateKey, std::move(newState));
    115  }
    116 
    117  const auto& state = internals->GetFormState();
    118  const auto& value = internals->GetFormSubmissionValue();
    119  result->contentData() = CustomElementTuple(
    120      nsContentUtils::ConvertToCustomElementFormValue(value),
    121      nsContentUtils::ConvertToCustomElementFormValue(state));
    122 }
    123 
    124 void HTMLElement::MaybeRestoreFormAssociatedCustomElementState() {
    125  MOZ_ASSERT(IsFormAssociatedElement());
    126 
    127  if (HasFlag(HTML_ELEMENT_INHIBIT_RESTORATION)) {
    128    return;
    129  }
    130 
    131  auto* internals = GetElementInternals();
    132  if (internals->GetStateKey().IsEmpty()) {
    133    Document* doc = GetUncomposedDoc();
    134    nsCString stateKey;
    135    nsContentUtils::GenerateStateKey(this, doc, stateKey);
    136    internals->SetStateKey(std::move(stateKey));
    137 
    138    RestoreFormAssociatedCustomElementState();
    139  }
    140 }
    141 
    142 void HTMLElement::RestoreFormAssociatedCustomElementState() {
    143  MOZ_ASSERT(IsFormAssociatedElement());
    144 
    145  auto* internals = GetElementInternals();
    146 
    147  const nsCString& stateKey = internals->GetStateKey();
    148  if (stateKey.IsEmpty()) {
    149    return;
    150  }
    151  nsCOMPtr<nsILayoutHistoryState> history = GetLayoutHistory(true);
    152  if (!history) {
    153    return;
    154  }
    155  PresState* result = history->GetState(stateKey);
    156  if (!result) {
    157    return;
    158  }
    159  auto& content = result->contentData();
    160  if (content.type() != PresContentData::TCustomElementTuple) {
    161    return;
    162  }
    163 
    164  auto& ce = content.get_CustomElementTuple();
    165  nsCOMPtr<nsIGlobalObject> global = GetOwnerDocument()->GetOwnerGlobal();
    166  internals->RestoreFormValue(
    167      nsContentUtils::ExtractFormAssociatedCustomElementValue(global,
    168                                                              ce.value()),
    169      nsContentUtils::ExtractFormAssociatedCustomElementValue(global,
    170                                                              ce.state()));
    171 }
    172 
    173 void HTMLElement::InhibitRestoration(bool aShouldInhibit) {
    174  if (aShouldInhibit) {
    175    SetFlags(HTML_ELEMENT_INHIBIT_RESTORATION);
    176  } else {
    177    UnsetFlags(HTML_ELEMENT_INHIBIT_RESTORATION);
    178  }
    179 }
    180 
    181 void HTMLElement::SetCustomElementDefinition(
    182    CustomElementDefinition* aDefinition) {
    183  nsGenericHTMLFormElement::SetCustomElementDefinition(aDefinition);
    184  // Always create an ElementInternal for form-associated custom element as the
    185  // Form related implementation lives in ElementInternal which implements
    186  // nsIFormControl. It is okay for the attachElementInternal API as there is a
    187  // separated flag for whether attachElementInternal is called.
    188  if (aDefinition && !aDefinition->IsCustomBuiltIn() &&
    189      aDefinition->mFormAssociated) {
    190    CustomElementData* data = GetCustomElementData();
    191    MOZ_ASSERT(data);
    192    auto* internals = data->GetOrCreateElementInternals(this);
    193 
    194    // This is for the case that script constructs a custom element directly,
    195    // e.g. via new MyCustomElement(), where the upgrade steps won't be ran to
    196    // update the disabled state in UpdateFormOwner().
    197    if (data->mState == CustomElementData::State::eCustom) {
    198      UpdateDisabledState(true);
    199    } else if (!HasFlag(HTML_ELEMENT_INHIBIT_RESTORATION)) {
    200      internals->InitializeControlNumber();
    201    }
    202  }
    203 }
    204 
    205 // https://html.spec.whatwg.org/commit-snapshots/53bc3803433e1c817918b83e8a84f3db900031dd/#dom-attachinternals
    206 already_AddRefed<ElementInternals> HTMLElement::AttachInternals(
    207    ErrorResult& aRv) {
    208  CustomElementData* ceData = GetCustomElementData();
    209 
    210  // 1. If element's is value is not null, then throw a "NotSupportedError"
    211  //    DOMException.
    212  if (nsAtom* isAtom = ceData ? ceData->GetIs(this) : nullptr) {
    213    aRv.ThrowNotSupportedError(nsPrintfCString(
    214        "Cannot attach ElementInternals to a customized built-in element "
    215        "'%s'",
    216        NS_ConvertUTF16toUTF8(isAtom->GetUTF16String()).get()));
    217    return nullptr;
    218  }
    219 
    220  // 2. Let definition be the result of looking up a custom element definition
    221  //    given element's node document, its namespace, its local name, and null
    222  //    as is value.
    223  nsAtom* nameAtom = NodeInfo()->NameAtom();
    224  CustomElementDefinition* definition = nullptr;
    225  if (ceData) {
    226    definition = ceData->GetCustomElementDefinition();
    227 
    228    // If the definition is null, the element possible hasn't yet upgraded.
    229    // Fallback to use LookupCustomElementDefinition to find its definition.
    230    if (!definition) {
    231      definition = nsContentUtils::LookupCustomElementDefinition(
    232          NodeInfo()->GetDocument(), nameAtom, NodeInfo()->NamespaceID(),
    233          ceData->GetCustomElementType());
    234    }
    235  }
    236 
    237  // 3. If definition is null, then throw an "NotSupportedError" DOMException.
    238  if (!definition) {
    239    aRv.ThrowNotSupportedError(nsPrintfCString(
    240        "Cannot attach ElementInternals to a non-custom element '%s'",
    241        NS_ConvertUTF16toUTF8(nameAtom->GetUTF16String()).get()));
    242    return nullptr;
    243  }
    244 
    245  // 4. If definition's disable internals is true, then throw a
    246  //    "NotSupportedError" DOMException.
    247  if (definition->mDisableInternals) {
    248    aRv.ThrowNotSupportedError(nsPrintfCString(
    249        "AttachInternal() to '%s' is disabled by disabledFeatures",
    250        NS_ConvertUTF16toUTF8(nameAtom->GetUTF16String()).get()));
    251    return nullptr;
    252  }
    253 
    254  // If this is not a custom element, i.e. ceData is nullptr, we are unable to
    255  // find a definition and should return earlier above.
    256  MOZ_ASSERT(ceData);
    257 
    258  // 5. If element's attached internals is true, then throw an
    259  //    "NotSupportedError" DOMException.
    260  if (ceData->HasAttachedInternals()) {
    261    aRv.ThrowNotSupportedError(nsPrintfCString(
    262        "AttachInternals() has already been called from '%s'",
    263        NS_ConvertUTF16toUTF8(nameAtom->GetUTF16String()).get()));
    264    return nullptr;
    265  }
    266 
    267  // 6. If element's custom element state is not "precustomized" or "custom",
    268  //    then throw a "NotSupportedError" DOMException.
    269  if (ceData->mState != CustomElementData::State::ePrecustomized &&
    270      ceData->mState != CustomElementData::State::eCustom) {
    271    aRv.ThrowNotSupportedError(
    272        R"(Custom element state is not "precustomized" or "custom".)");
    273    return nullptr;
    274  }
    275 
    276  // 7. Set element's attached internals to true.
    277  ceData->AttachedInternals();
    278 
    279  // 8. Create a new ElementInternals instance targeting element, and return it.
    280  return do_AddRef(ceData->GetOrCreateElementInternals(this));
    281 }
    282 
    283 void HTMLElement::AfterClearForm(bool aUnbindOrDelete) {
    284  // No need to enqueue formAssociated callback if we aren't releasing or
    285  // unbinding from tree, UpdateFormOwner() will handle it.
    286  if (aUnbindOrDelete) {
    287    MOZ_ASSERT(IsFormAssociatedElement());
    288    nsContentUtils::EnqueueLifecycleCallback(
    289        ElementCallbackType::eFormAssociated, this, {});
    290  }
    291 }
    292 
    293 void HTMLElement::UpdateFormOwner() {
    294  MOZ_ASSERT(IsFormAssociatedElement());
    295 
    296  // If @form is set, the element *has* to be in a composed document,
    297  // otherwise it wouldn't be possible to find an element with the
    298  // corresponding id. If @form isn't set, the element *has* to have a parent,
    299  // otherwise it wouldn't be possible to find a form ancestor. We should not
    300  // call UpdateFormOwner if none of these conditions are fulfilled.
    301  if (HasAttr(nsGkAtoms::form) ? IsInComposedDoc() : !!GetParent()) {
    302    UpdateFormOwner(true, nullptr);
    303  }
    304  UpdateFieldSet(true);
    305  UpdateDisabledState(true);
    306  UpdateBarredFromConstraintValidation();
    307  UpdateValidityElementStates(true);
    308 
    309  MaybeRestoreFormAssociatedCustomElementState();
    310 }
    311 
    312 bool HTMLElement::IsDisabledForEvents(WidgetEvent* aEvent) {
    313  if (IsFormAssociatedElement()) {
    314    return IsElementDisabledForEvents(aEvent, GetPrimaryFrame());
    315  }
    316 
    317  return false;
    318 }
    319 
    320 void HTMLElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
    321                               const nsAttrValue* aValue,
    322                               const nsAttrValue* aOldValue,
    323                               nsIPrincipal* aMaybeScriptedPrincipal,
    324                               bool aNotify) {
    325  if (aNameSpaceID == kNameSpaceID_None &&
    326      (aName == nsGkAtoms::disabled || aName == nsGkAtoms::readonly)) {
    327    if (aName == nsGkAtoms::disabled) {
    328      // This *has* to be called *before* validity state check because
    329      // UpdateBarredFromConstraintValidation depend on our disabled state.
    330      UpdateDisabledState(aNotify);
    331    }
    332    if (aName == nsGkAtoms::readonly && !!aValue != !!aOldValue) {
    333      UpdateReadOnlyState(aNotify);
    334    }
    335    UpdateBarredFromConstraintValidation();
    336    UpdateValidityElementStates(aNotify);
    337  }
    338 
    339  return nsGenericHTMLFormElement::AfterSetAttr(
    340      aNameSpaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify);
    341 }
    342 
    343 void HTMLElement::UpdateValidityElementStates(bool aNotify) {
    344  AutoStateChangeNotifier notifier(*this, aNotify);
    345  RemoveStatesSilently(ElementState::VALIDITY_STATES);
    346  ElementInternals* internals = GetElementInternals();
    347  if (!internals || !internals->IsCandidateForConstraintValidation()) {
    348    return;
    349  }
    350  if (internals->IsValid()) {
    351    AddStatesSilently(ElementState::VALID | ElementState::USER_VALID);
    352  } else {
    353    AddStatesSilently(ElementState::INVALID | ElementState::USER_INVALID);
    354  }
    355 }
    356 
    357 void HTMLElement::SetFormInternal(HTMLFormElement* aForm, bool aBindToTree) {
    358  ElementInternals* internals = GetElementInternals();
    359  MOZ_ASSERT(internals);
    360  internals->SetForm(aForm);
    361 }
    362 
    363 HTMLFormElement* HTMLElement::GetFormInternal() const {
    364  ElementInternals* internals = GetElementInternals();
    365  MOZ_ASSERT(internals);
    366  return internals->GetForm();
    367 }
    368 
    369 void HTMLElement::SetFieldSetInternal(HTMLFieldSetElement* aFieldset) {
    370  ElementInternals* internals = GetElementInternals();
    371  MOZ_ASSERT(internals);
    372  internals->SetFieldSet(aFieldset);
    373 }
    374 
    375 HTMLFieldSetElement* HTMLElement::GetFieldSetInternal() const {
    376  ElementInternals* internals = GetElementInternals();
    377  MOZ_ASSERT(internals);
    378  return internals->GetFieldSet();
    379 }
    380 
    381 bool HTMLElement::CanBeDisabled() const { return IsFormAssociatedElement(); }
    382 
    383 void HTMLElement::UpdateDisabledState(bool aNotify) {
    384  bool oldState = IsDisabled();
    385  nsGenericHTMLFormElement::UpdateDisabledState(aNotify);
    386  if (oldState != IsDisabled()) {
    387    MOZ_ASSERT(IsFormAssociatedElement());
    388    LifecycleCallbackArgs args;
    389    args.mDisabled = !oldState;
    390    nsContentUtils::EnqueueLifecycleCallback(ElementCallbackType::eFormDisabled,
    391                                             this, args);
    392  }
    393 }
    394 
    395 void HTMLElement::UpdateFormOwner(bool aBindToTree, Element* aFormIdElement) {
    396  HTMLFormElement* oldForm = GetFormInternal();
    397  nsGenericHTMLFormElement::UpdateFormOwner(aBindToTree, aFormIdElement);
    398  HTMLFormElement* newForm = GetFormInternal();
    399  if (newForm != oldForm) {
    400    LifecycleCallbackArgs args;
    401    args.mForm = newForm;
    402    nsContentUtils::EnqueueLifecycleCallback(
    403        ElementCallbackType::eFormAssociated, this, args);
    404  }
    405 }
    406 
    407 bool HTMLElement::IsFormAssociatedElement() const {
    408  CustomElementData* data = GetCustomElementData();
    409  return data && data->IsFormAssociated();
    410 }
    411 
    412 void HTMLElement::FieldSetDisabledChanged(bool aNotify) {
    413  // This *has* to be called *before* UpdateBarredFromConstraintValidation
    414  // because this function depend on our disabled state.
    415  nsGenericHTMLFormElement::FieldSetDisabledChanged(aNotify);
    416 
    417  UpdateBarredFromConstraintValidation();
    418  UpdateValidityElementStates(aNotify);
    419 }
    420 
    421 ElementInternals* HTMLElement::GetElementInternals() const {
    422  CustomElementData* data = GetCustomElementData();
    423  if (!data || !data->IsFormAssociated()) {
    424    // If the element is not a form associated custom element, it should not be
    425    // able to be QueryInterfaced to nsIFormControl and could not perform
    426    // the form operation, either, so we return nullptr here.
    427    return nullptr;
    428  }
    429 
    430  return data->GetElementInternals();
    431 }
    432 
    433 nsIFormControl* HTMLElement::GetAsFormControl() {
    434  return GetElementInternals();
    435 }
    436 
    437 const nsIFormControl* HTMLElement::GetAsFormControl() const {
    438  return GetElementInternals();
    439 }
    440 
    441 void HTMLElement::UpdateBarredFromConstraintValidation() {
    442  CustomElementData* data = GetCustomElementData();
    443  if (data && data->IsFormAssociated()) {
    444    ElementInternals* internals = data->GetElementInternals();
    445    MOZ_ASSERT(internals);
    446    internals->UpdateBarredFromConstraintValidation();
    447  }
    448 }
    449 
    450 }  // namespace mozilla::dom
    451 
    452 // Here, we expand 'NS_IMPL_NS_NEW_HTML_ELEMENT()' by hand.
    453 // (Calling the macro directly (with no args) produces compiler warnings.)
    454 nsGenericHTMLElement* NS_NewHTMLElement(
    455    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
    456    mozilla::dom::FromParser aFromParser) {
    457  RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
    458  auto* nim = nodeInfo->NodeInfoManager();
    459  return new (nim) mozilla::dom::HTMLElement(nodeInfo.forget(), aFromParser);
    460 }
    461 
    462 // Distinct from the above in order to have function pointer that compared
    463 // unequal to a function pointer to the above.
    464 nsGenericHTMLElement* NS_NewCustomElement(
    465    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
    466    mozilla::dom::FromParser aFromParser) {
    467  RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
    468  auto* nim = nodeInfo->NodeInfoManager();
    469  return new (nim) mozilla::dom::HTMLElement(nodeInfo.forget(), aFromParser);
    470 }