LCOV - code coverage report
Current view: top level - editor/libeditor - CompositionTransaction.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 155 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 15 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "CompositionTransaction.h"
       7             : 
       8             : #include "mozilla/EditorBase.h"         // mEditorBase
       9             : #include "mozilla/SelectionState.h"     // RangeUpdater
      10             : #include "mozilla/dom/Selection.h"      // local var
      11             : #include "mozilla/dom/Text.h"           // mTextNode
      12             : #include "nsAString.h"                  // params
      13             : #include "nsDebug.h"                    // for NS_ASSERTION, etc
      14             : #include "nsError.h"                    // for NS_SUCCEEDED, NS_FAILED, etc
      15             : #include "nsIPresShell.h"               // nsISelectionController constants
      16             : #include "nsRange.h"                    // local var
      17             : #include "nsQueryObject.h"              // for do_QueryObject
      18             : 
      19             : namespace mozilla {
      20             : 
      21             : using namespace dom;
      22             : 
      23           0 : CompositionTransaction::CompositionTransaction(
      24             :                           Text& aTextNode,
      25             :                           uint32_t aOffset,
      26             :                           uint32_t aReplaceLength,
      27             :                           TextRangeArray* aTextRangeArray,
      28             :                           const nsAString& aStringToInsert,
      29             :                           EditorBase& aEditorBase,
      30           0 :                           RangeUpdater* aRangeUpdater)
      31             :   : mTextNode(&aTextNode)
      32             :   , mOffset(aOffset)
      33             :   , mReplaceLength(aReplaceLength)
      34             :   , mRanges(aTextRangeArray)
      35             :   , mStringToInsert(aStringToInsert)
      36             :   , mEditorBase(&aEditorBase)
      37             :   , mRangeUpdater(aRangeUpdater)
      38           0 :   , mFixed(false)
      39             : {
      40           0 :   MOZ_ASSERT(mTextNode->TextLength() >= mOffset);
      41           0 : }
      42             : 
      43           0 : CompositionTransaction::~CompositionTransaction()
      44             : {
      45           0 : }
      46             : 
      47           0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(CompositionTransaction, EditTransactionBase,
      48             :                                    mEditorBase,
      49             :                                    mTextNode)
      50             : // mRangeList can't lead to cycles
      51             : 
      52           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CompositionTransaction)
      53           0 :   if (aIID.Equals(NS_GET_IID(CompositionTransaction))) {
      54           0 :     foundInterface = static_cast<nsITransaction*>(this);
      55             :   } else
      56           0 : NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
      57             : 
      58           0 : NS_IMPL_ADDREF_INHERITED(CompositionTransaction, EditTransactionBase)
      59           0 : NS_IMPL_RELEASE_INHERITED(CompositionTransaction, EditTransactionBase)
      60             : 
      61             : NS_IMETHODIMP
      62           0 : CompositionTransaction::DoTransaction()
      63             : {
      64           0 :   if (NS_WARN_IF(!mEditorBase)) {
      65           0 :     return NS_ERROR_NOT_INITIALIZED;
      66             :   }
      67             : 
      68             :   // Fail before making any changes if there's no selection controller
      69           0 :   nsCOMPtr<nsISelectionController> selCon;
      70           0 :   mEditorBase->GetSelectionController(getter_AddRefs(selCon));
      71           0 :   NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
      72             : 
      73             :   // Advance caret: This requires the presentation shell to get the selection.
      74           0 :   if (mReplaceLength == 0) {
      75           0 :     nsresult rv = mTextNode->InsertData(mOffset, mStringToInsert);
      76           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
      77           0 :       return rv;
      78             :     }
      79           0 :     mRangeUpdater->SelAdjInsertText(*mTextNode, mOffset, mStringToInsert);
      80             :   } else {
      81           0 :     uint32_t replaceableLength = mTextNode->TextLength() - mOffset;
      82             :     nsresult rv =
      83           0 :       mTextNode->ReplaceData(mOffset, mReplaceLength, mStringToInsert);
      84           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
      85           0 :       return rv;
      86             :     }
      87           0 :     mRangeUpdater->SelAdjDeleteText(mTextNode, mOffset, mReplaceLength);
      88           0 :     mRangeUpdater->SelAdjInsertText(*mTextNode, mOffset, mStringToInsert);
      89             : 
      90             :     // If IME text node is multiple node, ReplaceData doesn't remove all IME
      91             :     // text.  So we need remove remained text into other text node.
      92           0 :     if (replaceableLength < mReplaceLength) {
      93           0 :       int32_t remainLength = mReplaceLength - replaceableLength;
      94           0 :       nsCOMPtr<nsINode> node = mTextNode->GetNextSibling();
      95           0 :       while (node && node->IsNodeOfType(nsINode::eTEXT) &&
      96             :              remainLength > 0) {
      97           0 :         Text* text = static_cast<Text*>(node.get());
      98           0 :         uint32_t textLength = text->TextLength();
      99           0 :         text->DeleteData(0, remainLength);
     100           0 :         mRangeUpdater->SelAdjDeleteText(text, 0, remainLength);
     101           0 :         remainLength -= textLength;
     102           0 :         node = node->GetNextSibling();
     103             :       }
     104             :     }
     105             :   }
     106             : 
     107           0 :   nsresult rv = SetSelectionForRanges();
     108           0 :   NS_ENSURE_SUCCESS(rv, rv);
     109             : 
     110           0 :   return NS_OK;
     111             : }
     112             : 
     113             : NS_IMETHODIMP
     114           0 : CompositionTransaction::UndoTransaction()
     115             : {
     116           0 :   if (NS_WARN_IF(!mEditorBase)) {
     117           0 :     return NS_ERROR_NOT_INITIALIZED;
     118             :   }
     119             : 
     120             :   // Get the selection first so we'll fail before making any changes if we
     121             :   // can't get it
     122           0 :   RefPtr<Selection> selection = mEditorBase->GetSelection();
     123           0 :   NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
     124             : 
     125           0 :   nsresult rv = mTextNode->DeleteData(mOffset, mStringToInsert.Length());
     126           0 :   NS_ENSURE_SUCCESS(rv, rv);
     127             : 
     128             :   // set the selection to the insertion point where the string was removed
     129           0 :   rv = selection->Collapse(mTextNode, mOffset);
     130           0 :   NS_ASSERTION(NS_SUCCEEDED(rv),
     131             :                "Selection could not be collapsed after undo of IME insert.");
     132           0 :   NS_ENSURE_SUCCESS(rv, rv);
     133             : 
     134           0 :   return NS_OK;
     135             : }
     136             : 
     137             : NS_IMETHODIMP
     138           0 : CompositionTransaction::Merge(nsITransaction* aTransaction,
     139             :                               bool* aDidMerge)
     140             : {
     141           0 :   NS_ENSURE_ARG_POINTER(aTransaction && aDidMerge);
     142             : 
     143             :   // Check to make sure we aren't fixed, if we are then nothing gets absorbed
     144           0 :   if (mFixed) {
     145           0 :     *aDidMerge = false;
     146           0 :     return NS_OK;
     147             :   }
     148             : 
     149             :   // If aTransaction is another CompositionTransaction then absorb it
     150             :   RefPtr<CompositionTransaction> otherTransaction =
     151           0 :     do_QueryObject(aTransaction);
     152           0 :   if (otherTransaction) {
     153             :     // We absorb the next IME transaction by adopting its insert string
     154           0 :     mStringToInsert = otherTransaction->mStringToInsert;
     155           0 :     mRanges = otherTransaction->mRanges;
     156           0 :     *aDidMerge = true;
     157           0 :     return NS_OK;
     158             :   }
     159             : 
     160           0 :   *aDidMerge = false;
     161           0 :   return NS_OK;
     162             : }
     163             : 
     164             : void
     165           0 : CompositionTransaction::MarkFixed()
     166             : {
     167           0 :   mFixed = true;
     168           0 : }
     169             : 
     170             : NS_IMETHODIMP
     171           0 : CompositionTransaction::GetTxnDescription(nsAString& aString)
     172             : {
     173           0 :   aString.AssignLiteral("CompositionTransaction: ");
     174           0 :   aString += mStringToInsert;
     175           0 :   return NS_OK;
     176             : }
     177             : 
     178             : /* ============ private methods ================== */
     179             : 
     180             : nsresult
     181           0 : CompositionTransaction::SetSelectionForRanges()
     182             : {
     183           0 :   if (NS_WARN_IF(!mEditorBase)) {
     184           0 :     return NS_ERROR_NOT_INITIALIZED;
     185             :   }
     186           0 :   return SetIMESelection(*mEditorBase, mTextNode, mOffset,
     187           0 :                          mStringToInsert.Length(), mRanges);
     188             : }
     189             : 
     190             : // static
     191             : nsresult
     192           0 : CompositionTransaction::SetIMESelection(EditorBase& aEditorBase,
     193             :                                         Text* aTextNode,
     194             :                                         uint32_t aOffsetInNode,
     195             :                                         uint32_t aLengthOfCompositionString,
     196             :                                         const TextRangeArray* aRanges)
     197             : {
     198           0 :   RefPtr<Selection> selection = aEditorBase.GetSelection();
     199           0 :   NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
     200             : 
     201           0 :   nsresult rv = selection->StartBatchChanges();
     202           0 :   NS_ENSURE_SUCCESS(rv, rv);
     203             : 
     204             :   // First, remove all selections of IME composition.
     205             :   static const RawSelectionType kIMESelections[] = {
     206             :     nsISelectionController::SELECTION_IME_RAWINPUT,
     207             :     nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT,
     208             :     nsISelectionController::SELECTION_IME_CONVERTEDTEXT,
     209             :     nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT
     210             :   };
     211             : 
     212           0 :   nsCOMPtr<nsISelectionController> selCon;
     213           0 :   aEditorBase.GetSelectionController(getter_AddRefs(selCon));
     214           0 :   NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
     215             : 
     216           0 :   for (uint32_t i = 0; i < ArrayLength(kIMESelections); ++i) {
     217           0 :     nsCOMPtr<nsISelection> selectionOfIME;
     218           0 :     if (NS_FAILED(selCon->GetSelection(kIMESelections[i],
     219             :                                        getter_AddRefs(selectionOfIME)))) {
     220           0 :       continue;
     221             :     }
     222           0 :     rv = selectionOfIME->RemoveAllRanges();
     223           0 :     NS_ASSERTION(NS_SUCCEEDED(rv),
     224             :                  "Failed to remove all ranges of IME selection");
     225             :   }
     226             : 
     227             :   // Set caret position and selection of IME composition with TextRangeArray.
     228           0 :   bool setCaret = false;
     229           0 :   uint32_t countOfRanges = aRanges ? aRanges->Length() : 0;
     230             : 
     231             : #ifdef DEBUG
     232             :   // Bounds-checking on debug builds
     233           0 :   uint32_t maxOffset = aTextNode->Length();
     234             : #endif
     235             : 
     236             :   // NOTE: composition string may be truncated when it's committed and
     237             :   //       maxlength attribute value doesn't allow input of all text of this
     238             :   //       composition.
     239           0 :   for (uint32_t i = 0; i < countOfRanges; ++i) {
     240           0 :     const TextRange& textRange = aRanges->ElementAt(i);
     241             : 
     242             :     // Caret needs special handling since its length may be 0 and if it's not
     243             :     // specified explicitly, we need to handle it ourselves later.
     244           0 :     if (textRange.mRangeType == TextRangeType::eCaret) {
     245           0 :       NS_ASSERTION(!setCaret, "The ranges already has caret position");
     246           0 :       NS_ASSERTION(!textRange.Length(),
     247             :                    "EditorBase doesn't support wide caret");
     248             :       int32_t caretOffset = static_cast<int32_t>(
     249           0 :         aOffsetInNode +
     250           0 :           std::min(textRange.mStartOffset, aLengthOfCompositionString));
     251           0 :       MOZ_ASSERT(caretOffset >= 0 &&
     252             :                  static_cast<uint32_t>(caretOffset) <= maxOffset);
     253           0 :       rv = selection->Collapse(aTextNode, caretOffset);
     254           0 :       setCaret = setCaret || NS_SUCCEEDED(rv);
     255           0 :       if (NS_WARN_IF(!setCaret)) {
     256           0 :         continue;
     257             :       }
     258             :       // If caret range is specified explicitly, we should show the caret if
     259             :       // it should be so.
     260           0 :       aEditorBase.HideCaret(false);
     261           0 :       continue;
     262             :     }
     263             : 
     264             :     // If the clause length is 0, it should be a bug.
     265           0 :     if (!textRange.Length()) {
     266           0 :       NS_WARNING("Any clauses must not be empty");
     267           0 :       continue;
     268             :     }
     269             : 
     270           0 :     RefPtr<nsRange> clauseRange;
     271             :     int32_t startOffset = static_cast<int32_t>(
     272           0 :       aOffsetInNode +
     273           0 :         std::min(textRange.mStartOffset, aLengthOfCompositionString));
     274           0 :     MOZ_ASSERT(startOffset >= 0 &&
     275             :                static_cast<uint32_t>(startOffset) <= maxOffset);
     276             :     int32_t endOffset = static_cast<int32_t>(
     277           0 :       aOffsetInNode +
     278           0 :         std::min(textRange.mEndOffset, aLengthOfCompositionString));
     279           0 :     MOZ_ASSERT(endOffset >= startOffset &&
     280             :                static_cast<uint32_t>(endOffset) <= maxOffset);
     281           0 :     rv = nsRange::CreateRange(aTextNode, startOffset,
     282             :                               aTextNode, endOffset,
     283           0 :                               getter_AddRefs(clauseRange));
     284           0 :     if (NS_FAILED(rv)) {
     285           0 :       NS_WARNING("Failed to create a DOM range for a clause of composition");
     286           0 :       break;
     287             :     }
     288             : 
     289             :     // Set the range of the clause to selection.
     290           0 :     nsCOMPtr<nsISelection> selectionOfIME;
     291           0 :     rv = selCon->GetSelection(ToRawSelectionType(textRange.mRangeType),
     292           0 :                               getter_AddRefs(selectionOfIME));
     293           0 :     if (NS_FAILED(rv)) {
     294           0 :       NS_WARNING("Failed to get IME selection");
     295           0 :       break;
     296             :     }
     297             : 
     298           0 :     rv = selectionOfIME->AddRange(clauseRange);
     299           0 :     if (NS_FAILED(rv)) {
     300           0 :       NS_WARNING("Failed to add selection range for a clause of composition");
     301           0 :       break;
     302             :     }
     303             : 
     304             :     // Set the style of the clause.
     305             :     nsCOMPtr<nsISelectionPrivate> selectionOfIMEPriv =
     306           0 :                                     do_QueryInterface(selectionOfIME);
     307           0 :     if (!selectionOfIMEPriv) {
     308           0 :       NS_WARNING("Failed to get nsISelectionPrivate interface from selection");
     309           0 :       continue; // Since this is additional feature, we can continue this job.
     310             :     }
     311           0 :     rv = selectionOfIMEPriv->SetTextRangeStyle(clauseRange,
     312           0 :                                                textRange.mRangeStyle);
     313           0 :     if (NS_FAILED(rv)) {
     314           0 :       NS_WARNING("Failed to set selection style");
     315           0 :       break; // but this is unexpected...
     316             :     }
     317             :   }
     318             : 
     319             :   // If the ranges doesn't include explicit caret position, let's set the
     320             :   // caret to the end of composition string.
     321           0 :   if (!setCaret) {
     322             :     int32_t caretOffset =
     323           0 :       static_cast<int32_t>(aOffsetInNode + aLengthOfCompositionString);
     324           0 :     MOZ_ASSERT(caretOffset >= 0 &&
     325             :                static_cast<uint32_t>(caretOffset) <= maxOffset);
     326           0 :     rv = selection->Collapse(aTextNode, caretOffset);
     327           0 :     NS_ASSERTION(NS_SUCCEEDED(rv),
     328             :                  "Failed to set caret at the end of composition string");
     329             : 
     330             :     // If caret range isn't specified explicitly, we should hide the caret.
     331             :     // Hiding the caret benefits a Windows build (see bug 555642 comment #6).
     332             :     // However, when there is no range, we should keep showing caret.
     333           0 :     if (countOfRanges) {
     334           0 :       aEditorBase.HideCaret(true);
     335             :     }
     336             :   }
     337             : 
     338           0 :   rv = selection->EndBatchChangesInternal();
     339           0 :   NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to end batch changes");
     340             : 
     341           0 :   return rv;
     342             : }
     343             : 
     344             : } // namespace mozilla

Generated by: LCOV version 1.13