tor-browser

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

HighlightRegistry.cpp (8529B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      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 "HighlightRegistry.h"
      8 
      9 #include "Document.h"
     10 #include "Highlight.h"
     11 #include "PresShell.h"
     12 #include "mozilla/CompactPair.h"
     13 #include "mozilla/ErrorResult.h"
     14 #include "mozilla/dom/HighlightBinding.h"
     15 #include "nsAtom.h"
     16 #include "nsCycleCollectionParticipant.h"
     17 #include "nsFrameSelection.h"
     18 
     19 namespace mozilla::dom {
     20 
     21 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(HighlightRegistry)
     22 
     23 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HighlightRegistry)
     24  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
     25  for (auto const& iter : tmp->mHighlightsOrdered) {
     26    iter.second()->RemoveFromHighlightRegistry(*tmp, *iter.first());
     27  }
     28  NS_IMPL_CYCLE_COLLECTION_UNLINK(mHighlightsOrdered)
     29  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     30 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     31 
     32 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HighlightRegistry)
     33  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
     34  for (size_t i = 0; i < tmp->mHighlightsOrdered.Length(); ++i) {
     35    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHighlightsOrdered[i].second())
     36  }
     37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     38 
     39 NS_IMPL_CYCLE_COLLECTING_ADDREF(HighlightRegistry)
     40 NS_IMPL_CYCLE_COLLECTING_RELEASE(HighlightRegistry)
     41 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HighlightRegistry)
     42  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     43  NS_INTERFACE_MAP_ENTRY(nsISupports)
     44 NS_INTERFACE_MAP_END
     45 
     46 HighlightRegistry::HighlightRegistry(Document* aDocument)
     47    : mDocument(aDocument) {}
     48 
     49 HighlightRegistry::~HighlightRegistry() {
     50  for (auto const& iter : mHighlightsOrdered) {
     51    iter.second()->RemoveFromHighlightRegistry(*this, *iter.first());
     52  }
     53 }
     54 
     55 JSObject* HighlightRegistry::WrapObject(JSContext* aCx,
     56                                        JS::Handle<JSObject*> aGivenProto) {
     57  return HighlightRegistry_Binding::Wrap(aCx, this, aGivenProto);
     58 }
     59 
     60 void HighlightRegistry::MaybeAddRangeToHighlightSelection(
     61    AbstractRange& aRange, Highlight& aHighlight) {
     62  RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
     63  if (!frameSelection) {
     64    return;
     65  }
     66  MOZ_ASSERT(frameSelection->GetPresShell());
     67  if (!frameSelection->GetPresShell()->GetDocument() ||
     68      frameSelection->GetPresShell()->GetDocument() !=
     69          aRange.GetComposedDocOfContainers()) {
     70    // ranges that belong to a different document must not be added.
     71    return;
     72  }
     73  for (auto const& iter : mHighlightsOrdered) {
     74    if (iter.second() != &aHighlight) {
     75      continue;
     76    }
     77 
     78    const RefPtr<nsAtom> highlightName = iter.first();
     79    frameSelection->AddHighlightSelectionRange(highlightName, aHighlight,
     80                                               aRange);
     81  }
     82 }
     83 
     84 void HighlightRegistry::MaybeRemoveRangeFromHighlightSelection(
     85    AbstractRange& aRange, Highlight& aHighlight) {
     86  RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
     87  if (!frameSelection) {
     88    return;
     89  }
     90  MOZ_ASSERT(frameSelection->GetPresShell());
     91 
     92  for (auto const& iter : mHighlightsOrdered) {
     93    if (iter.second() != &aHighlight) {
     94      continue;
     95    }
     96 
     97    const RefPtr<nsAtom> highlightName = iter.first();
     98    frameSelection->RemoveHighlightSelectionRange(highlightName, aRange);
     99  }
    100 }
    101 
    102 void HighlightRegistry::RemoveHighlightSelection(Highlight& aHighlight) {
    103  RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
    104  if (!frameSelection) {
    105    return;
    106  }
    107  for (auto const& iter : mHighlightsOrdered) {
    108    if (iter.second() != &aHighlight) {
    109      continue;
    110    }
    111 
    112    const RefPtr<nsAtom> highlightName = iter.first();
    113    frameSelection->RemoveHighlightSelection(highlightName);
    114  }
    115 }
    116 
    117 void HighlightRegistry::RepaintHighlightSelection(Highlight& aHighlight) {
    118  RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
    119  if (!frameSelection) {
    120    return;
    121  }
    122  for (auto const& iter : mHighlightsOrdered) {
    123    if (iter.second() != &aHighlight) {
    124      continue;
    125    }
    126 
    127    const RefPtr<nsAtom> highlightName = iter.first();
    128    frameSelection->RepaintHighlightSelection(highlightName);
    129  }
    130 }
    131 
    132 void HighlightRegistry::AddHighlightSelectionsToFrameSelection() {
    133  if (mHighlightsOrdered.IsEmpty()) {
    134    return;
    135  }
    136  RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
    137  if (!frameSelection) {
    138    return;
    139  }
    140  for (auto const& iter : mHighlightsOrdered) {
    141    RefPtr<nsAtom> highlightName = iter.first();
    142    RefPtr<Highlight> highlight = iter.second();
    143    frameSelection->AddHighlightSelection(highlightName, *highlight);
    144  }
    145 }
    146 
    147 HighlightRegistry* HighlightRegistry::Set(const nsAString& aKey,
    148                                          Highlight& aValue, ErrorResult& aRv) {
    149  // manually check if the highlight `aKey` is already registered to be able to
    150  // provide a fast path later that avoids calling `std::find_if()`.
    151  const bool highlightAlreadyPresent =
    152      HighlightRegistry_Binding::MaplikeHelpers::Has(this, aKey, aRv);
    153  if (aRv.Failed()) {
    154    return this;
    155  }
    156  HighlightRegistry_Binding::MaplikeHelpers::Set(this, aKey, aValue, aRv);
    157  if (aRv.Failed()) {
    158    return this;
    159  }
    160  RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
    161  RefPtr<nsAtom> highlightNameAtom = NS_AtomizeMainThread(aKey);
    162  if (highlightAlreadyPresent) {
    163    // If the highlight named `aKey` was present before, replace its value.
    164    auto foundIter =
    165        std::find_if(mHighlightsOrdered.begin(), mHighlightsOrdered.end(),
    166                     [&highlightNameAtom](auto const& aElm) {
    167                       return aElm.first() == highlightNameAtom;
    168                     });
    169    MOZ_ASSERT(foundIter != mHighlightsOrdered.end(),
    170               "webIDL maplike and DOM mirror are not in sync");
    171    foundIter->second()->RemoveFromHighlightRegistry(*this, *highlightNameAtom);
    172    if (frameSelection) {
    173      frameSelection->RemoveHighlightSelection(highlightNameAtom);
    174    }
    175    foundIter->second() = &aValue;
    176  } else {
    177    mHighlightsOrdered.AppendElement(
    178        CompactPair<RefPtr<nsAtom>, RefPtr<Highlight>>(highlightNameAtom,
    179                                                       &aValue));
    180  }
    181  aValue.AddToHighlightRegistry(*this, *highlightNameAtom);
    182  if (frameSelection) {
    183    frameSelection->AddHighlightSelection(highlightNameAtom, aValue);
    184  }
    185  return this;
    186 }
    187 
    188 void HighlightRegistry::Clear(ErrorResult& aRv) {
    189  HighlightRegistry_Binding::MaplikeHelpers::Clear(this, aRv);
    190  if (aRv.Failed()) {
    191    return;
    192  }
    193  auto frameSelection = GetFrameSelection();
    194  AutoFrameSelectionBatcher batcher(__FUNCTION__);
    195  batcher.AddFrameSelection(frameSelection);
    196  for (auto const& iter : mHighlightsOrdered) {
    197    const RefPtr<nsAtom>& highlightName = iter.first();
    198    const RefPtr<Highlight>& highlight = iter.second();
    199    highlight->RemoveFromHighlightRegistry(*this, *highlightName);
    200    if (frameSelection) {
    201      // The selection batcher makes sure that no script is run in this call.
    202      // However, `nsFrameSelection::RemoveHighlightSelection` is marked
    203      // `MOZ_CAN_RUN_SCRIPT`, therefore `MOZ_KnownLive` is needed regardless.
    204      frameSelection->RemoveHighlightSelection(MOZ_KnownLive(highlightName));
    205    }
    206  }
    207 
    208  mHighlightsOrdered.Clear();
    209 }
    210 
    211 bool HighlightRegistry::Delete(const nsAString& aKey, ErrorResult& aRv) {
    212  if (!HighlightRegistry_Binding::MaplikeHelpers::Delete(this, aKey, aRv)) {
    213    return false;
    214  }
    215  RefPtr<nsAtom> highlightNameAtom = NS_AtomizeMainThread(aKey);
    216  auto foundIter =
    217      std::find_if(mHighlightsOrdered.cbegin(), mHighlightsOrdered.cend(),
    218                   [&highlightNameAtom](auto const& aElm) {
    219                     return aElm.first() == highlightNameAtom;
    220                   });
    221  MOZ_ASSERT(foundIter != mHighlightsOrdered.cend(),
    222             "HighlightRegistry: maplike and internal data are out of sync!");
    223 
    224  RefPtr<Highlight> highlight = foundIter->second();
    225  mHighlightsOrdered.RemoveElementAt(foundIter);
    226 
    227  if (auto frameSelection = GetFrameSelection()) {
    228    frameSelection->RemoveHighlightSelection(highlightNameAtom);
    229  }
    230  highlight->RemoveFromHighlightRegistry(*this, *highlightNameAtom);
    231  return true;
    232 }
    233 
    234 RefPtr<nsFrameSelection> HighlightRegistry::GetFrameSelection() {
    235  return RefPtr<nsFrameSelection>(
    236      mDocument->GetPresShell() ? mDocument->GetPresShell()->FrameSelection()
    237                                : nullptr);
    238 }
    239 
    240 }  // namespace mozilla::dom