LCOV - code coverage report
Current view: top level - widget - ContentCache.h (source / functions) Hit Total Coverage
Test: output.info Lines: 35 91 38.5 %
Date: 2017-07-14 16:53:18 Functions: 11 29 37.9 %
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             : #ifndef mozilla_ContentCache_h
       9             : #define mozilla_ContentCache_h
      10             : 
      11             : #include <stdint.h>
      12             : 
      13             : #include "mozilla/Assertions.h"
      14             : #include "mozilla/CheckedInt.h"
      15             : #include "mozilla/EventForwards.h"
      16             : #include "mozilla/WritingModes.h"
      17             : #include "nsIWidget.h"
      18             : #include "nsString.h"
      19             : #include "nsTArray.h"
      20             : #include "Units.h"
      21             : 
      22             : namespace mozilla {
      23             : 
      24             : class ContentCacheInParent;
      25             : 
      26             : namespace dom {
      27             : class TabParent;
      28             : } // namespace dom
      29             : 
      30             : /**
      31             :  * ContentCache stores various information of the child content.
      32             :  * This class has members which are necessary both in parent process and
      33             :  * content process.
      34             :  */
      35             : 
      36           1 : class ContentCache
      37             : {
      38             : public:
      39             :   typedef InfallibleTArray<LayoutDeviceIntRect> RectArray;
      40             :   typedef widget::IMENotification IMENotification;
      41             : 
      42             :   ContentCache();
      43             : 
      44             : protected:
      45             :   // Whole text in the target
      46             :   nsString mText;
      47             : 
      48             :   // Start offset of the composition string.
      49             :   uint32_t mCompositionStart;
      50             : 
      51             :   enum
      52             :   {
      53             :     ePrevCharRect = 1,
      54             :     eNextCharRect = 0
      55             :   };
      56             : 
      57             :   struct Selection final
      58             :   {
      59             :     // Following values are offset in "flat text".
      60             :     uint32_t mAnchor;
      61             :     uint32_t mFocus;
      62             : 
      63             :     WritingMode mWritingMode;
      64             : 
      65             :     // Character rects at previous and next character of mAnchor and mFocus.
      66             :     // The reason why ContentCache needs to store each previous character of
      67             :     // them is IME may query character rect of the last character of a line
      68             :     // when caret is at the end of the line.
      69             :     // Note that use ePrevCharRect and eNextCharRect for accessing each item.
      70             :     LayoutDeviceIntRect mAnchorCharRects[2];
      71             :     LayoutDeviceIntRect mFocusCharRects[2];
      72             : 
      73             :     // Whole rect of selected text. This is empty if the selection is collapsed.
      74             :     LayoutDeviceIntRect mRect;
      75             : 
      76           3 :     Selection()
      77           3 :       : mAnchor(UINT32_MAX)
      78           3 :       , mFocus(UINT32_MAX)
      79             :     {
      80           3 :     }
      81             : 
      82           1 :     void Clear()
      83             :     {
      84           1 :       mAnchor = mFocus = UINT32_MAX;
      85           1 :       mWritingMode = WritingMode();
      86           1 :       ClearAnchorCharRects();
      87           1 :       ClearFocusCharRects();
      88           1 :       mRect.SetEmpty();
      89           1 :     }
      90             : 
      91           1 :     void ClearAnchorCharRects()
      92             :     {
      93           3 :       for (size_t i = 0; i < ArrayLength(mAnchorCharRects); i++) {
      94           2 :         mAnchorCharRects[i].SetEmpty();
      95             :       }
      96           1 :     }
      97           1 :     void ClearFocusCharRects()
      98             :     {
      99           3 :       for (size_t i = 0; i < ArrayLength(mFocusCharRects); i++) {
     100           2 :         mFocusCharRects[i].SetEmpty();
     101             :       }
     102           1 :     }
     103             : 
     104           0 :     bool IsValid() const
     105             :     {
     106           0 :       return mAnchor != UINT32_MAX && mFocus != UINT32_MAX;
     107             :     }
     108           0 :     bool Collapsed() const
     109             :     {
     110           0 :       NS_ASSERTION(IsValid(),
     111             :                    "The caller should check if the selection is valid");
     112           0 :       return mFocus == mAnchor;
     113             :     }
     114           0 :     bool Reversed() const
     115             :     {
     116           0 :       NS_ASSERTION(IsValid(),
     117             :                    "The caller should check if the selection is valid");
     118           0 :       return mFocus < mAnchor;
     119             :     }
     120           0 :     uint32_t StartOffset() const
     121             :     {
     122           0 :       NS_ASSERTION(IsValid(),
     123             :                    "The caller should check if the selection is valid");
     124           0 :       return Reversed() ? mFocus : mAnchor;
     125             :     }
     126           0 :     uint32_t EndOffset() const
     127             :     {
     128           0 :       NS_ASSERTION(IsValid(),
     129             :                    "The caller should check if the selection is valid");
     130           0 :       return Reversed() ? mAnchor : mFocus;
     131             :     }
     132           0 :     uint32_t Length() const
     133             :     {
     134           0 :       NS_ASSERTION(IsValid(),
     135             :                    "The caller should check if the selection is valid");
     136           0 :       return Reversed() ? mAnchor - mFocus : mFocus - mAnchor;
     137             :     }
     138           0 :     LayoutDeviceIntRect StartCharRect() const
     139             :     {
     140           0 :       NS_ASSERTION(IsValid(),
     141             :                    "The caller should check if the selection is valid");
     142           0 :       return Reversed() ? mFocusCharRects[eNextCharRect] :
     143           0 :                           mAnchorCharRects[eNextCharRect];
     144             :     }
     145             :     LayoutDeviceIntRect EndCharRect() const
     146             :     {
     147             :       NS_ASSERTION(IsValid(),
     148             :                    "The caller should check if the selection is valid");
     149             :       return Reversed() ? mAnchorCharRects[eNextCharRect] :
     150             :                           mFocusCharRects[eNextCharRect];
     151             :     }
     152             :   } mSelection;
     153             : 
     154           0 :   bool IsSelectionValid() const
     155             :   {
     156           0 :     return mSelection.IsValid() && mSelection.EndOffset() <= mText.Length();
     157             :   }
     158             : 
     159             :   // Stores first char rect because Yosemite's Japanese IME sometimes tries
     160             :   // to query it.  If there is no text, this is caret rect.
     161             :   LayoutDeviceIntRect mFirstCharRect;
     162             : 
     163             :   struct Caret final
     164             :   {
     165             :     uint32_t mOffset;
     166             :     LayoutDeviceIntRect mRect;
     167             : 
     168           3 :     Caret()
     169           3 :       : mOffset(UINT32_MAX)
     170             :     {
     171           3 :     }
     172             : 
     173           1 :     void Clear()
     174             :     {
     175           1 :       mOffset = UINT32_MAX;
     176           1 :       mRect.SetEmpty();
     177           1 :     }
     178             : 
     179           0 :     bool IsValid() const { return mOffset != UINT32_MAX; }
     180             : 
     181             :     uint32_t Offset() const
     182             :     {
     183             :       NS_ASSERTION(IsValid(),
     184             :                    "The caller should check if the caret is valid");
     185             :       return mOffset;
     186             :     }
     187             :   } mCaret;
     188             : 
     189           2 :   struct TextRectArray final
     190             :   {
     191             :     uint32_t mStart;
     192             :     RectArray mRects;
     193             : 
     194           3 :     TextRectArray()
     195           3 :       : mStart(UINT32_MAX)
     196             :     {
     197           3 :     }
     198             : 
     199           1 :     void Clear()
     200             :     {
     201           1 :       mStart = UINT32_MAX;
     202           1 :       mRects.Clear();
     203           1 :     }
     204             : 
     205           0 :     bool IsValid() const
     206             :     {
     207           0 :       if (mStart == UINT32_MAX) {
     208           0 :         return false;
     209             :       }
     210             :       CheckedInt<uint32_t> endOffset =
     211           0 :         CheckedInt<uint32_t>(mStart) + mRects.Length();
     212           0 :       return endOffset.isValid();
     213             :     }
     214           0 :     bool HasRects() const
     215             :     {
     216           0 :       return IsValid() && !mRects.IsEmpty();
     217             :     }
     218           0 :     uint32_t StartOffset() const
     219             :     {
     220           0 :       NS_ASSERTION(IsValid(),
     221             :                    "The caller should check if the caret is valid");
     222           0 :       return mStart;
     223             :     }
     224           0 :     uint32_t EndOffset() const
     225             :     {
     226           0 :       NS_ASSERTION(IsValid(),
     227             :                    "The caller should check if the caret is valid");
     228           0 :       if (!IsValid()) {
     229           0 :         return UINT32_MAX;
     230             :       }
     231           0 :       return mStart + mRects.Length();
     232             :     }
     233           0 :     bool InRange(uint32_t aOffset) const
     234             :     {
     235           0 :       return IsValid() &&
     236           0 :              StartOffset() <= aOffset && aOffset < EndOffset();
     237             :     }
     238           0 :     bool InRange(uint32_t aOffset, uint32_t aLength) const
     239             :     {
     240             :       CheckedInt<uint32_t> endOffset =
     241           0 :         CheckedInt<uint32_t>(aOffset) + aLength;
     242           0 :       if (NS_WARN_IF(!endOffset.isValid())) {
     243           0 :         return false;
     244             :       }
     245           0 :       return InRange(aOffset) && aOffset + aLength <= EndOffset();
     246             :     }
     247           0 :     bool IsOverlappingWith(uint32_t aOffset, uint32_t aLength) const
     248             :     {
     249           0 :       if (!HasRects() || aOffset == UINT32_MAX || !aLength) {
     250           0 :         return false;
     251             :       }
     252             :       CheckedInt<uint32_t> endOffset =
     253           0 :         CheckedInt<uint32_t>(aOffset) + aLength;
     254           0 :       if (NS_WARN_IF(!endOffset.isValid())) {
     255           0 :         return false;
     256             :       }
     257           0 :       return aOffset < EndOffset() && endOffset.value() > mStart;
     258             :     }
     259             :     LayoutDeviceIntRect GetRect(uint32_t aOffset) const;
     260             :     LayoutDeviceIntRect GetUnionRect(uint32_t aOffset, uint32_t aLength) const;
     261             :     LayoutDeviceIntRect GetUnionRectAsFarAsPossible(
     262             :                           uint32_t aOffset, uint32_t aLength,
     263             :                           bool aRoundToExistingOffset) const;
     264             :   } mTextRectArray;
     265             : 
     266             :   LayoutDeviceIntRect mEditorRect;
     267             : 
     268             :   friend class ContentCacheInParent;
     269             :   friend struct IPC::ParamTraits<ContentCache>;
     270             : };
     271             : 
     272           0 : class ContentCacheInChild final : public ContentCache
     273             : {
     274             : public:
     275             :   ContentCacheInChild();
     276             : 
     277             :   /**
     278             :    * When IME loses focus, this should be called and making this forget the
     279             :    * content for reducing footprint.
     280             :    */
     281             :   void Clear();
     282             : 
     283             :   /**
     284             :    * Cache*() retrieves the latest content information and store them.
     285             :    * Be aware, CacheSelection() calls CacheTextRects(), and also CacheText()
     286             :    * calls CacheSelection().  So, related data is also retrieved automatically.
     287             :    */
     288             :   bool CacheEditorRect(nsIWidget* aWidget,
     289             :                        const IMENotification* aNotification = nullptr);
     290             :   bool CacheSelection(nsIWidget* aWidget,
     291             :                       const IMENotification* aNotification = nullptr);
     292             :   bool CacheText(nsIWidget* aWidget,
     293             :                  const IMENotification* aNotification = nullptr);
     294             : 
     295             :   bool CacheAll(nsIWidget* aWidget,
     296             :                 const IMENotification* aNotification = nullptr);
     297             : 
     298             :   /**
     299             :    * SetSelection() modifies selection with specified raw data. And also this
     300             :    * tries to retrieve text rects too.
     301             :    */
     302             :   void SetSelection(nsIWidget* aWidget,
     303             :                     uint32_t aStartOffset,
     304             :                     uint32_t aLength,
     305             :                     bool aReversed,
     306             :                     const WritingMode& aWritingMode);
     307             : 
     308             : private:
     309             :   bool QueryCharRect(nsIWidget* aWidget,
     310             :                      uint32_t aOffset,
     311             :                      LayoutDeviceIntRect& aCharRect) const;
     312             :   bool QueryCharRectArray(nsIWidget* aWidget,
     313             :                           uint32_t aOffset,
     314             :                           uint32_t aLength,
     315             :                           RectArray& aCharRectArray) const;
     316             :   bool CacheCaret(nsIWidget* aWidget,
     317             :                   const IMENotification* aNotification = nullptr);
     318             :   bool CacheTextRects(nsIWidget* aWidget,
     319             :                       const IMENotification* aNotification = nullptr);
     320             : };
     321             : 
     322           0 : class ContentCacheInParent final : public ContentCache
     323             : {
     324             : public:
     325             :   explicit ContentCacheInParent(dom::TabParent& aTabParent);
     326             : 
     327             :   /**
     328             :    * AssignContent() is called when TabParent receives ContentCache from
     329             :    * the content process.  This doesn't copy composition information because
     330             :    * it's managed by TabParent itself.
     331             :    */
     332             :   void AssignContent(const ContentCache& aOther,
     333             :                      nsIWidget* aWidget,
     334             :                      const IMENotification* aNotification = nullptr);
     335             : 
     336             :   /**
     337             :    * HandleQueryContentEvent() sets content data to aEvent.mReply.
     338             :    *
     339             :    * For eQuerySelectedText, fail if the cache doesn't contain the whole
     340             :    *  selected range. (This shouldn't happen because PuppetWidget should have
     341             :    *  already sent the whole selection.)
     342             :    *
     343             :    * For eQueryTextContent, fail only if the cache doesn't overlap with
     344             :    *  the queried range. Note the difference from above. We use
     345             :    *  this behavior because a normal eQueryTextContent event is allowed to
     346             :    *  have out-of-bounds offsets, so that widget can request content without
     347             :    *  knowing the exact length of text. It's up to widget to handle cases when
     348             :    *  the returned offset/length are different from the queried offset/length.
     349             :    *
     350             :    * For eQueryTextRect, fail if cached offset/length aren't equals to input.
     351             :    *   Cocoa widget always queries selected offset, so it works on it.
     352             :    *
     353             :    * For eQueryCaretRect, fail if cached offset isn't equals to input
     354             :    *
     355             :    * For eQueryEditorRect, always success
     356             :    */
     357             :   bool HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
     358             :                                nsIWidget* aWidget) const;
     359             : 
     360             :   /**
     361             :    * OnCompositionEvent() should be called before sending composition string.
     362             :    * This returns true if the event should be sent.  Otherwise, false.
     363             :    */
     364             :   bool OnCompositionEvent(const WidgetCompositionEvent& aCompositionEvent);
     365             : 
     366             :   /**
     367             :    * OnSelectionEvent() should be called before sending selection event.
     368             :    */
     369             :   void OnSelectionEvent(const WidgetSelectionEvent& aSelectionEvent);
     370             : 
     371             :   /**
     372             :    * OnEventNeedingAckHandled() should be called after the child process
     373             :    * handles a sent event which needs acknowledging.
     374             :    *
     375             :    * WARNING: This may send notifications to IME.  That might cause destroying
     376             :    *          TabParent or aWidget.  Therefore, the caller must not destroy
     377             :    *          this instance during a call of this method.
     378             :    */
     379             :   void OnEventNeedingAckHandled(nsIWidget* aWidget, EventMessage aMessage);
     380             : 
     381             :   /**
     382             :    * RequestIMEToCommitComposition() requests aWidget to commit or cancel
     383             :    * composition.  If it's handled synchronously, this returns true.
     384             :    *
     385             :    * @param aWidget     The widget to be requested to commit or cancel
     386             :    *                    the composition.
     387             :    * @param aCancel     When the caller tries to cancel the composition, true.
     388             :    *                    Otherwise, i.e., tries to commit the composition, false.
     389             :    * @param aCommittedString    The committed string (i.e., the last data of
     390             :    *                            dispatched composition events during requesting
     391             :    *                            IME to commit composition.
     392             :    * @return            Whether the composition is actually committed
     393             :    *                    synchronously.
     394             :    */
     395             :   bool RequestIMEToCommitComposition(nsIWidget* aWidget,
     396             :                                      bool aCancel,
     397             :                                      nsAString& aCommittedString);
     398             : 
     399             :   /**
     400             :    * MaybeNotifyIME() may notify IME of the notification.  If child process
     401             :    * hasn't been handled all sending events yet, this stores the notification
     402             :    * and flush it later.
     403             :    */
     404             :   void MaybeNotifyIME(nsIWidget* aWidget,
     405             :                       const IMENotification& aNotification);
     406             : 
     407             : private:
     408             :   IMENotification mPendingSelectionChange;
     409             :   IMENotification mPendingTextChange;
     410             :   IMENotification mPendingLayoutChange;
     411             :   IMENotification mPendingCompositionUpdate;
     412             : 
     413             :   // mTabParent is owner of the instance.
     414             :   dom::TabParent& MOZ_NON_OWNING_REF mTabParent;
     415             :   // mCompositionString is composition string which were sent to the remote
     416             :   // process but not yet committed in the remote process.
     417             :   nsString mCompositionString;
     418             :   // This is not nullptr only while the instance is requesting IME to
     419             :   // composition.  Then, data value of dispatched composition events should
     420             :   // be stored into the instance.
     421             :   nsAString* mCommitStringByRequest;
     422             :   // mPendingEventsNeedingAck is increased before sending a composition event or
     423             :   // a selection event and decreased after they are received in the child
     424             :   // process.
     425             :   uint32_t mPendingEventsNeedingAck;
     426             :   // mCompositionStartInChild stores current composition start offset in the
     427             :   // remote process.
     428             :   uint32_t mCompositionStartInChild;
     429             :   // mPendingCommitLength is commit string length of the first pending
     430             :   // composition.  This is used by relative offset query events when querying
     431             :   // new composition start offset.
     432             :   // Note that when mPendingCompositionCount is not 0, i.e., there are 2 or
     433             :   // more pending compositions, this cache won't be used because in such case,
     434             :   // anyway ContentCacheInParent cannot return proper character rect.
     435             :   uint32_t mPendingCommitLength;
     436             :   // mPendingCompositionCount is number of compositions which started in widget
     437             :   // but not yet handled in the child process.
     438             :   uint8_t mPendingCompositionCount;
     439             :   // mWidgetHasComposition is true when the widget in this process thinks that
     440             :   // IME has composition.  So, this is set to true when eCompositionStart is
     441             :   // dispatched and set to false when eCompositionCommit(AsIs) is dispatched.
     442             :   bool mWidgetHasComposition;
     443             :   // mIsPendingLastCommitEvent is true only when this sends
     444             :   // eCompositionCommit(AsIs) event to the remote process but it's not handled
     445             :   // in the remote process yet.
     446             :   bool mIsPendingLastCommitEvent;
     447             : 
     448             :   ContentCacheInParent() = delete;
     449             : 
     450             :   /**
     451             :    * When following methods' aRoundToExistingOffset is true, even if specified
     452             :    * offset or range is out of bounds, the result is computed with the existing
     453             :    * cache forcibly.
     454             :    */
     455             :   bool GetCaretRect(uint32_t aOffset,
     456             :                     bool aRoundToExistingOffset,
     457             :                     LayoutDeviceIntRect& aCaretRect) const;
     458             :   bool GetTextRect(uint32_t aOffset,
     459             :                    bool aRoundToExistingOffset,
     460             :                    LayoutDeviceIntRect& aTextRect) const;
     461             :   bool GetUnionTextRects(uint32_t aOffset,
     462             :                          uint32_t aLength,
     463             :                          bool aRoundToExistingOffset,
     464             :                          LayoutDeviceIntRect& aUnionTextRect) const;
     465             : 
     466             :   void FlushPendingNotifications(nsIWidget* aWidget);
     467             : };
     468             : 
     469             : } // namespace mozilla
     470             : 
     471             : #endif // mozilla_ContentCache_h

Generated by: LCOV version 1.13