tor-browser

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

PlaceholderTransaction.cpp (13823B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "PlaceholderTransaction.h"
      7 
      8 #include <utility>
      9 
     10 #include "CompositionTransaction.h"
     11 #include "mozilla/EditorBase.h"
     12 #include "mozilla/Logging.h"
     13 #include "mozilla/dom/Selection.h"
     14 #include "nsGkAtoms.h"
     15 #include "nsQueryObject.h"
     16 
     17 namespace mozilla {
     18 
     19 using namespace dom;
     20 
     21 PlaceholderTransaction::PlaceholderTransaction(
     22    EditorBase& aEditorBase, nsStaticAtom& aName,
     23    Maybe<SelectionState>&& aSelState)
     24    : mEditorBase(&aEditorBase),
     25      mCompositionTransaction(nullptr),
     26      mStartSel(*std::move(aSelState)),
     27      mAbsorb(true),
     28      mCommitted(false) {
     29  mName = &aName;
     30 }
     31 
     32 NS_IMPL_CYCLE_COLLECTION_CLASS(PlaceholderTransaction)
     33 
     34 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PlaceholderTransaction,
     35                                                EditAggregateTransaction)
     36  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase);
     37  NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartSel);
     38  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndSel);
     39 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     40 
     41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PlaceholderTransaction,
     42                                                  EditAggregateTransaction)
     43  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase);
     44  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartSel);
     45  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndSel);
     46 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     47 
     48 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PlaceholderTransaction)
     49 NS_INTERFACE_MAP_END_INHERITING(EditAggregateTransaction)
     50 
     51 NS_IMPL_ADDREF_INHERITED(PlaceholderTransaction, EditAggregateTransaction)
     52 NS_IMPL_RELEASE_INHERITED(PlaceholderTransaction, EditAggregateTransaction)
     53 
     54 void PlaceholderTransaction::AppendChild(EditTransactionBase& aTransaction) {
     55  mChildren.AppendElement(aTransaction);
     56 }
     57 
     58 NS_IMETHODIMP PlaceholderTransaction::DoTransaction() {
     59  MOZ_LOG(
     60      GetLogModule(), LogLevel::Info,
     61      ("%p PlaceholderTransaction::%s this={ mName=%s }", this, __FUNCTION__,
     62       nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
     63  return NS_OK;
     64 }
     65 
     66 NS_IMETHODIMP PlaceholderTransaction::UndoTransaction() {
     67  MOZ_LOG(GetLogModule(), LogLevel::Info,
     68          ("%p PlaceholderTransaction::%s this={ mName=%s } "
     69           "Start==============================",
     70           this, __FUNCTION__,
     71           nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
     72 
     73  if (NS_WARN_IF(!mEditorBase)) {
     74    return NS_ERROR_NOT_INITIALIZED;
     75  }
     76 
     77  // Undo transactions.
     78  nsresult rv = EditAggregateTransaction::UndoTransaction();
     79  if (NS_FAILED(rv)) {
     80    NS_WARNING("EditAggregateTransaction::UndoTransaction() failed");
     81    return rv;
     82  }
     83 
     84  // now restore selection
     85  RefPtr<Selection> selection = mEditorBase->GetSelection();
     86  if (NS_WARN_IF(!selection)) {
     87    return NS_ERROR_FAILURE;
     88  }
     89  rv = mStartSel.RestoreSelection(*selection);
     90  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     91                       "SelectionState::RestoreSelection() failed");
     92 
     93  MOZ_LOG(GetLogModule(), LogLevel::Info,
     94          ("%p PlaceholderTransaction::%s this={ mName=%s } "
     95           "End==============================",
     96           this, __FUNCTION__,
     97           nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
     98  return rv;
     99 }
    100 
    101 NS_IMETHODIMP PlaceholderTransaction::RedoTransaction() {
    102  MOZ_LOG(GetLogModule(), LogLevel::Info,
    103          ("%p PlaceholderTransaction::%s this={ mName=%s } "
    104           "Start==============================",
    105           this, __FUNCTION__,
    106           nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
    107 
    108  if (NS_WARN_IF(!mEditorBase)) {
    109    return NS_ERROR_NOT_INITIALIZED;
    110  }
    111 
    112  // Redo transactions.
    113  nsresult rv = EditAggregateTransaction::RedoTransaction();
    114  if (NS_FAILED(rv)) {
    115    NS_WARNING("EditAggregateTransaction::RedoTransaction() failed");
    116    return rv;
    117  }
    118 
    119  // now restore selection
    120  RefPtr<Selection> selection = mEditorBase->GetSelection();
    121  if (NS_WARN_IF(!selection)) {
    122    return NS_ERROR_FAILURE;
    123  }
    124  rv = mEndSel.RestoreSelection(*selection);
    125  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    126                       "SelectionState::RestoreSelection() failed");
    127  MOZ_LOG(GetLogModule(), LogLevel::Info,
    128          ("%p PlaceholderTransaction::%s this={ mName=%s } "
    129           "End==============================",
    130           this, __FUNCTION__,
    131           nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
    132  return rv;
    133 }
    134 
    135 NS_IMETHODIMP PlaceholderTransaction::Merge(nsITransaction* aOtherTransaction,
    136                                            bool* aDidMerge) {
    137  if (NS_WARN_IF(!aDidMerge) || NS_WARN_IF(!aOtherTransaction)) {
    138    return NS_ERROR_INVALID_ARG;
    139  }
    140 
    141  // set out param default value
    142  *aDidMerge = false;
    143 
    144  if (mForwardingTransaction) {
    145    MOZ_ASSERT_UNREACHABLE(
    146        "tried to merge into a placeholder that was in "
    147        "forwarding mode!");
    148    return NS_ERROR_FAILURE;
    149  }
    150 
    151  RefPtr<EditTransactionBase> otherTransactionBase =
    152      aOtherTransaction->GetAsEditTransactionBase();
    153  if (!otherTransactionBase) {
    154    MOZ_LOG(GetLogModule(), LogLevel::Debug,
    155            ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
    156             "mName=%s } returned false due to non edit transaction",
    157             this, __FUNCTION__, aOtherTransaction,
    158             nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
    159    return NS_OK;
    160  }
    161 
    162  // We are absorbing all transactions if mAbsorb is lit.
    163  if (mAbsorb) {
    164    if (CompositionTransaction* otherCompositionTransaction =
    165            otherTransactionBase->GetAsCompositionTransaction()) {
    166      // special handling for CompositionTransaction's: they need to merge with
    167      // any previous CompositionTransaction in this placeholder, if possible.
    168      if (!mCompositionTransaction) {
    169        // this is the first IME txn in the placeholder
    170        mCompositionTransaction = otherCompositionTransaction;
    171        AppendChild(*otherCompositionTransaction);
    172      } else {
    173        bool didMerge;
    174        mCompositionTransaction->Merge(otherCompositionTransaction, &didMerge);
    175        if (!didMerge) {
    176          // it wouldn't merge.  Earlier IME txn is already committed and will
    177          // not absorb further IME txns.  So just stack this one after it
    178          // and remember it as a candidate for further merges.
    179          mCompositionTransaction = otherCompositionTransaction;
    180          AppendChild(*otherCompositionTransaction);
    181        }
    182      }
    183    } else {
    184      PlaceholderTransaction* otherPlaceholderTransaction =
    185          otherTransactionBase->GetAsPlaceholderTransaction();
    186      if (!otherPlaceholderTransaction) {
    187        // See bug 171243: just drop incoming placeholders on the floor.
    188        // Their children will be swallowed by this preexisting one.
    189        AppendChild(*otherTransactionBase);
    190      }
    191    }
    192    *aDidMerge = true;
    193    //  RememberEndingSelection();
    194    //  efficiency hack: no need to remember selection here, as we haven't yet
    195    //  finished the initial batch and we know we will be told when the batch
    196    //  ends. we can remeber the selection then.
    197    return NS_OK;
    198  }
    199 
    200  // merge typing or IME or deletion transactions if the selection matches
    201  if (mCommitted ||
    202      (mName != nsGkAtoms::TypingTxnName && mName != nsGkAtoms::IMETxnName &&
    203       mName != nsGkAtoms::DeleteTxnName)) {
    204    MOZ_LOG(GetLogModule(), LogLevel::Debug,
    205            ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
    206             "mName=%s } returned false due to non mergable transaction",
    207             this, __FUNCTION__, aOtherTransaction,
    208             nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
    209    return NS_OK;
    210  }
    211 
    212  PlaceholderTransaction* otherPlaceholderTransaction =
    213      otherTransactionBase->GetAsPlaceholderTransaction();
    214  if (!otherPlaceholderTransaction) {
    215    MOZ_LOG(GetLogModule(), LogLevel::Debug,
    216            ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
    217             "mName=%s } returned false due to non placeholder transaction",
    218             this, __FUNCTION__, aOtherTransaction,
    219             nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
    220    return NS_OK;
    221  }
    222 
    223  RefPtr<nsAtom> otherTransactionName = otherPlaceholderTransaction->GetName();
    224  if (!otherTransactionName || otherTransactionName == nsGkAtoms::_empty ||
    225      otherTransactionName != mName) {
    226    MOZ_LOG(GetLogModule(), LogLevel::Debug,
    227            ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
    228             "mName=%s } returned false due to non mergable placeholder "
    229             "transaction",
    230             this, __FUNCTION__, aOtherTransaction,
    231             nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
    232    return NS_OK;
    233  }
    234 
    235  // check if start selection of next placeholder matches
    236  // end selection of this placeholder
    237  // XXX Theese checks seem wrong.  The ending selection is initialized with
    238  //     actual Selection rather than expected Selection.  Therefore, even when
    239  //     web apps modifies Selection, we don't merge mergable transactions.
    240 
    241  // If the new transaction's starting Selection is not a caret, we shouldn't be
    242  // merged with it because it's probably caused deleting the selection.
    243  if (!otherPlaceholderTransaction->mStartSel.HasOnlyCollapsedRange()) {
    244    MOZ_LOG(GetLogModule(), LogLevel::Debug,
    245            ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
    246             "mName=%s } returned false due to not collapsed selection at "
    247             "start of new transactions",
    248             this, __FUNCTION__, aOtherTransaction,
    249             nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
    250    return NS_OK;
    251  }
    252 
    253  // If our ending Selection is not a caret, we should not be merged with it
    254  // because we probably changed format of a block or style of text.
    255  if (!mEndSel.HasOnlyCollapsedRange()) {
    256    MOZ_LOG(GetLogModule(), LogLevel::Debug,
    257            ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
    258             "mName=%s } returned false due to not collapsed selection at end "
    259             "of previous transactions",
    260             this, __FUNCTION__, aOtherTransaction,
    261             nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
    262    return NS_OK;
    263  }
    264 
    265  // If the caret positions are now in different root nodes, e.g., the previous
    266  // caret position was removed from the DOM tree, this merge should not be
    267  // done.
    268  const bool isPreviousCaretPointInSameRootOfNewCaretPoint = [&]() {
    269    nsINode* previousRootInCurrentDOMTree = mEndSel.GetCommonRootNode();
    270    return previousRootInCurrentDOMTree &&
    271           previousRootInCurrentDOMTree ==
    272               otherPlaceholderTransaction->mStartSel.GetCommonRootNode();
    273  }();
    274  if (!isPreviousCaretPointInSameRootOfNewCaretPoint) {
    275    MOZ_LOG(GetLogModule(), LogLevel::Debug,
    276            ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
    277             "mName=%s } returned false due to the caret points are in "
    278             "different root nodes",
    279             this, __FUNCTION__, aOtherTransaction,
    280             nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
    281    return NS_OK;
    282  }
    283 
    284  // If the caret points of end of us and start of new transaction are not same,
    285  // we shouldn't merge them.
    286  if (!otherPlaceholderTransaction->mStartSel.Equals(mEndSel)) {
    287    MOZ_LOG(GetLogModule(), LogLevel::Debug,
    288            ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
    289             "mName=%s } returned false due to caret positions were different",
    290             this, __FUNCTION__, aOtherTransaction,
    291             nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
    292    return NS_OK;
    293  }
    294 
    295  mAbsorb = true;  // we need to start absorbing again
    296  otherPlaceholderTransaction->ForwardEndBatchTo(*this);
    297  // AppendChild(editTransactionBase);
    298  // see bug 171243: we don't need to merge placeholders
    299  // into placeholders.  We just reactivate merging in the
    300  // pre-existing placeholder and drop the new one on the floor.  The
    301  // EndPlaceHolderBatch() call on the new placeholder will be
    302  // forwarded to this older one.
    303  DebugOnly<nsresult> rvIgnored = RememberEndingSelection();
    304  NS_WARNING_ASSERTION(
    305      NS_SUCCEEDED(rvIgnored),
    306      "PlaceholderTransaction::RememberEndingSelection() failed, but "
    307      "ignored");
    308  *aDidMerge = true;
    309  MOZ_LOG(GetLogModule(), LogLevel::Debug,
    310          ("%p PlaceholderTransaction::%s(aOtherTransaction=%p) this={ "
    311           "mName=%s } returned true",
    312           this, __FUNCTION__, aOtherTransaction,
    313           nsAtomCString(mName ? mName.get() : nsGkAtoms::_empty).get()));
    314  return NS_OK;
    315 }
    316 
    317 nsresult PlaceholderTransaction::EndPlaceHolderBatch() {
    318  mAbsorb = false;
    319 
    320  if (mForwardingTransaction) {
    321    if (mForwardingTransaction) {
    322      DebugOnly<nsresult> rvIgnored =
    323          mForwardingTransaction->EndPlaceHolderBatch();
    324      NS_WARNING_ASSERTION(
    325          NS_SUCCEEDED(rvIgnored),
    326          "PlaceholderTransaction::EndPlaceHolderBatch() failed, but ignored");
    327    }
    328  }
    329  // remember our selection state.
    330  nsresult rv = RememberEndingSelection();
    331  NS_WARNING_ASSERTION(
    332      NS_SUCCEEDED(rv),
    333      "PlaceholderTransaction::RememberEndingSelection() failed");
    334  return rv;
    335 }
    336 
    337 nsresult PlaceholderTransaction::RememberEndingSelection() {
    338  if (NS_WARN_IF(!mEditorBase)) {
    339    return NS_ERROR_NOT_INITIALIZED;
    340  }
    341 
    342  RefPtr<Selection> selection = mEditorBase->GetSelection();
    343  if (NS_WARN_IF(!selection)) {
    344    return NS_ERROR_FAILURE;
    345  }
    346  mEndSel.SaveSelection(*selection);
    347  return NS_OK;
    348 }
    349 
    350 }  // namespace mozilla