LCOV - code coverage report
Current view: top level - widget - ContentCache.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 32 610 5.2 %
Date: 2017-07-14 16:53:18 Functions: 5 38 13.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2             :  * vim: sw=2 ts=8 et :
       3             :  */
       4             : /* This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       7             : 
       8             : #include "mozilla/ContentCache.h"
       9             : 
      10             : #include "mozilla/IMEStateManager.h"
      11             : #include "mozilla/IntegerPrintfMacros.h"
      12             : #include "mozilla/Logging.h"
      13             : #include "mozilla/Move.h"
      14             : #include "mozilla/RefPtr.h"
      15             : #include "mozilla/SizePrintfMacros.h"
      16             : #include "mozilla/TextComposition.h"
      17             : #include "mozilla/TextEvents.h"
      18             : #include "mozilla/dom/TabParent.h"
      19             : #include "nsIWidget.h"
      20             : 
      21             : namespace mozilla {
      22             : 
      23             : using namespace dom;
      24             : using namespace widget;
      25             : 
      26             : static const char*
      27           0 : GetBoolName(bool aBool)
      28             : {
      29           0 :   return aBool ? "true" : "false";
      30             : }
      31             : 
      32             : static const char*
      33           0 : GetNotificationName(const IMENotification* aNotification)
      34             : {
      35           0 :   if (!aNotification) {
      36           0 :     return "Not notification";
      37             :   }
      38           0 :   return ToChar(aNotification->mMessage);
      39             : }
      40             : 
      41             : class GetRectText : public nsAutoCString
      42             : {
      43             : public:
      44           0 :   explicit GetRectText(const LayoutDeviceIntRect& aRect)
      45           0 :   {
      46           0 :     Assign("{ x=");
      47           0 :     AppendInt(aRect.x);
      48           0 :     Append(", y=");
      49           0 :     AppendInt(aRect.y);
      50           0 :     Append(", width=");
      51           0 :     AppendInt(aRect.width);
      52           0 :     Append(", height=");
      53           0 :     AppendInt(aRect.height);
      54           0 :     Append(" }");
      55           0 :   }
      56           0 :   virtual ~GetRectText() {}
      57             : };
      58             : 
      59             : class GetWritingModeName : public nsAutoCString
      60             : {
      61             : public:
      62           0 :   explicit GetWritingModeName(const WritingMode& aWritingMode)
      63           0 :   {
      64           0 :     if (!aWritingMode.IsVertical()) {
      65           0 :       Assign("Horizontal");
      66           0 :       return;
      67             :     }
      68           0 :     if (aWritingMode.IsVerticalLR()) {
      69           0 :       Assign("Vertical (LTR)");
      70           0 :       return;
      71             :     }
      72           0 :     Assign("Vertical (RTL)");
      73             :   }
      74           0 :   virtual ~GetWritingModeName() {}
      75             : };
      76             : 
      77           0 : class GetEscapedUTF8String final : public NS_ConvertUTF16toUTF8
      78             : {
      79             : public:
      80           0 :   explicit GetEscapedUTF8String(const nsAString& aString)
      81           0 :     : NS_ConvertUTF16toUTF8(aString)
      82             :   {
      83           0 :     Escape();
      84           0 :   }
      85             :   explicit GetEscapedUTF8String(const char16ptr_t aString)
      86             :     : NS_ConvertUTF16toUTF8(aString)
      87             :   {
      88             :     Escape();
      89             :   }
      90             :   GetEscapedUTF8String(const char16ptr_t aString, uint32_t aLength)
      91             :     : NS_ConvertUTF16toUTF8(aString, aLength)
      92             :   {
      93             :     Escape();
      94             :   }
      95             : 
      96             : private:
      97           0 :   void Escape()
      98             :   {
      99           0 :     ReplaceSubstring("\r", "\\r");
     100           0 :     ReplaceSubstring("\n", "\\n");
     101           0 :     ReplaceSubstring("\t", "\\t");
     102           0 :   }
     103             : };
     104             : 
     105             : /*****************************************************************************
     106             :  * mozilla::ContentCache
     107             :  *****************************************************************************/
     108             : 
     109             : LazyLogModule sContentCacheLog("ContentCacheWidgets");
     110             : 
     111           3 : ContentCache::ContentCache()
     112           3 :   : mCompositionStart(UINT32_MAX)
     113             : {
     114           3 : }
     115             : 
     116             : /*****************************************************************************
     117             :  * mozilla::ContentCacheInChild
     118             :  *****************************************************************************/
     119             : 
     120           1 : ContentCacheInChild::ContentCacheInChild()
     121           1 :   : ContentCache()
     122             : {
     123           1 : }
     124             : 
     125             : void
     126           1 : ContentCacheInChild::Clear()
     127             : {
     128           1 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     129             :     ("0x%p Clear()", this));
     130             : 
     131           1 :   mCompositionStart = UINT32_MAX;
     132           1 :   mText.Truncate();
     133           1 :   mSelection.Clear();
     134           1 :   mFirstCharRect.SetEmpty();
     135           1 :   mCaret.Clear();
     136           1 :   mTextRectArray.Clear();
     137           1 :   mEditorRect.SetEmpty();
     138           1 : }
     139             : 
     140             : bool
     141           0 : ContentCacheInChild::CacheAll(nsIWidget* aWidget,
     142             :                               const IMENotification* aNotification)
     143             : {
     144           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     145             :     ("0x%p CacheAll(aWidget=0x%p, aNotification=%s)",
     146             :      this, aWidget, GetNotificationName(aNotification)));
     147             : 
     148           0 :   if (NS_WARN_IF(!CacheText(aWidget, aNotification)) ||
     149           0 :       NS_WARN_IF(!CacheEditorRect(aWidget, aNotification))) {
     150           0 :     return false;
     151             :   }
     152           0 :   return true;
     153             : }
     154             : 
     155             : bool
     156           0 : ContentCacheInChild::CacheSelection(nsIWidget* aWidget,
     157             :                                     const IMENotification* aNotification)
     158             : {
     159           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     160             :     ("0x%p CacheSelection(aWidget=0x%p, aNotification=%s)",
     161             :      this, aWidget, GetNotificationName(aNotification)));
     162             : 
     163           0 :   mCaret.Clear();
     164           0 :   mSelection.Clear();
     165             : 
     166           0 :   nsEventStatus status = nsEventStatus_eIgnore;
     167           0 :   WidgetQueryContentEvent selection(true, eQuerySelectedText, aWidget);
     168           0 :   aWidget->DispatchEvent(&selection, status);
     169           0 :   if (NS_WARN_IF(!selection.mSucceeded)) {
     170           0 :     MOZ_LOG(sContentCacheLog, LogLevel::Error,
     171             :       ("0x%p CacheSelection(), FAILED, "
     172             :        "couldn't retrieve the selected text", this));
     173           0 :     return false;
     174             :   }
     175           0 :   if (selection.mReply.mReversed) {
     176           0 :     mSelection.mAnchor =
     177           0 :       selection.mReply.mOffset + selection.mReply.mString.Length();
     178           0 :     mSelection.mFocus = selection.mReply.mOffset;
     179             :   } else {
     180           0 :     mSelection.mAnchor = selection.mReply.mOffset;
     181           0 :     mSelection.mFocus =
     182           0 :       selection.mReply.mOffset + selection.mReply.mString.Length();
     183             :   }
     184           0 :   mSelection.mWritingMode = selection.GetWritingMode();
     185             : 
     186           0 :   return CacheCaret(aWidget, aNotification) &&
     187           0 :          CacheTextRects(aWidget, aNotification);
     188             : }
     189             : 
     190             : bool
     191           0 : ContentCacheInChild::CacheCaret(nsIWidget* aWidget,
     192             :                                 const IMENotification* aNotification)
     193             : {
     194           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     195             :     ("0x%p CacheCaret(aWidget=0x%p, aNotification=%s)",
     196             :      this, aWidget, GetNotificationName(aNotification)));
     197             : 
     198           0 :   mCaret.Clear();
     199             : 
     200           0 :   if (NS_WARN_IF(!mSelection.IsValid())) {
     201           0 :     return false;
     202             :   }
     203             : 
     204             :   // XXX Should be mSelection.mFocus?
     205           0 :   mCaret.mOffset = mSelection.StartOffset();
     206             : 
     207           0 :   nsEventStatus status = nsEventStatus_eIgnore;
     208           0 :   WidgetQueryContentEvent caretRect(true, eQueryCaretRect, aWidget);
     209           0 :   caretRect.InitForQueryCaretRect(mCaret.mOffset);
     210           0 :   aWidget->DispatchEvent(&caretRect, status);
     211           0 :   if (NS_WARN_IF(!caretRect.mSucceeded)) {
     212           0 :     MOZ_LOG(sContentCacheLog, LogLevel::Error,
     213             :       ("0x%p CacheCaret(), FAILED, "
     214             :        "couldn't retrieve the caret rect at offset=%u",
     215             :        this, mCaret.mOffset));
     216           0 :     mCaret.Clear();
     217           0 :     return false;
     218             :   }
     219           0 :   mCaret.mRect = caretRect.mReply.mRect;
     220           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     221             :     ("0x%p CacheCaret(), Succeeded, "
     222             :      "mSelection={ mAnchor=%u, mFocus=%u, mWritingMode=%s }, "
     223             :      "mCaret={ mOffset=%u, mRect=%s }",
     224             :      this, mSelection.mAnchor, mSelection.mFocus,
     225             :      GetWritingModeName(mSelection.mWritingMode).get(), mCaret.mOffset,
     226             :      GetRectText(mCaret.mRect).get()));
     227           0 :   return true;
     228             : }
     229             : 
     230             : bool
     231           0 : ContentCacheInChild::CacheEditorRect(nsIWidget* aWidget,
     232             :                                      const IMENotification* aNotification)
     233             : {
     234           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     235             :     ("0x%p CacheEditorRect(aWidget=0x%p, aNotification=%s)",
     236             :      this, aWidget, GetNotificationName(aNotification)));
     237             : 
     238           0 :   nsEventStatus status = nsEventStatus_eIgnore;
     239           0 :   WidgetQueryContentEvent editorRectEvent(true, eQueryEditorRect, aWidget);
     240           0 :   aWidget->DispatchEvent(&editorRectEvent, status);
     241           0 :   if (NS_WARN_IF(!editorRectEvent.mSucceeded)) {
     242           0 :     MOZ_LOG(sContentCacheLog, LogLevel::Error,
     243             :       ("0x%p CacheEditorRect(), FAILED, "
     244             :        "couldn't retrieve the editor rect", this));
     245           0 :     return false;
     246             :   }
     247           0 :   mEditorRect = editorRectEvent.mReply.mRect;
     248           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     249             :     ("0x%p CacheEditorRect(), Succeeded, "
     250             :      "mEditorRect=%s", this, GetRectText(mEditorRect).get()));
     251           0 :   return true;
     252             : }
     253             : 
     254             : bool
     255           0 : ContentCacheInChild::CacheText(nsIWidget* aWidget,
     256             :                                const IMENotification* aNotification)
     257             : {
     258           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     259             :     ("0x%p CacheText(aWidget=0x%p, aNotification=%s)",
     260             :      this, aWidget, GetNotificationName(aNotification)));
     261             : 
     262           0 :   nsEventStatus status = nsEventStatus_eIgnore;
     263           0 :   WidgetQueryContentEvent queryText(true, eQueryTextContent, aWidget);
     264           0 :   queryText.InitForQueryTextContent(0, UINT32_MAX);
     265           0 :   aWidget->DispatchEvent(&queryText, status);
     266           0 :   if (NS_WARN_IF(!queryText.mSucceeded)) {
     267           0 :     MOZ_LOG(sContentCacheLog, LogLevel::Error,
     268             :       ("0x%p CacheText(), FAILED, couldn't retrieve whole text", this));
     269           0 :     mText.Truncate();
     270           0 :     return false;
     271             :   }
     272           0 :   mText = queryText.mReply.mString;
     273           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     274             :     ("0x%p CacheText(), Succeeded, mText.Length()=%u", this, mText.Length()));
     275             : 
     276           0 :   return CacheSelection(aWidget, aNotification);
     277             : }
     278             : 
     279             : bool
     280           0 : ContentCacheInChild::QueryCharRect(nsIWidget* aWidget,
     281             :                                    uint32_t aOffset,
     282             :                                    LayoutDeviceIntRect& aCharRect) const
     283             : {
     284           0 :   aCharRect.SetEmpty();
     285             : 
     286           0 :   nsEventStatus status = nsEventStatus_eIgnore;
     287           0 :   WidgetQueryContentEvent textRect(true, eQueryTextRect, aWidget);
     288           0 :   textRect.InitForQueryTextRect(aOffset, 1);
     289           0 :   aWidget->DispatchEvent(&textRect, status);
     290           0 :   if (NS_WARN_IF(!textRect.mSucceeded)) {
     291           0 :     return false;
     292             :   }
     293           0 :   aCharRect = textRect.mReply.mRect;
     294             : 
     295             :   // Guarantee the rect is not empty.
     296           0 :   if (NS_WARN_IF(!aCharRect.height)) {
     297           0 :     aCharRect.height = 1;
     298             :   }
     299           0 :   if (NS_WARN_IF(!aCharRect.width)) {
     300           0 :     aCharRect.width = 1;
     301             :   }
     302           0 :   return true;
     303             : }
     304             : 
     305             : bool
     306           0 : ContentCacheInChild::QueryCharRectArray(nsIWidget* aWidget,
     307             :                                         uint32_t aOffset,
     308             :                                         uint32_t aLength,
     309             :                                         RectArray& aCharRectArray) const
     310             : {
     311           0 :   nsEventStatus status = nsEventStatus_eIgnore;
     312           0 :   WidgetQueryContentEvent textRects(true, eQueryTextRectArray, aWidget);
     313           0 :   textRects.InitForQueryTextRectArray(aOffset, aLength);
     314           0 :   aWidget->DispatchEvent(&textRects, status);
     315           0 :   if (NS_WARN_IF(!textRects.mSucceeded)) {
     316           0 :     aCharRectArray.Clear();
     317           0 :     return false;
     318             :   }
     319           0 :   aCharRectArray = Move(textRects.mReply.mRectArray);
     320           0 :   return true;
     321             : }
     322             : 
     323             : bool
     324           0 : ContentCacheInChild::CacheTextRects(nsIWidget* aWidget,
     325             :                                     const IMENotification* aNotification)
     326             : {
     327           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     328             :     ("0x%p CacheTextRects(aWidget=0x%p, aNotification=%s), "
     329             :      "mCaret={ mOffset=%u, IsValid()=%s }",
     330             :      this, aWidget, GetNotificationName(aNotification), mCaret.mOffset,
     331             :      GetBoolName(mCaret.IsValid())));
     332             : 
     333           0 :   mCompositionStart = UINT32_MAX;
     334           0 :   mTextRectArray.Clear();
     335           0 :   mSelection.ClearAnchorCharRects();
     336           0 :   mSelection.ClearFocusCharRects();
     337           0 :   mSelection.mRect.SetEmpty();
     338           0 :   mFirstCharRect.SetEmpty();
     339             : 
     340           0 :   if (NS_WARN_IF(!mSelection.IsValid())) {
     341           0 :     return false;
     342             :   }
     343             : 
     344             :   // Retrieve text rects in composition string if there is.
     345             :   RefPtr<TextComposition> textComposition =
     346           0 :     IMEStateManager::GetTextCompositionFor(aWidget);
     347           0 :   if (textComposition) {
     348             :     // mCompositionStart may be updated by some composition event handlers.
     349             :     // So, let's update it with the latest information.
     350           0 :     mCompositionStart = textComposition->NativeOffsetOfStartComposition();
     351             :     // Note that TextComposition::String() may not be modified here because
     352             :     // it's modified after all edit action listeners are performed but this
     353             :     // is called while some of them are performed.
     354             :     // FYI: For supporting IME which commits composition and restart new
     355             :     //      composition immediately, we should cache next character of current
     356             :     //      composition too.
     357           0 :     uint32_t length = textComposition->LastData().Length() + 1;
     358           0 :     mTextRectArray.mStart = mCompositionStart;
     359           0 :     if (NS_WARN_IF(!QueryCharRectArray(aWidget, mTextRectArray.mStart, length,
     360             :                                        mTextRectArray.mRects))) {
     361           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Error,
     362             :         ("0x%p CacheTextRects(), FAILED, "
     363             :          "couldn't retrieve text rect array of the composition string", this));
     364             :     }
     365             :   }
     366             : 
     367           0 :   if (mTextRectArray.InRange(mSelection.mAnchor) &&
     368           0 :       (!mSelection.mAnchor || mTextRectArray.InRange(mSelection.mAnchor - 1))) {
     369             :     mSelection.mAnchorCharRects[eNextCharRect] =
     370           0 :       mTextRectArray.GetRect(mSelection.mAnchor);
     371           0 :     if (mSelection.mAnchor) {
     372             :       mSelection.mAnchorCharRects[ePrevCharRect] =
     373           0 :         mTextRectArray.GetRect(mSelection.mAnchor - 1);
     374             :     }
     375             :   } else {
     376           0 :     RectArray rects;
     377           0 :     uint32_t startOffset = mSelection.mAnchor ? mSelection.mAnchor - 1 : 0;
     378           0 :     uint32_t length = mSelection.mAnchor ? 2 : 1;
     379           0 :     if (NS_WARN_IF(!QueryCharRectArray(aWidget, startOffset, length, rects))) {
     380           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Error,
     381             :         ("0x%p CacheTextRects(), FAILED, "
     382             :          "couldn't retrieve text rect array around the selection anchor (%u)",
     383             :          this, mSelection.mAnchor));
     384           0 :       MOZ_ASSERT(mSelection.mAnchorCharRects[ePrevCharRect].IsEmpty());
     385           0 :       MOZ_ASSERT(mSelection.mAnchorCharRects[eNextCharRect].IsEmpty());
     386             :     } else {
     387           0 :       if (rects.Length() > 1) {
     388           0 :         mSelection.mAnchorCharRects[ePrevCharRect] = rects[0];
     389           0 :         mSelection.mAnchorCharRects[eNextCharRect] = rects[1];
     390           0 :       } else if (rects.Length()) {
     391           0 :         mSelection.mAnchorCharRects[eNextCharRect] = rects[0];
     392           0 :         MOZ_ASSERT(mSelection.mAnchorCharRects[ePrevCharRect].IsEmpty());
     393             :       }
     394             :     }
     395             :   }
     396             : 
     397           0 :   if (mSelection.Collapsed()) {
     398           0 :     mSelection.mFocusCharRects[0] = mSelection.mAnchorCharRects[0];
     399           0 :     mSelection.mFocusCharRects[1] = mSelection.mAnchorCharRects[1];
     400           0 :   } else if (mTextRectArray.InRange(mSelection.mFocus) &&
     401           0 :              (!mSelection.mFocus ||
     402           0 :               mTextRectArray.InRange(mSelection.mFocus - 1))) {
     403             :     mSelection.mFocusCharRects[eNextCharRect] =
     404           0 :       mTextRectArray.GetRect(mSelection.mFocus);
     405           0 :     if (mSelection.mFocus) {
     406             :       mSelection.mFocusCharRects[ePrevCharRect] =
     407           0 :         mTextRectArray.GetRect(mSelection.mFocus - 1);
     408             :     }
     409             :   } else {
     410           0 :     RectArray rects;
     411           0 :     uint32_t startOffset = mSelection.mFocus ? mSelection.mFocus - 1 : 0;
     412           0 :     uint32_t length = mSelection.mFocus ? 2 : 1;
     413           0 :     if (NS_WARN_IF(!QueryCharRectArray(aWidget, startOffset, length, rects))) {
     414           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Error,
     415             :         ("0x%p CacheTextRects(), FAILED, "
     416             :          "couldn't retrieve text rect array around the selection focus (%u)",
     417             :          this, mSelection.mFocus));
     418           0 :       MOZ_ASSERT(mSelection.mFocusCharRects[ePrevCharRect].IsEmpty());
     419           0 :       MOZ_ASSERT(mSelection.mFocusCharRects[eNextCharRect].IsEmpty());
     420             :     } else {
     421           0 :       if (rects.Length() > 1) {
     422           0 :         mSelection.mFocusCharRects[ePrevCharRect] = rects[0];
     423           0 :         mSelection.mFocusCharRects[eNextCharRect] = rects[1];
     424           0 :       } else if (rects.Length()) {
     425           0 :         mSelection.mFocusCharRects[eNextCharRect] = rects[0];
     426           0 :         MOZ_ASSERT(mSelection.mFocusCharRects[ePrevCharRect].IsEmpty());
     427             :       }
     428             :     }
     429             :   }
     430             : 
     431           0 :   if (!mSelection.Collapsed()) {
     432           0 :     nsEventStatus status = nsEventStatus_eIgnore;
     433           0 :     WidgetQueryContentEvent textRect(true, eQueryTextRect, aWidget);
     434           0 :     textRect.InitForQueryTextRect(mSelection.StartOffset(),
     435           0 :                                   mSelection.Length());
     436           0 :     aWidget->DispatchEvent(&textRect, status);
     437           0 :     if (NS_WARN_IF(!textRect.mSucceeded)) {
     438           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Error,
     439             :         ("0x%p CacheTextRects(), FAILED, "
     440             :          "couldn't retrieve text rect of whole selected text", this));
     441             :     } else {
     442           0 :       mSelection.mRect = textRect.mReply.mRect;
     443             :     }
     444             :   }
     445             : 
     446           0 :   if (!mSelection.mFocus) {
     447           0 :     mFirstCharRect = mSelection.mFocusCharRects[eNextCharRect];
     448           0 :   } else if (mSelection.mFocus == 1) {
     449           0 :     mFirstCharRect = mSelection.mFocusCharRects[ePrevCharRect];
     450           0 :   } else if (!mSelection.mAnchor) {
     451           0 :     mFirstCharRect = mSelection.mAnchorCharRects[eNextCharRect];
     452           0 :   } else if (mSelection.mAnchor == 1) {
     453           0 :     mFirstCharRect = mSelection.mFocusCharRects[ePrevCharRect];
     454           0 :   } else if (mTextRectArray.InRange(0)) {
     455           0 :     mFirstCharRect = mTextRectArray.GetRect(0);
     456             :   } else {
     457           0 :     LayoutDeviceIntRect charRect;
     458           0 :     if (NS_WARN_IF(!QueryCharRect(aWidget, 0, charRect))) {
     459           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Error,
     460             :         ("0x%p CacheTextRects(), FAILED, "
     461             :          "couldn't retrieve first char rect", this));
     462             :     } else {
     463           0 :       mFirstCharRect = charRect;
     464             :     }
     465             :   }
     466             : 
     467           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     468             :     ("0x%p CacheTextRects(), Succeeded, "
     469             :      "mText.Length()=%x, mTextRectArray={ mStart=%u, mRects.Length()=%"
     470             :      PRIuSIZE " }, mSelection={ mAnchor=%u, mAnchorCharRects[eNextCharRect]=%s, "
     471             :      "mAnchorCharRects[ePrevCharRect]=%s, mFocus=%u, "
     472             :      "mFocusCharRects[eNextCharRect]=%s, mFocusCharRects[ePrevCharRect]=%s, "
     473             :      "mRect=%s }, mFirstCharRect=%s",
     474             :      this, mText.Length(), mTextRectArray.mStart,
     475             :      mTextRectArray.mRects.Length(), mSelection.mAnchor,
     476             :      GetRectText(mSelection.mAnchorCharRects[eNextCharRect]).get(),
     477             :      GetRectText(mSelection.mAnchorCharRects[ePrevCharRect]).get(),
     478             :      mSelection.mFocus,
     479             :      GetRectText(mSelection.mFocusCharRects[eNextCharRect]).get(),
     480             :      GetRectText(mSelection.mFocusCharRects[ePrevCharRect]).get(),
     481             :      GetRectText(mSelection.mRect).get(), GetRectText(mFirstCharRect).get()));
     482           0 :   return true;
     483             : }
     484             : 
     485             : void
     486           0 : ContentCacheInChild::SetSelection(nsIWidget* aWidget,
     487             :                                   uint32_t aStartOffset,
     488             :                                   uint32_t aLength,
     489             :                                   bool aReversed,
     490             :                                   const WritingMode& aWritingMode)
     491             : {
     492           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     493             :     ("0x%p SetSelection(aStartOffset=%u, "
     494             :      "aLength=%u, aReversed=%s, aWritingMode=%s), mText.Length()=%u",
     495             :      this, aStartOffset, aLength, GetBoolName(aReversed),
     496             :      GetWritingModeName(aWritingMode).get(), mText.Length()));
     497             : 
     498           0 :   if (!aReversed) {
     499           0 :     mSelection.mAnchor = aStartOffset;
     500           0 :     mSelection.mFocus = aStartOffset + aLength;
     501             :   } else {
     502           0 :     mSelection.mAnchor = aStartOffset + aLength;
     503           0 :     mSelection.mFocus = aStartOffset;
     504             :   }
     505           0 :   mSelection.mWritingMode = aWritingMode;
     506             : 
     507           0 :   if (NS_WARN_IF(!CacheCaret(aWidget))) {
     508           0 :     return;
     509             :   }
     510           0 :   Unused << NS_WARN_IF(!CacheTextRects(aWidget));
     511             : }
     512             : 
     513             : /*****************************************************************************
     514             :  * mozilla::ContentCacheInParent
     515             :  *****************************************************************************/
     516             : 
     517           1 : ContentCacheInParent::ContentCacheInParent(TabParent& aTabParent)
     518             :   : ContentCache()
     519             :   , mTabParent(aTabParent)
     520             :   , mCommitStringByRequest(nullptr)
     521             :   , mPendingEventsNeedingAck(0)
     522             :   , mCompositionStartInChild(UINT32_MAX)
     523             :   , mPendingCompositionCount(0)
     524             :   , mWidgetHasComposition(false)
     525           1 :   , mIsPendingLastCommitEvent(false)
     526             : {
     527           1 : }
     528             : 
     529             : void
     530           1 : ContentCacheInParent::AssignContent(const ContentCache& aOther,
     531             :                                     nsIWidget* aWidget,
     532             :                                     const IMENotification* aNotification)
     533             : {
     534           1 :   mText = aOther.mText;
     535           1 :   mSelection = aOther.mSelection;
     536           1 :   mFirstCharRect = aOther.mFirstCharRect;
     537           1 :   mCaret = aOther.mCaret;
     538           1 :   mTextRectArray = aOther.mTextRectArray;
     539           1 :   mEditorRect = aOther.mEditorRect;
     540             : 
     541             :   // Only when there is one composition, the TextComposition instance in this
     542             :   // process is managing the composition in the remote process.  Therefore,
     543             :   // we shouldn't update composition start offset of TextComposition with
     544             :   // old composition which is still being handled by the child process.
     545           1 :   if (mWidgetHasComposition && mPendingCompositionCount == 1) {
     546           0 :     IMEStateManager::MaybeStartOffsetUpdatedInChild(aWidget, mCompositionStart);
     547             :   }
     548             : 
     549             :   // When the widget has composition, we should set mCompositionStart to
     550             :   // *current* composition start offset.  Note that, in strictly speaking,
     551             :   // widget should not use WidgetQueryContentEvent if there are some pending
     552             :   // compositions (i.e., when mPendingCompositionCount is 2 or more).
     553           1 :   mCompositionStartInChild = aOther.mCompositionStart;
     554           1 :   if (mWidgetHasComposition) {
     555           0 :     if (aOther.mCompositionStart != UINT32_MAX) {
     556           0 :       if (mCompositionStart != aOther.mCompositionStart) {
     557           0 :         mCompositionStart = aOther.mCompositionStart;
     558           0 :         mPendingCommitLength = 0;
     559             :       }
     560           0 :     } else if (mCompositionStart != mSelection.StartOffset()) {
     561           0 :       mCompositionStart = mSelection.StartOffset();
     562           0 :       mPendingCommitLength = 0;
     563           0 :       NS_WARNING_ASSERTION(mCompositionStart != UINT32_MAX,
     564             :                            "mCompositionStart shouldn't be invalid offset when "
     565             :                            "the widget has composition");
     566             :     }
     567           1 :   } else if (mCompositionStart != UINT32_MAX) {
     568           0 :     mCompositionStart = UINT32_MAX;
     569           0 :     mPendingCommitLength = 0;
     570             :   }
     571             : 
     572           1 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     573             :     ("0x%p AssignContent(aNotification=%s), "
     574             :      "Succeeded, mText.Length()=%u, mSelection={ mAnchor=%u, mFocus=%u, "
     575             :      "mWritingMode=%s, mAnchorCharRects[eNextCharRect]=%s, "
     576             :      "mAnchorCharRects[ePrevCharRect]=%s, mFocusCharRects[eNextCharRect]=%s, "
     577             :      "mFocusCharRects[ePrevCharRect]=%s, mRect=%s }, "
     578             :      "mFirstCharRect=%s, mCaret={ mOffset=%u, mRect=%s }, mTextRectArray={ "
     579             :      "mStart=%u, mRects.Length()=%" PRIuSIZE " }, mWidgetHasComposition=%s, "
     580             :      "mPendingCompositionCount=%u, mCompositionStart=%u, "
     581             :      "mPendingCommitLength=%u, mEditorRect=%s",
     582             :      this, GetNotificationName(aNotification),
     583             :      mText.Length(), mSelection.mAnchor, mSelection.mFocus,
     584             :      GetWritingModeName(mSelection.mWritingMode).get(),
     585             :      GetRectText(mSelection.mAnchorCharRects[eNextCharRect]).get(),
     586             :      GetRectText(mSelection.mAnchorCharRects[ePrevCharRect]).get(),
     587             :      GetRectText(mSelection.mFocusCharRects[eNextCharRect]).get(),
     588             :      GetRectText(mSelection.mFocusCharRects[ePrevCharRect]).get(),
     589             :      GetRectText(mSelection.mRect).get(), GetRectText(mFirstCharRect).get(),
     590             :      mCaret.mOffset, GetRectText(mCaret.mRect).get(), mTextRectArray.mStart,
     591             :      mTextRectArray.mRects.Length(), GetBoolName(mWidgetHasComposition),
     592             :      mPendingCompositionCount, mCompositionStart, mPendingCommitLength,
     593             :      GetRectText(mEditorRect).get()));
     594           1 : }
     595             : 
     596             : bool
     597           0 : ContentCacheInParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
     598             :                                               nsIWidget* aWidget) const
     599             : {
     600           0 :   MOZ_ASSERT(aWidget);
     601             : 
     602           0 :   aEvent.mSucceeded = false;
     603           0 :   aEvent.mReply.mFocusedWidget = aWidget;
     604             : 
     605             :   // ContentCache doesn't store offset of its start with XP linebreaks.
     606             :   // So, we don't support to query contents relative to composition start
     607             :   // offset with XP linebreaks.
     608           0 :   if (NS_WARN_IF(!aEvent.mUseNativeLineBreak)) {
     609           0 :     MOZ_LOG(sContentCacheLog, LogLevel::Error,
     610             :       ("0x%p HandleQueryContentEvent(), FAILED due to query with XP linebreaks",
     611             :        this));
     612           0 :     return false;
     613             :   }
     614             : 
     615           0 :   if (NS_WARN_IF(!aEvent.mInput.IsValidOffset())) {
     616           0 :     MOZ_LOG(sContentCacheLog, LogLevel::Error,
     617             :       ("0x%p HandleQueryContentEvent(), FAILED due to invalid offset",
     618             :        this));
     619           0 :     return false;
     620             :   }
     621             : 
     622           0 :   if (NS_WARN_IF(!aEvent.mInput.IsValidEventMessage(aEvent.mMessage))) {
     623           0 :     MOZ_LOG(sContentCacheLog, LogLevel::Error,
     624             :       ("0x%p HandleQueryContentEvent(), FAILED due to invalid event message",
     625             :        this));
     626           0 :     return false;
     627             :   }
     628             : 
     629           0 :   bool isRelativeToInsertionPoint = aEvent.mInput.mRelativeToInsertionPoint;
     630           0 :   if (isRelativeToInsertionPoint) {
     631           0 :     if (aWidget->PluginHasFocus()) {
     632           0 :       if (NS_WARN_IF(!aEvent.mInput.MakeOffsetAbsolute(0))) {
     633           0 :         MOZ_LOG(sContentCacheLog, LogLevel::Error,
     634             :           ("0x%p HandleQueryContentEvent(), FAILED due to "
     635             :            "aEvent.mInput.MakeOffsetAbsolute(0) failure, aEvent={ mMessage=%s, "
     636             :            "mInput={ mOffset=%" PRId64 ", mLength=%" PRIu32 " } }",
     637             :            this, ToChar(aEvent.mMessage), aEvent.mInput.mOffset,
     638             :            aEvent.mInput.mLength));
     639           0 :         return false;
     640             :       }
     641           0 :     } else if (mWidgetHasComposition) {
     642           0 :       if (NS_WARN_IF(!aEvent.mInput.MakeOffsetAbsolute(
     643             :                                       mCompositionStart +
     644             :                                         mPendingCommitLength))) {
     645           0 :         MOZ_LOG(sContentCacheLog, LogLevel::Error,
     646             :           ("0x%p HandleQueryContentEvent(), FAILED due to "
     647             :            "aEvent.mInput.MakeOffsetAbsolute(mCompositionStart + "
     648             :            "mPendingCommitLength) failure, "
     649             :            "mCompositionStart=%" PRIu32 ", mPendingCommitLength=%" PRIu32 ", "
     650             :            "aEvent={ mMessage=%s, mInput={ mOffset=%" PRId64
     651             :            ", mLength=%" PRIu32 " } }",
     652             :            this, mCompositionStart, mPendingCommitLength,
     653             :            ToChar(aEvent.mMessage), aEvent.mInput.mOffset,
     654             :            aEvent.mInput.mLength));
     655           0 :         return false;
     656             :       }
     657           0 :     } else if (NS_WARN_IF(!mSelection.IsValid())) {
     658           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Error,
     659             :         ("0x%p HandleQueryContentEvent(), FAILED due to mSelection is invalid",
     660             :          this));
     661           0 :       return false;
     662           0 :     } else if (NS_WARN_IF(!aEvent.mInput.MakeOffsetAbsolute(
     663             :                                            mSelection.StartOffset() +
     664             :                                              mPendingCommitLength))) {
     665           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Error,
     666             :         ("0x%p HandleQueryContentEvent(), FAILED due to "
     667             :          "aEvent.mInput.MakeOffsetAbsolute(mSelection.StartOffset() + "
     668             :          "mPendingCommitLength) failure, "
     669             :          "mSelection={ StartOffset()=%d, Length()=%d }, "
     670             :          "mPendingCommitLength=%" PRIu32 ", aEvent={ mMessage=%s, "
     671             :          "mInput={ mOffset=%" PRId64 ", mLength=%" PRIu32 " } }",
     672             :          this, mSelection.StartOffset(), mSelection.Length(),
     673             :          mPendingCommitLength, ToChar(aEvent.mMessage),
     674             :          aEvent.mInput.mOffset, aEvent.mInput.mLength));
     675           0 :       return false;
     676             :     }
     677             :   }
     678             : 
     679           0 :   switch (aEvent.mMessage) {
     680             :     case eQuerySelectedText:
     681           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Info,
     682             :         ("0x%p HandleQueryContentEvent("
     683             :          "aEvent={ mMessage=eQuerySelectedText }, aWidget=0x%p)",
     684             :          this, aWidget));
     685           0 :       if (aWidget->PluginHasFocus()) {
     686           0 :         MOZ_LOG(sContentCacheLog, LogLevel::Info,
     687             :           ("0x%p HandleQueryContentEvent(), "
     688             :            "return emtpy selection becasue plugin has focus",
     689             :            this));
     690           0 :         aEvent.mSucceeded = true;
     691           0 :         aEvent.mReply.mOffset = 0;
     692           0 :         aEvent.mReply.mReversed = false;
     693           0 :         aEvent.mReply.mHasSelection = false;
     694           0 :         return true;
     695             :       }
     696           0 :       if (NS_WARN_IF(!IsSelectionValid())) {
     697             :         // If content cache hasn't been initialized properly, make the query
     698             :         // failed.
     699           0 :         MOZ_LOG(sContentCacheLog, LogLevel::Error,
     700             :           ("0x%p HandleQueryContentEvent(), "
     701             :            "FAILED because mSelection is not valid", this));
     702           0 :         return true;
     703             :       }
     704           0 :       aEvent.mReply.mOffset = mSelection.StartOffset();
     705           0 :       if (mSelection.Collapsed()) {
     706           0 :         aEvent.mReply.mString.Truncate(0);
     707             :       } else {
     708           0 :         if (NS_WARN_IF(mSelection.EndOffset() > mText.Length())) {
     709           0 :           MOZ_LOG(sContentCacheLog, LogLevel::Error,
     710             :             ("0x%p HandleQueryContentEvent(), "
     711             :              "FAILED because mSelection.EndOffset()=%u is larger than "
     712             :              "mText.Length()=%u",
     713             :              this, mSelection.EndOffset(), mText.Length()));
     714           0 :           return false;
     715             :         }
     716             :         aEvent.mReply.mString =
     717           0 :           Substring(mText, aEvent.mReply.mOffset, mSelection.Length());
     718             :       }
     719           0 :       aEvent.mReply.mReversed = mSelection.Reversed();
     720           0 :       aEvent.mReply.mHasSelection = true;
     721           0 :       aEvent.mReply.mWritingMode = mSelection.mWritingMode;
     722           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Info,
     723             :         ("0x%p HandleQueryContentEvent(), "
     724             :          "Succeeded, aEvent={ mReply={ mOffset=%u, mString=\"%s\", "
     725             :          "mReversed=%s, mHasSelection=%s, mWritingMode=%s } }",
     726             :          this, aEvent.mReply.mOffset,
     727             :          GetEscapedUTF8String(aEvent.mReply.mString).get(),
     728             :          GetBoolName(aEvent.mReply.mReversed),
     729             :          GetBoolName(aEvent.mReply.mHasSelection),
     730             :          GetWritingModeName(aEvent.mReply.mWritingMode).get()));
     731           0 :       break;
     732             :     case eQueryTextContent: {
     733           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Info,
     734             :         ("0x%p HandleQueryContentEvent("
     735             :          "aEvent={ mMessage=eQueryTextContent, mInput={ mOffset=%" PRId64
     736             :          ", mLength=%u } }, aWidget=0x%p), mText.Length()=%u",
     737             :          this, aEvent.mInput.mOffset,
     738             :          aEvent.mInput.mLength, aWidget, mText.Length()));
     739           0 :       uint32_t inputOffset = aEvent.mInput.mOffset;
     740             :       uint32_t inputEndOffset =
     741           0 :         std::min(aEvent.mInput.EndOffset(), mText.Length());
     742           0 :       if (NS_WARN_IF(inputEndOffset < inputOffset)) {
     743           0 :         MOZ_LOG(sContentCacheLog, LogLevel::Error,
     744             :           ("0x%p HandleQueryContentEvent(), "
     745             :            "FAILED because inputOffset=%u is larger than inputEndOffset=%u",
     746             :            this, inputOffset, inputEndOffset));
     747           0 :         return false;
     748             :       }
     749           0 :       aEvent.mReply.mOffset = inputOffset;
     750             :       aEvent.mReply.mString =
     751           0 :         Substring(mText, inputOffset, inputEndOffset - inputOffset);
     752           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Info,
     753             :         ("0x%p HandleQueryContentEvent(), "
     754             :          "Succeeded, aEvent={ mReply={ mOffset=%u, mString.Length()=%u } }",
     755             :          this, aEvent.mReply.mOffset, aEvent.mReply.mString.Length()));
     756           0 :       break;
     757             :     }
     758             :     case eQueryTextRect:
     759           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Info,
     760             :         ("0x%p HandleQueryContentEvent("
     761             :          "aEvent={ mMessage=eQueryTextRect, mInput={ mOffset=%" PRId64
     762             :          ", mLength=%u } }, aWidget=0x%p), mText.Length()=%u",
     763             :          this, aEvent.mInput.mOffset, aEvent.mInput.mLength, aWidget,
     764             :          mText.Length()));
     765           0 :       if (NS_WARN_IF(!IsSelectionValid())) {
     766             :         // If content cache hasn't been initialized properly, make the query
     767             :         // failed.
     768           0 :         MOZ_LOG(sContentCacheLog, LogLevel::Error,
     769             :           ("0x%p HandleQueryContentEvent(), "
     770             :            "FAILED because mSelection is not valid", this));
     771           0 :         return true;
     772             :       }
     773             :       // Note that if the query is relative to insertion point, the query was
     774             :       // probably requested by native IME.  In such case, we should return
     775             :       // non-empty rect since returning failure causes IME showing its window
     776             :       // at odd position.
     777           0 :       if (aEvent.mInput.mLength) {
     778           0 :         if (NS_WARN_IF(!GetUnionTextRects(aEvent.mInput.mOffset,
     779             :                                           aEvent.mInput.mLength,
     780             :                                           isRelativeToInsertionPoint,
     781             :                                           aEvent.mReply.mRect))) {
     782             :           // XXX We don't have cache for this request.
     783           0 :           MOZ_LOG(sContentCacheLog, LogLevel::Error,
     784             :             ("0x%p HandleQueryContentEvent(), "
     785             :              "FAILED to get union rect", this));
     786           0 :           return false;
     787             :         }
     788             :       } else {
     789             :         // If the length is 0, we should return caret rect instead.
     790           0 :         if (NS_WARN_IF(!GetCaretRect(aEvent.mInput.mOffset,
     791             :                                      isRelativeToInsertionPoint,
     792             :                                      aEvent.mReply.mRect))) {
     793           0 :           MOZ_LOG(sContentCacheLog, LogLevel::Error,
     794             :             ("0x%p HandleQueryContentEvent(), "
     795             :              "FAILED to get caret rect", this));
     796           0 :           return false;
     797             :         }
     798             :       }
     799           0 :       if (aEvent.mInput.mOffset < mText.Length()) {
     800             :         aEvent.mReply.mString =
     801           0 :           Substring(mText, aEvent.mInput.mOffset,
     802           0 :                     mText.Length() >= aEvent.mInput.EndOffset() ?
     803           0 :                       aEvent.mInput.mLength : UINT32_MAX);
     804             :       } else {
     805           0 :         aEvent.mReply.mString.Truncate(0);
     806             :       }
     807           0 :       aEvent.mReply.mOffset = aEvent.mInput.mOffset;
     808             :       // XXX This may be wrong if storing range isn't in the selection range.
     809           0 :       aEvent.mReply.mWritingMode = mSelection.mWritingMode;
     810           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Info,
     811             :         ("0x%p HandleQueryContentEvent(), "
     812             :          "Succeeded, aEvent={ mReply={ mOffset=%u, mString=\"%s\", "
     813             :          "mWritingMode=%s, mRect=%s } }",
     814             :          this, aEvent.mReply.mOffset,
     815             :          GetEscapedUTF8String(aEvent.mReply.mString).get(),
     816             :          GetWritingModeName(aEvent.mReply.mWritingMode).get(),
     817             :          GetRectText(aEvent.mReply.mRect).get()));
     818           0 :       break;
     819             :     case eQueryCaretRect:
     820           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Info,
     821             :         ("0x%p HandleQueryContentEvent("
     822             :          "aEvent={ mMessage=eQueryCaretRect, mInput={ mOffset=%" PRId64 " } }, "
     823             :          "aWidget=0x%p), mText.Length()=%u",
     824             :          this, aEvent.mInput.mOffset, aWidget, mText.Length()));
     825           0 :       if (NS_WARN_IF(!IsSelectionValid())) {
     826             :         // If content cache hasn't been initialized properly, make the query
     827             :         // failed.
     828           0 :         MOZ_LOG(sContentCacheLog, LogLevel::Error,
     829             :           ("0x%p HandleQueryContentEvent(), "
     830             :            "FAILED because mSelection is not valid", this));
     831           0 :         return true;
     832             :       }
     833             :       // Note that if the query is relative to insertion point, the query was
     834             :       // probably requested by native IME.  In such case, we should return
     835             :       // non-empty rect since returning failure causes IME showing its window
     836             :       // at odd position.
     837           0 :       if (NS_WARN_IF(!GetCaretRect(aEvent.mInput.mOffset,
     838             :                                    isRelativeToInsertionPoint,
     839             :                                    aEvent.mReply.mRect))) {
     840           0 :         MOZ_LOG(sContentCacheLog, LogLevel::Error,
     841             :           ("0x%p HandleQueryContentEvent(), "
     842             :            "FAILED to get caret rect", this));
     843           0 :         return false;
     844             :       }
     845           0 :       aEvent.mReply.mOffset = aEvent.mInput.mOffset;
     846           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Info,
     847             :         ("0x%p HandleQueryContentEvent(), "
     848             :          "Succeeded, aEvent={ mReply={ mOffset=%u, mRect=%s } }",
     849             :          this, aEvent.mReply.mOffset, GetRectText(aEvent.mReply.mRect).get()));
     850           0 :       break;
     851             :     case eQueryEditorRect:
     852           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Info,
     853             :         ("0x%p HandleQueryContentEvent("
     854             :          "aEvent={ mMessage=eQueryEditorRect }, aWidget=0x%p)",
     855             :          this, aWidget));
     856           0 :       aEvent.mReply.mRect = mEditorRect;
     857           0 :       MOZ_LOG(sContentCacheLog, LogLevel::Info,
     858             :         ("0x%p HandleQueryContentEvent(), "
     859             :          "Succeeded, aEvent={ mReply={ mRect=%s } }",
     860             :          this, GetRectText(aEvent.mReply.mRect).get()));
     861           0 :       break;
     862             :     default:
     863           0 :       break;
     864             :   }
     865           0 :   aEvent.mSucceeded = true;
     866           0 :   return true;
     867             : }
     868             : 
     869             : bool
     870           0 : ContentCacheInParent::GetTextRect(uint32_t aOffset,
     871             :                                   bool aRoundToExistingOffset,
     872             :                                   LayoutDeviceIntRect& aTextRect) const
     873             : {
     874           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     875             :     ("0x%p GetTextRect(aOffset=%u, "
     876             :      "aRoundToExistingOffset=%s), "
     877             :      "mTextRectArray={ mStart=%u, mRects.Length()=%" PRIuSIZE " }, "
     878             :      "mSelection={ mAnchor=%u, mFocus=%u }",
     879             :      this, aOffset, GetBoolName(aRoundToExistingOffset),
     880             :      mTextRectArray.mStart, mTextRectArray.mRects.Length(),
     881             :      mSelection.mAnchor, mSelection.mFocus));
     882             : 
     883           0 :   if (!aOffset) {
     884           0 :     NS_WARNING_ASSERTION(!mFirstCharRect.IsEmpty(), "empty rect");
     885           0 :     aTextRect = mFirstCharRect;
     886           0 :     return !aTextRect.IsEmpty();
     887             :   }
     888           0 :   if (aOffset == mSelection.mAnchor) {
     889           0 :     NS_WARNING_ASSERTION(!mSelection.mAnchorCharRects[eNextCharRect].IsEmpty(),
     890             :                          "empty rect");
     891           0 :     aTextRect = mSelection.mAnchorCharRects[eNextCharRect];
     892           0 :     return !aTextRect.IsEmpty();
     893             :   }
     894           0 :   if (mSelection.mAnchor && aOffset == mSelection.mAnchor - 1) {
     895           0 :     NS_WARNING_ASSERTION(!mSelection.mAnchorCharRects[ePrevCharRect].IsEmpty(),
     896             :                          "empty rect");
     897           0 :     aTextRect = mSelection.mAnchorCharRects[ePrevCharRect];
     898           0 :     return !aTextRect.IsEmpty();
     899             :   }
     900           0 :   if (aOffset == mSelection.mFocus) {
     901           0 :     NS_WARNING_ASSERTION(!mSelection.mFocusCharRects[eNextCharRect].IsEmpty(),
     902             :                          "empty rect");
     903           0 :     aTextRect = mSelection.mFocusCharRects[eNextCharRect];
     904           0 :     return !aTextRect.IsEmpty();
     905             :   }
     906           0 :   if (mSelection.mFocus && aOffset == mSelection.mFocus - 1) {
     907           0 :     NS_WARNING_ASSERTION(!mSelection.mFocusCharRects[ePrevCharRect].IsEmpty(),
     908             :                          "empty rect");
     909           0 :     aTextRect = mSelection.mFocusCharRects[ePrevCharRect];
     910           0 :     return !aTextRect.IsEmpty();
     911             :   }
     912             : 
     913           0 :   uint32_t offset = aOffset;
     914           0 :   if (!mTextRectArray.InRange(aOffset)) {
     915           0 :     if (!aRoundToExistingOffset) {
     916           0 :       aTextRect.SetEmpty();
     917           0 :       return false;
     918             :     }
     919           0 :     if (!mTextRectArray.IsValid()) {
     920             :       // If there are no rects in mTextRectArray, we should refer the start of
     921             :       // the selection because IME must query a char rect around it if there is
     922             :       // no composition.
     923           0 :       aTextRect = mSelection.StartCharRect();
     924           0 :       return !aTextRect.IsEmpty();
     925             :     }
     926           0 :     if (offset < mTextRectArray.StartOffset()) {
     927           0 :       offset = mTextRectArray.StartOffset();
     928             :     } else {
     929           0 :       offset = mTextRectArray.EndOffset() - 1;
     930             :     }
     931             :   }
     932           0 :   aTextRect = mTextRectArray.GetRect(offset);
     933           0 :   return !aTextRect.IsEmpty();
     934             : }
     935             : 
     936             : bool
     937           0 : ContentCacheInParent::GetUnionTextRects(
     938             :                         uint32_t aOffset,
     939             :                         uint32_t aLength,
     940             :                         bool aRoundToExistingOffset,
     941             :                         LayoutDeviceIntRect& aUnionTextRect) const
     942             : {
     943           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     944             :     ("0x%p GetUnionTextRects(aOffset=%u, "
     945             :      "aLength=%u, aRoundToExistingOffset=%s), mTextRectArray={ "
     946             :      "mStart=%u, mRects.Length()=%" PRIuSIZE " }, "
     947             :      "mSelection={ mAnchor=%u, mFocus=%u }",
     948             :      this, aOffset, aLength, GetBoolName(aRoundToExistingOffset),
     949             :      mTextRectArray.mStart, mTextRectArray.mRects.Length(),
     950             :      mSelection.mAnchor, mSelection.mFocus));
     951             : 
     952             :   CheckedInt<uint32_t> endOffset =
     953           0 :     CheckedInt<uint32_t>(aOffset) + aLength;
     954           0 :   if (!endOffset.isValid()) {
     955           0 :     return false;
     956             :   }
     957             : 
     958           0 :   if (!mSelection.Collapsed() &&
     959           0 :       aOffset == mSelection.StartOffset() && aLength == mSelection.Length()) {
     960           0 :     NS_WARNING_ASSERTION(!mSelection.mRect.IsEmpty(), "empty rect");
     961           0 :     aUnionTextRect = mSelection.mRect;
     962           0 :     return !aUnionTextRect.IsEmpty();
     963             :   }
     964             : 
     965           0 :   if (aLength == 1) {
     966           0 :     if (!aOffset) {
     967           0 :       NS_WARNING_ASSERTION(!mFirstCharRect.IsEmpty(), "empty rect");
     968           0 :       aUnionTextRect = mFirstCharRect;
     969           0 :       return !aUnionTextRect.IsEmpty();
     970             :     }
     971           0 :     if (aOffset == mSelection.mAnchor) {
     972           0 :       NS_WARNING_ASSERTION(
     973             :         !mSelection.mAnchorCharRects[eNextCharRect].IsEmpty(), "empty rect");
     974           0 :       aUnionTextRect = mSelection.mAnchorCharRects[eNextCharRect];
     975           0 :       return !aUnionTextRect.IsEmpty();
     976             :     }
     977           0 :     if (mSelection.mAnchor && aOffset == mSelection.mAnchor - 1) {
     978           0 :       NS_WARNING_ASSERTION(
     979             :         !mSelection.mAnchorCharRects[ePrevCharRect].IsEmpty(), "empty rect");
     980           0 :       aUnionTextRect = mSelection.mAnchorCharRects[ePrevCharRect];
     981           0 :       return !aUnionTextRect.IsEmpty();
     982             :     }
     983           0 :     if (aOffset == mSelection.mFocus) {
     984           0 :       NS_WARNING_ASSERTION(
     985             :         !mSelection.mFocusCharRects[eNextCharRect].IsEmpty(), "empty rect");
     986           0 :       aUnionTextRect = mSelection.mFocusCharRects[eNextCharRect];
     987           0 :       return !aUnionTextRect.IsEmpty();
     988             :     }
     989           0 :     if (mSelection.mFocus && aOffset == mSelection.mFocus - 1) {
     990           0 :       NS_WARNING_ASSERTION(
     991             :         !mSelection.mFocusCharRects[ePrevCharRect].IsEmpty(), "empty rect");
     992           0 :       aUnionTextRect = mSelection.mFocusCharRects[ePrevCharRect];
     993           0 :       return !aUnionTextRect.IsEmpty();
     994             :     }
     995             :   }
     996             : 
     997             :   // Even if some text rects are not cached of the queried range,
     998             :   // we should return union rect when the first character's rect is cached
     999             :   // since the first character rect is important and the others are not so
    1000             :   // in most cases.
    1001             : 
    1002           0 :   if (!aOffset && aOffset != mSelection.mAnchor &&
    1003           0 :       aOffset != mSelection.mFocus && !mTextRectArray.InRange(aOffset)) {
    1004             :     // The first character rect isn't cached.
    1005           0 :     return false;
    1006             :   }
    1007             : 
    1008           0 :   if ((aRoundToExistingOffset && mTextRectArray.HasRects()) ||
    1009           0 :       mTextRectArray.IsOverlappingWith(aOffset, aLength)) {
    1010             :     aUnionTextRect =
    1011             :       mTextRectArray.GetUnionRectAsFarAsPossible(aOffset, aLength,
    1012           0 :                                                  aRoundToExistingOffset);
    1013             :   } else {
    1014           0 :     aUnionTextRect.SetEmpty();
    1015             :   }
    1016             : 
    1017           0 :   if (!aOffset) {
    1018           0 :     aUnionTextRect = aUnionTextRect.Union(mFirstCharRect);
    1019             :   }
    1020           0 :   if (aOffset <= mSelection.mAnchor && mSelection.mAnchor < endOffset.value()) {
    1021             :     aUnionTextRect =
    1022           0 :       aUnionTextRect.Union(mSelection.mAnchorCharRects[eNextCharRect]);
    1023             :   }
    1024           0 :   if (mSelection.mAnchor && aOffset <= mSelection.mAnchor - 1 &&
    1025           0 :       mSelection.mAnchor - 1 < endOffset.value()) {
    1026             :     aUnionTextRect =
    1027           0 :       aUnionTextRect.Union(mSelection.mAnchorCharRects[ePrevCharRect]);
    1028             :   }
    1029           0 :   if (aOffset <= mSelection.mFocus && mSelection.mFocus < endOffset.value()) {
    1030             :     aUnionTextRect =
    1031           0 :       aUnionTextRect.Union(mSelection.mFocusCharRects[eNextCharRect]);
    1032             :   }
    1033           0 :   if (mSelection.mFocus && aOffset <= mSelection.mFocus - 1 &&
    1034           0 :       mSelection.mFocus - 1 < endOffset.value()) {
    1035             :     aUnionTextRect =
    1036           0 :       aUnionTextRect.Union(mSelection.mFocusCharRects[ePrevCharRect]);
    1037             :   }
    1038             : 
    1039           0 :   return !aUnionTextRect.IsEmpty();
    1040             : }
    1041             : 
    1042             : bool
    1043           0 : ContentCacheInParent::GetCaretRect(uint32_t aOffset,
    1044             :                                    bool aRoundToExistingOffset,
    1045             :                                    LayoutDeviceIntRect& aCaretRect) const
    1046             : {
    1047           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
    1048             :     ("0x%p GetCaretRect(aOffset=%u, "
    1049             :      "aRoundToExistingOffset=%s), "
    1050             :      "mCaret={ mOffset=%u, mRect=%s, IsValid()=%s }, mTextRectArray={ "
    1051             :      "mStart=%u, mRects.Length()=%" PRIuSIZE " }, mSelection={ mAnchor=%u, mFocus=%u, "
    1052             :      "mWritingMode=%s, mAnchorCharRects[eNextCharRect]=%s, "
    1053             :      "mAnchorCharRects[ePrevCharRect]=%s, mFocusCharRects[eNextCharRect]=%s, "
    1054             :      "mFocusCharRects[ePrevCharRect]=%s }, mFirstCharRect=%s",
    1055             :      this, aOffset, GetBoolName(aRoundToExistingOffset),
    1056             :      mCaret.mOffset, GetRectText(mCaret.mRect).get(),
    1057             :      GetBoolName(mCaret.IsValid()), mTextRectArray.mStart,
    1058             :      mTextRectArray.mRects.Length(), mSelection.mAnchor, mSelection.mFocus,
    1059             :      GetWritingModeName(mSelection.mWritingMode).get(),
    1060             :      GetRectText(mSelection.mAnchorCharRects[eNextCharRect]).get(),
    1061             :      GetRectText(mSelection.mAnchorCharRects[ePrevCharRect]).get(),
    1062             :      GetRectText(mSelection.mFocusCharRects[eNextCharRect]).get(),
    1063             :      GetRectText(mSelection.mFocusCharRects[ePrevCharRect]).get(),
    1064             :      GetRectText(mFirstCharRect).get()));
    1065             : 
    1066           0 :   if (mCaret.IsValid() && mCaret.mOffset == aOffset) {
    1067           0 :     aCaretRect = mCaret.mRect;
    1068           0 :     return true;
    1069             :   }
    1070             : 
    1071             :   // Guess caret rect from the text rect if it's stored.
    1072           0 :   if (!GetTextRect(aOffset, aRoundToExistingOffset, aCaretRect)) {
    1073             :     // There might be previous character rect in the cache.  If so, we can
    1074             :     // guess the caret rect with it.
    1075           0 :     if (!aOffset ||
    1076           0 :         !GetTextRect(aOffset - 1, aRoundToExistingOffset, aCaretRect)) {
    1077           0 :       aCaretRect.SetEmpty();
    1078           0 :       return false;
    1079             :     }
    1080             : 
    1081           0 :     if (mSelection.mWritingMode.IsVertical()) {
    1082           0 :       aCaretRect.y = aCaretRect.YMost();
    1083             :     } else {
    1084             :       // XXX bidi-unaware.
    1085           0 :       aCaretRect.x = aCaretRect.XMost();
    1086             :     }
    1087             :   }
    1088             : 
    1089             :   // XXX This is not bidi aware because we don't cache each character's
    1090             :   //     direction.  However, this is usually used by IME, so, assuming the
    1091             :   //     character is in LRT context must not cause any problem.
    1092           0 :   if (mSelection.mWritingMode.IsVertical()) {
    1093           0 :     aCaretRect.height = mCaret.IsValid() ? mCaret.mRect.height : 1;
    1094             :   } else {
    1095           0 :     aCaretRect.width = mCaret.IsValid() ? mCaret.mRect.width : 1;
    1096             :   }
    1097           0 :   return true;
    1098             : }
    1099             : 
    1100             : bool
    1101           0 : ContentCacheInParent::OnCompositionEvent(const WidgetCompositionEvent& aEvent)
    1102             : {
    1103           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
    1104             :     ("0x%p OnCompositionEvent(aEvent={ "
    1105             :      "mMessage=%s, mData=\"%s\" (Length()=%u), mRanges->Length()=%" PRIuSIZE " }), "
    1106             :      "mPendingEventsNeedingAck=%u, mWidgetHasComposition=%s, "
    1107             :      "mPendingCompositionCount=%u, mCommitStringByRequest=0x%p",
    1108             :      this, ToChar(aEvent.mMessage),
    1109             :      GetEscapedUTF8String(aEvent.mData).get(), aEvent.mData.Length(),
    1110             :      aEvent.mRanges ? aEvent.mRanges->Length() : 0, mPendingEventsNeedingAck,
    1111             :      GetBoolName(mWidgetHasComposition), mPendingCompositionCount,
    1112             :      mCommitStringByRequest));
    1113             : 
    1114             :   // We must be able to simulate the selection because
    1115             :   // we might not receive selection updates in time
    1116           0 :   if (!mWidgetHasComposition) {
    1117           0 :     if (aEvent.mWidget && aEvent.mWidget->PluginHasFocus()) {
    1118             :       // If focus is on plugin, we cannot get selection range
    1119           0 :       mCompositionStart = 0;
    1120           0 :     } else if (mCompositionStartInChild != UINT32_MAX) {
    1121             :       // If there is pending composition in the remote process, let's use
    1122             :       // its start offset temporarily because this stores a lot of information
    1123             :       // around it and the user must look around there, so, showing some UI
    1124             :       // around it must make sense.
    1125           0 :       mCompositionStart = mCompositionStartInChild;
    1126             :     } else {
    1127           0 :       mCompositionStart = mSelection.StartOffset();
    1128             :     }
    1129           0 :     MOZ_ASSERT(aEvent.mMessage == eCompositionStart);
    1130           0 :     MOZ_RELEASE_ASSERT(mPendingCompositionCount < UINT8_MAX);
    1131           0 :     mPendingCompositionCount++;
    1132             :   }
    1133             : 
    1134           0 :   mWidgetHasComposition = !aEvent.CausesDOMCompositionEndEvent();
    1135             : 
    1136           0 :   if (!mWidgetHasComposition) {
    1137           0 :     mCompositionStart = UINT32_MAX;
    1138           0 :     if (mPendingCompositionCount == 1) {
    1139           0 :       mPendingCommitLength = aEvent.mData.Length();
    1140             :     }
    1141           0 :     mIsPendingLastCommitEvent = true;
    1142           0 :   } else if (aEvent.mMessage != eCompositionStart) {
    1143           0 :     mCompositionString = aEvent.mData;
    1144             :   }
    1145             : 
    1146             :   // During REQUEST_TO_COMMIT_COMPOSITION or REQUEST_TO_CANCEL_COMPOSITION,
    1147             :   // widget usually sends a eCompositionChange and/or eCompositionCommit event
    1148             :   // to finalize or clear the composition, respectively.  In this time,
    1149             :   // we need to intercept all composition events here and pass the commit
    1150             :   // string for returning to the remote process as a result of
    1151             :   // RequestIMEToCommitComposition().  Then, eCommitComposition event will
    1152             :   // be dispatched with the committed string in the remote process internally.
    1153           0 :   if (mCommitStringByRequest) {
    1154           0 :     MOZ_ASSERT(aEvent.mMessage == eCompositionChange ||
    1155             :                aEvent.mMessage == eCompositionCommit);
    1156           0 :     *mCommitStringByRequest = aEvent.mData;
    1157             :     // We need to wait eCompositionCommitRequestHandled from the remote process
    1158             :     // in this case.  Therefore, mPendingEventsNeedingAck needs to be
    1159             :     // incremented here.
    1160           0 :     if (!mWidgetHasComposition) {
    1161           0 :       mPendingEventsNeedingAck++;
    1162             :     }
    1163             :     // Cancel mIsPendingLastCommitEvent because we won't send the commit event
    1164             :     // to the remote process.
    1165           0 :     mIsPendingLastCommitEvent = false;
    1166           0 :     return false;
    1167             :   }
    1168             : 
    1169           0 :   mPendingEventsNeedingAck++;
    1170           0 :   return true;
    1171             : }
    1172             : 
    1173             : void
    1174           0 : ContentCacheInParent::OnSelectionEvent(
    1175             :                         const WidgetSelectionEvent& aSelectionEvent)
    1176             : {
    1177           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
    1178             :     ("0x%p OnSelectionEvent(aEvent={ "
    1179             :      "mMessage=%s, mOffset=%u, mLength=%u, mReversed=%s, "
    1180             :      "mExpandToClusterBoundary=%s, mUseNativeLineBreak=%s }), "
    1181             :      "mPendingEventsNeedingAck=%u, mWidgetHasComposition=%s, "
    1182             :      "mPendingCompositionCount=%u",
    1183             :      this, ToChar(aSelectionEvent.mMessage),
    1184             :      aSelectionEvent.mOffset, aSelectionEvent.mLength,
    1185             :      GetBoolName(aSelectionEvent.mReversed),
    1186             :      GetBoolName(aSelectionEvent.mExpandToClusterBoundary),
    1187             :      GetBoolName(aSelectionEvent.mUseNativeLineBreak), mPendingEventsNeedingAck,
    1188             :      GetBoolName(mWidgetHasComposition), mPendingCompositionCount));
    1189             : 
    1190           0 :   mPendingEventsNeedingAck++;
    1191           0 : }
    1192             : 
    1193             : void
    1194           0 : ContentCacheInParent::OnEventNeedingAckHandled(nsIWidget* aWidget,
    1195             :                                                 EventMessage aMessage)
    1196             : {
    1197             :   // This is called when the child process receives WidgetCompositionEvent or
    1198             :   // WidgetSelectionEvent.
    1199             : 
    1200           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
    1201             :     ("0x%p OnEventNeedingAckHandled(aWidget=0x%p, "
    1202             :      "aMessage=%s), mPendingEventsNeedingAck=%u, mPendingCompositionCount=%" PRIu8,
    1203             :      this, aWidget, ToChar(aMessage), mPendingEventsNeedingAck, mPendingCompositionCount));
    1204             : 
    1205           0 :   if (WidgetCompositionEvent::IsFollowedByCompositionEnd(aMessage) ||
    1206             :       aMessage == eCompositionCommitRequestHandled) {
    1207           0 :     MOZ_RELEASE_ASSERT(mPendingCompositionCount > 0);
    1208           0 :     mPendingCompositionCount--;
    1209             :     // Forget composition string only when the latest composition string is
    1210             :     // handled in the remote process because if there is 2 or more pending
    1211             :     // composition, this value shouldn't be referred.
    1212           0 :     if (!mPendingCompositionCount) {
    1213           0 :       mCompositionString.Truncate();
    1214           0 :       mIsPendingLastCommitEvent = false;
    1215             :     }
    1216             :     // Forget pending commit string length if it's handled in the remote
    1217             :     // process.  Note that this doesn't care too old composition's commit
    1218             :     // string because in such case, we cannot return proper information
    1219             :     // to IME synchornously.
    1220           0 :     mPendingCommitLength = 0;
    1221             :   }
    1222             : 
    1223           0 :   MOZ_RELEASE_ASSERT(mPendingEventsNeedingAck > 0);
    1224           0 :   if (--mPendingEventsNeedingAck) {
    1225           0 :     return;
    1226             :   }
    1227             : 
    1228           0 :   FlushPendingNotifications(aWidget);
    1229             : }
    1230             : 
    1231             : bool
    1232           0 : ContentCacheInParent::RequestIMEToCommitComposition(nsIWidget* aWidget,
    1233             :                                                     bool aCancel,
    1234             :                                                     nsAString& aCommittedString)
    1235             : {
    1236           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
    1237             :     ("0x%p RequestToCommitComposition(aWidget=%p, "
    1238             :      "aCancel=%s), mPendingCompositionCount=%u, "
    1239             :      "IMEStateManager::DoesTabParentHaveIMEFocus(&mTabParent)=%s, "
    1240             :      "mWidgetHasComposition=%s, mCommitStringByRequest=%p",
    1241             :      this, aWidget, GetBoolName(aCancel), mPendingCompositionCount,
    1242             :      GetBoolName(IMEStateManager::DoesTabParentHaveIMEFocus(&mTabParent)),
    1243             :      GetBoolName(mWidgetHasComposition), mCommitStringByRequest));
    1244             : 
    1245           0 :   MOZ_ASSERT(!mCommitStringByRequest);
    1246             : 
    1247             :   // If there are 2 or more pending compositions, we already sent
    1248             :   // eCompositionCommit(AsIs) to the remote process.  So, this request is
    1249             :   // too late for IME.  The remote process should wait following
    1250             :   // composition events for cleaning up TextComposition and handle the
    1251             :   // request as it's handled asynchronously.
    1252           0 :   if (mPendingCompositionCount > 1) {
    1253           0 :     return false;
    1254             :   }
    1255             : 
    1256             :   // If TabParent which has IME focus was already changed to different one, the
    1257             :   // request shouldn't be sent to IME because it's too late.
    1258           0 :   if (!IMEStateManager::DoesTabParentHaveIMEFocus(&mTabParent)) {
    1259             :     // Use the latest composition string which may not be handled in the
    1260             :     // remote process for avoiding data loss.
    1261           0 :     aCommittedString = mCompositionString;
    1262           0 :     return true;
    1263             :   }
    1264             : 
    1265             :   // Even if the remote process has IME focus and there is no pending
    1266             :   // composition, we may have already sent eCompositionCommit(AsIs) event
    1267             :   // to it.  If so, the remote process will receive composition events
    1268             :   // which causes cleaning up TextComposition.  So, this shouldn't do nothing
    1269             :   // and TextComposition should handle the request as it's handled
    1270             :   // asynchronously.
    1271           0 :   if (mIsPendingLastCommitEvent) {
    1272           0 :     return false;
    1273             :   }
    1274             : 
    1275             :   RefPtr<TextComposition> composition =
    1276           0 :     IMEStateManager::GetTextCompositionFor(aWidget);
    1277           0 :   if (NS_WARN_IF(!composition)) {
    1278           0 :     MOZ_LOG(sContentCacheLog, LogLevel::Warning,
    1279             :       ("  0x%p RequestToCommitComposition(), "
    1280             :        "does nothing due to no composition", this));
    1281           0 :     return false;
    1282             :   }
    1283             : 
    1284           0 :   mCommitStringByRequest = &aCommittedString;
    1285             : 
    1286           0 :   aWidget->NotifyIME(IMENotification(aCancel ? REQUEST_TO_CANCEL_COMPOSITION :
    1287           0 :                                                REQUEST_TO_COMMIT_COMPOSITION));
    1288             : 
    1289           0 :   mCommitStringByRequest = nullptr;
    1290             : 
    1291           0 :   MOZ_LOG(sContentCacheLog, LogLevel::Info,
    1292             :     ("  0x%p RequestToCommitComposition(), "
    1293             :      "mWidgetHasComposition=%s, the composition %s committed synchronously",
    1294             :      this, GetBoolName(mWidgetHasComposition),
    1295             :      composition->Destroyed() ? "WAS" : "has NOT been"));
    1296             : 
    1297           0 :   if (!composition->Destroyed()) {
    1298             :     // When the composition isn't committed synchronously, the remote process's
    1299             :     // TextComposition instance will synthesize commit events and wait to
    1300             :     // receive delayed composition events.  When TextComposition instances both
    1301             :     // in this process and the remote process will be destroyed when delayed
    1302             :     // composition events received. TextComposition instance in the parent
    1303             :     // process will dispatch following composition events and be destroyed
    1304             :     // normally. On the other hand, TextComposition instance in the remote
    1305             :     // process won't dispatch following composition events and will be
    1306             :     // destroyed by IMEStateManager::DispatchCompositionEvent().
    1307           0 :     return false;
    1308             :   }
    1309             : 
    1310             :   // When the composition is committed synchronously, the commit string will be
    1311             :   // returned to the remote process. Then, PuppetWidget will dispatch
    1312             :   // eCompositionCommit event with the returned commit string (i.e., the value
    1313             :   // is aCommittedString of this method).  Finally, TextComposition instance in
    1314             :   // the remote process will be destroyed by
    1315             :   // IMEStateManager::DispatchCompositionEvent() at receiving the
    1316             :   // eCompositionCommit event (Note that TextComposition instance in this
    1317             :   // process was already destroyed).
    1318           0 :   return true;
    1319             : }
    1320             : 
    1321             : void
    1322           0 : ContentCacheInParent::MaybeNotifyIME(nsIWidget* aWidget,
    1323             :                                      const IMENotification& aNotification)
    1324             : {
    1325           0 :   if (!mPendingEventsNeedingAck) {
    1326           0 :     IMEStateManager::NotifyIME(aNotification, aWidget, &mTabParent);
    1327           0 :     return;
    1328             :   }
    1329             : 
    1330           0 :   switch (aNotification.mMessage) {
    1331             :     case NOTIFY_IME_OF_SELECTION_CHANGE:
    1332           0 :       mPendingSelectionChange.MergeWith(aNotification);
    1333           0 :       break;
    1334             :     case NOTIFY_IME_OF_TEXT_CHANGE:
    1335           0 :       mPendingTextChange.MergeWith(aNotification);
    1336           0 :       break;
    1337             :     case NOTIFY_IME_OF_POSITION_CHANGE:
    1338           0 :       mPendingLayoutChange.MergeWith(aNotification);
    1339           0 :       break;
    1340             :     case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
    1341           0 :       mPendingCompositionUpdate.MergeWith(aNotification);
    1342           0 :       break;
    1343             :     default:
    1344           0 :       MOZ_CRASH("Unsupported notification");
    1345             :       break;
    1346             :   }
    1347             : }
    1348             : 
    1349             : void
    1350           0 : ContentCacheInParent::FlushPendingNotifications(nsIWidget* aWidget)
    1351             : {
    1352           0 :   MOZ_ASSERT(!mPendingEventsNeedingAck);
    1353             : 
    1354             :   // If the TabParent's widget has already gone, this can do nothing since
    1355             :   // widget is necessary to notify IME of something.
    1356           0 :   if (!aWidget) {
    1357           0 :     return;
    1358             :   }
    1359             : 
    1360             :   // New notifications which are notified during flushing pending notifications
    1361             :   // should be merged again.
    1362           0 :   mPendingEventsNeedingAck++;
    1363             : 
    1364           0 :   nsCOMPtr<nsIWidget> widget = aWidget;
    1365             : 
    1366             :   // First, text change notification should be sent because selection change
    1367             :   // notification notifies IME of current selection range in the latest content.
    1368             :   // So, IME may need the latest content before that.
    1369           0 :   if (mPendingTextChange.HasNotification()) {
    1370           0 :     IMENotification notification(mPendingTextChange);
    1371           0 :     if (!widget->Destroyed()) {
    1372           0 :       mPendingTextChange.Clear();
    1373           0 :       IMEStateManager::NotifyIME(notification, widget, &mTabParent);
    1374             :     }
    1375             :   }
    1376             : 
    1377           0 :   if (mPendingSelectionChange.HasNotification()) {
    1378           0 :     IMENotification notification(mPendingSelectionChange);
    1379           0 :     if (!widget->Destroyed()) {
    1380           0 :       mPendingSelectionChange.Clear();
    1381           0 :       IMEStateManager::NotifyIME(notification, widget, &mTabParent);
    1382             :     }
    1383             :   }
    1384             : 
    1385             :   // Layout change notification should be notified after selection change
    1386             :   // notification because IME may want to query position of new caret position.
    1387           0 :   if (mPendingLayoutChange.HasNotification()) {
    1388           0 :     IMENotification notification(mPendingLayoutChange);
    1389           0 :     if (!widget->Destroyed()) {
    1390           0 :       mPendingLayoutChange.Clear();
    1391           0 :       IMEStateManager::NotifyIME(notification, widget, &mTabParent);
    1392             :     }
    1393             :   }
    1394             : 
    1395             :   // Finally, send composition update notification because it notifies IME of
    1396             :   // finishing handling whole sending events.
    1397           0 :   if (mPendingCompositionUpdate.HasNotification()) {
    1398           0 :     IMENotification notification(mPendingCompositionUpdate);
    1399           0 :     if (!widget->Destroyed()) {
    1400           0 :       mPendingCompositionUpdate.Clear();
    1401           0 :       IMEStateManager::NotifyIME(notification, widget, &mTabParent);
    1402             :     }
    1403             :   }
    1404             : 
    1405           0 :   if (!--mPendingEventsNeedingAck && !widget->Destroyed() &&
    1406           0 :       (mPendingTextChange.HasNotification() ||
    1407           0 :        mPendingSelectionChange.HasNotification() ||
    1408           0 :        mPendingLayoutChange.HasNotification() ||
    1409           0 :        mPendingCompositionUpdate.HasNotification())) {
    1410           0 :     FlushPendingNotifications(widget);
    1411             :   }
    1412             : }
    1413             : 
    1414             : /*****************************************************************************
    1415             :  * mozilla::ContentCache::TextRectArray
    1416             :  *****************************************************************************/
    1417             : 
    1418             : LayoutDeviceIntRect
    1419           0 : ContentCache::TextRectArray::GetRect(uint32_t aOffset) const
    1420             : {
    1421           0 :   LayoutDeviceIntRect rect;
    1422           0 :   if (InRange(aOffset)) {
    1423           0 :     rect = mRects[aOffset - mStart];
    1424             :   }
    1425           0 :   return rect;
    1426             : }
    1427             : 
    1428             : LayoutDeviceIntRect
    1429           0 : ContentCache::TextRectArray::GetUnionRect(uint32_t aOffset,
    1430             :                                           uint32_t aLength) const
    1431             : {
    1432           0 :   LayoutDeviceIntRect rect;
    1433           0 :   if (!InRange(aOffset, aLength)) {
    1434           0 :     return rect;
    1435             :   }
    1436           0 :   for (uint32_t i = 0; i < aLength; i++) {
    1437           0 :     rect = rect.Union(mRects[aOffset - mStart + i]);
    1438             :   }
    1439           0 :   return rect;
    1440             : }
    1441             : 
    1442             : LayoutDeviceIntRect
    1443           0 : ContentCache::TextRectArray::GetUnionRectAsFarAsPossible(
    1444             :                                uint32_t aOffset,
    1445             :                                uint32_t aLength,
    1446             :                                bool aRoundToExistingOffset) const
    1447             : {
    1448           0 :   LayoutDeviceIntRect rect;
    1449           0 :   if (!HasRects() ||
    1450           0 :       (!aRoundToExistingOffset && !IsOverlappingWith(aOffset, aLength))) {
    1451           0 :     return rect;
    1452             :   }
    1453           0 :   uint32_t startOffset = std::max(aOffset, mStart);
    1454           0 :   if (aRoundToExistingOffset && startOffset >= EndOffset()) {
    1455           0 :     startOffset = EndOffset() - 1;
    1456             :   }
    1457           0 :   uint32_t endOffset = std::min(aOffset + aLength, EndOffset());
    1458           0 :   if (aRoundToExistingOffset && endOffset < mStart + 1) {
    1459           0 :     endOffset = mStart + 1;
    1460             :   }
    1461           0 :   if (NS_WARN_IF(endOffset < startOffset)) {
    1462           0 :     return rect;
    1463             :   }
    1464           0 :   for (uint32_t i = 0; i < endOffset - startOffset; i++) {
    1465           0 :     rect = rect.Union(mRects[startOffset - mStart + i]);
    1466             :   }
    1467           0 :   return rect;
    1468             : }
    1469             : 
    1470             : } // namespace mozilla

Generated by: LCOV version 1.13