tor-browser

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

HTMLDetailsElement.cpp (8014B)


      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/HTMLDetailsElement.h"
      8 
      9 #include "mozilla/BuiltInStyleSheets.h"
     10 #include "mozilla/StaticPrefs_dom.h"
     11 #include "mozilla/dom/HTMLDetailsElementBinding.h"
     12 #include "mozilla/dom/HTMLSummaryElement.h"
     13 #include "mozilla/dom/ShadowRoot.h"
     14 #include "nsContentUtils.h"
     15 #include "nsTextNode.h"
     16 
     17 NS_IMPL_NS_NEW_HTML_ELEMENT(Details)
     18 
     19 namespace mozilla::dom {
     20 
     21 HTMLDetailsElement::~HTMLDetailsElement() = default;
     22 
     23 NS_IMPL_ELEMENT_CLONE(HTMLDetailsElement)
     24 
     25 HTMLDetailsElement::HTMLDetailsElement(already_AddRefed<NodeInfo>&& aNodeInfo)
     26    : nsGenericHTMLElement(std::move(aNodeInfo)) {
     27  SetupShadowTree();
     28 }
     29 
     30 HTMLSummaryElement* HTMLDetailsElement::GetFirstSummary() const {
     31  // XXX: Bug 1245032: Might want to cache the first summary element.
     32  for (nsIContent* child = nsINode::GetFirstChild(); child;
     33       child = child->GetNextSibling()) {
     34    if (auto* summary = HTMLSummaryElement::FromNode(child)) {
     35      return summary;
     36    }
     37  }
     38  return nullptr;
     39 }
     40 
     41 void HTMLDetailsElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
     42                                      const nsAttrValue* aValue,
     43                                      const nsAttrValue* aOldValue,
     44                                      nsIPrincipal* aMaybeScriptedPrincipal,
     45                                      bool aNotify) {
     46  if (aNameSpaceID == kNameSpaceID_None) {
     47    if (aName == nsGkAtoms::open) {
     48      bool wasOpen = !!aOldValue;
     49      bool isOpen = !!aValue;
     50      if (wasOpen != isOpen) {
     51        auto stringForState = [](bool aOpen) {
     52          return aOpen ? u"open"_ns : u"closed"_ns;
     53        };
     54        nsAutoString oldState;
     55        if (mToggleEventDispatcher) {
     56          oldState.Truncate();
     57          static_cast<ToggleEvent*>(mToggleEventDispatcher->mEvent.get())
     58              ->GetOldState(oldState);
     59          mToggleEventDispatcher->Cancel();
     60        } else {
     61          oldState.Assign(stringForState(wasOpen));
     62        }
     63        RefPtr<ToggleEvent> toggleEvent =
     64            CreateToggleEvent(u"toggle"_ns, oldState, stringForState(isOpen),
     65                              Cancelable::eNo, nullptr);
     66        mToggleEventDispatcher =
     67            new AsyncEventDispatcher(this, toggleEvent.forget());
     68        mToggleEventDispatcher->PostDOMEvent();
     69 
     70        if (isOpen) {
     71          CloseOtherElementsIfNeeded();
     72        }
     73        SetStates(ElementState::OPEN, isOpen);
     74      }
     75    } else if (aName == nsGkAtoms::name) {
     76      CloseElementIfNeeded();
     77    }
     78  }
     79 
     80  return nsGenericHTMLElement::AfterSetAttr(
     81      aNameSpaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify);
     82 }
     83 
     84 nsresult HTMLDetailsElement::BindToTree(BindContext& aContext,
     85                                        nsINode& aParent) {
     86  nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent);
     87  NS_ENSURE_SUCCESS(rv, rv);
     88 
     89  CloseElementIfNeeded();
     90 
     91  return NS_OK;
     92 }
     93 
     94 void HTMLDetailsElement::SetupShadowTree() {
     95  const bool kNotify = false;
     96  AttachAndSetUAShadowRoot(NotifyUAWidgetSetup::No);
     97  RefPtr<ShadowRoot> sr = GetShadowRoot();
     98  if (NS_WARN_IF(!sr)) {
     99    return;
    100  }
    101 
    102  nsNodeInfoManager* nim = OwnerDoc()->NodeInfoManager();
    103  RefPtr<NodeInfo> slotNodeInfo = nim->GetNodeInfo(
    104      nsGkAtoms::slot, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
    105  sr->AppendBuiltInStyleSheet(BuiltInStyleSheet::Details);
    106  {
    107    RefPtr<nsGenericHTMLElement> slot =
    108        NS_NewHTMLSlotElement(do_AddRef(slotNodeInfo));
    109    if (NS_WARN_IF(!slot)) {
    110      return;
    111    }
    112    slot->SetAttr(kNameSpaceID_None, nsGkAtoms::name,
    113                  u"internal-main-summary"_ns, kNotify);
    114    sr->AppendChildTo(slot, kNotify, IgnoreErrors());
    115 
    116    RefPtr<NodeInfo> summaryNodeInfo = nim->GetNodeInfo(
    117        nsGkAtoms::summary, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
    118    RefPtr<nsGenericHTMLElement> summary =
    119        NS_NewHTMLSummaryElement(summaryNodeInfo.forget());
    120    if (NS_WARN_IF(!summary)) {
    121      return;
    122    }
    123 
    124    nsAutoString defaultSummaryText;
    125    nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
    126                                            "DefaultSummary", OwnerDoc(),
    127                                            defaultSummaryText);
    128    RefPtr<nsTextNode> description = new (nim) nsTextNode(nim);
    129    description->SetText(defaultSummaryText, kNotify);
    130    summary->AppendChildTo(description, kNotify, IgnoreErrors());
    131 
    132    slot->AppendChildTo(summary, kNotify, IgnoreErrors());
    133  }
    134  {
    135    RefPtr<nsGenericHTMLElement> slot =
    136        NS_NewHTMLSlotElement(slotNodeInfo.forget());
    137    if (NS_WARN_IF(!slot)) {
    138      return;
    139    }
    140    if (StaticPrefs::layout_css_details_content_enabled()) {
    141      slot->SetPseudoElementType(PseudoStyleType::detailsContent);
    142    }
    143    sr->AppendChildTo(slot, kNotify, IgnoreErrors());
    144  }
    145 }
    146 
    147 void HTMLDetailsElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) {
    148  if (mToggleEventDispatcher == aEvent) {
    149    mToggleEventDispatcher = nullptr;
    150  }
    151 }
    152 
    153 JSObject* HTMLDetailsElement::WrapNode(JSContext* aCx,
    154                                       JS::Handle<JSObject*> aGivenProto) {
    155  return HTMLDetailsElement_Binding::Wrap(aCx, this, aGivenProto);
    156 }
    157 
    158 bool HTMLDetailsElement::IsValidCommandAction(Command aCommand) const {
    159  return nsGenericHTMLElement::IsValidCommandAction(aCommand) ||
    160         (StaticPrefs::dom_element_commandfor_on_details_enabled() &&
    161          (aCommand == Command::Toggle || aCommand == Command::Close ||
    162           aCommand == Command::Open));
    163 }
    164 
    165 bool HTMLDetailsElement::HandleCommandInternal(Element* aSource,
    166                                               Command aCommand,
    167                                               ErrorResult& aRv) {
    168  if (nsGenericHTMLElement::HandleCommandInternal(aSource, aCommand, aRv)) {
    169    return true;
    170  }
    171 
    172  MOZ_ASSERT(StaticPrefs::dom_element_commandfor_on_details_enabled());
    173  if (aCommand == Command::Toggle) {
    174    ToggleOpen();
    175    return true;
    176  }
    177  if (aCommand == Command::Close) {
    178    if (Open()) {
    179      SetOpen(false, IgnoreErrors());
    180    }
    181    return true;
    182  }
    183  if (aCommand == Command::Open) {
    184    if (!Open()) {
    185      SetOpen(true, IgnoreErrors());
    186    }
    187    return true;
    188  }
    189 
    190  return false;
    191 }
    192 
    193 void HTMLDetailsElement::CloseElementIfNeeded() {
    194  if (!StaticPrefs::dom_details_group_enabled()) {
    195    return;
    196  }
    197 
    198  if (!Open()) {
    199    return;
    200  }
    201 
    202  if (!HasName()) {
    203    return;
    204  }
    205 
    206  const RefPtr<nsAtom> name = GetParsedAttr(nsGkAtoms::name)->GetAsAtom();
    207  nsINode* const root = SubtreeRoot();
    208  for (nsINode* cur = root; cur; cur = cur->GetNextNode(root)) {
    209    if (!cur->HasName()) {
    210      continue;
    211    }
    212    if (auto* other = HTMLDetailsElement::FromNode(cur)) {
    213      if (other != this && other->Open() &&
    214          other->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, name,
    215                             eCaseMatters)) {
    216        SetOpen(false, IgnoreErrors());
    217        break;
    218      }
    219    }
    220  }
    221 }
    222 
    223 void HTMLDetailsElement::CloseOtherElementsIfNeeded() {
    224  if (!StaticPrefs::dom_details_group_enabled()) {
    225    return;
    226  }
    227 
    228  MOZ_ASSERT(Open());
    229 
    230  if (!HasName()) {
    231    return;
    232  }
    233 
    234  const RefPtr<nsAtom> name = GetParsedAttr(nsGkAtoms::name)->GetAsAtom();
    235  nsINode* const root = SubtreeRoot();
    236  for (nsINode* cur = root; cur; cur = cur->GetNextNode(root)) {
    237    if (!cur->HasName()) {
    238      continue;
    239    }
    240    if (auto* other = HTMLDetailsElement::FromNode(cur)) {
    241      if (other != this && other->Open() &&
    242          other->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, name,
    243                             eCaseMatters)) {
    244        RefPtr<HTMLDetailsElement> otherDetails = other;
    245        otherDetails->SetOpen(false, IgnoreErrors());
    246        break;
    247      }
    248    }
    249  }
    250 }
    251 
    252 }  // namespace mozilla::dom