/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: sw=2 ts=8 et :
*/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
namespace dom { class BrowserParent;
} // namespace dom
/** * ContentCache stores various information of the child content. * This class has members which are necessary both in parent process and * content process.
*/
class ContentCache { public: using RectArray = CopyableTArray<LayoutDeviceIntRect>; using IMENotification = widget::IMENotification;
ContentCache() = default;
[[nodiscard]] bool IsValid() const;
protected: void AssertIfInvalid() const;
// Whole text in the target
Maybe<nsString> mText;
// Start offset of the composition string.
Maybe<uint32_t> mCompositionStart;
enum { ePrevCharRect = 1, eNextCharRect = 0 };
struct Selection final { // Following values are offset in "flat text".
uint32_t mAnchor;
uint32_t mFocus;
WritingMode mWritingMode;
bool mHasRange;
// Character rects at previous and next character of mAnchor and mFocus. // The reason why ContentCache needs to store each previous character of // them is IME may query character rect of the last character of a line // when caret is at the end of the line. // Note that use ePrevCharRect and eNextCharRect for accessing each item.
LayoutDeviceIntRect mAnchorCharRects[2];
LayoutDeviceIntRect mFocusCharRects[2];
// Whole rect of selected text. This is empty if the selection is collapsed.
LayoutDeviceIntRect mRect;
// Stores first char rect because Yosemite's Japanese IME sometimes tries // to query it. If there is no text, this is caret rect.
LayoutDeviceIntRect mFirstCharRect;
struct Caret final {
uint32_t mOffset = 0u;
LayoutDeviceIntRect mRect;
class ContentCacheInChild final : public ContentCache { public:
ContentCacheInChild() = default;
/** * Called when composition event will be dispatched in this process from * PuppetWidget.
*/ void OnCompositionEvent(const WidgetCompositionEvent& aCompositionEvent);
/** * When IME loses focus, this should be called and making this forget the * content for reducing footprint.
*/ void Clear();
/** * Cache*() retrieves the latest content information and store them. * Be aware, CacheSelection() calls CacheCaretAndTextRects(), * CacheCaretAndTextRects() calls CacheCaret() and CacheTextRects(), and * CacheText() calls CacheSelection(). So, related data is also retrieved * automatically.
*/ bool CacheEditorRect(nsIWidget* aWidget, const IMENotification* aNotification = nullptr); bool CacheCaretAndTextRects(nsIWidget* aWidget, const IMENotification* aNotification = nullptr); bool CacheText(nsIWidget* aWidget, const IMENotification* aNotification = nullptr);
/** * SetSelection() modifies selection with specified raw data. And also this * tries to retrieve text rects too. * * @return true if the selection is cached. Otherwise, false.
*/
[[nodiscard]] bool SetSelection(
nsIWidget* aWidget, const IMENotification::SelectionChangeDataBase& aSelectionChangeData);
// Once composition is committed, all of the commit string may be composed // again by Kakutei-Undo of Japanese IME. Therefore, we need to keep // storing the last composition start to cache all character rects of the // last commit string.
Maybe<OffsetAndData<uint32_t>> mLastCommit;
};
class ContentCacheInParent final : public ContentCache { public:
ContentCacheInParent() = delete; explicit ContentCacheInParent(dom::BrowserParent& aBrowserParent);
/** * AssignContent() is called when BrowserParent receives ContentCache from * the content process. This doesn't copy composition information because * it's managed by BrowserParent itself.
*/ void AssignContent(const ContentCache& aOther, nsIWidget* aWidget, const IMENotification* aNotification = nullptr);
/** * HandleQueryContentEvent() sets content data to aEvent.mReply. * * For eQuerySelectedText, fail if the cache doesn't contain the whole * selected range. (This shouldn't happen because PuppetWidget should have * already sent the whole selection.) * * For eQueryTextContent, fail only if the cache doesn't overlap with * the queried range. Note the difference from above. We use * this behavior because a normal eQueryTextContent event is allowed to * have out-of-bounds offsets, so that widget can request content without * knowing the exact length of text. It's up to widget to handle cases when * the returned offset/length are different from the queried offset/length. * * For eQueryTextRect, fail if cached offset/length aren't equals to input. * Cocoa widget always queries selected offset, so it works on it. * * For eQueryCaretRect, fail if cached offset isn't equals to input * * For eQueryEditorRect, always success
*/ bool HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
nsIWidget* aWidget) const;
/** * OnCompositionEvent() should be called before sending composition string. * This returns true if the event should be sent. Otherwise, false.
*/ bool OnCompositionEvent(const WidgetCompositionEvent& aCompositionEvent);
/** * OnSelectionEvent() should be called before sending selection event.
*/ void OnSelectionEvent(const WidgetSelectionEvent& aSelectionEvent);
/** * OnContentCommandEvent() should called before sending a content command * event.
*/ void OnContentCommandEvent( const WidgetContentCommandEvent& aContentCommandEvent);
/** * OnEventNeedingAckHandled() should be called after the child process * handles a sent event which needs acknowledging. * * WARNING: This may send notifications to IME. That might cause destroying * BrowserParent or aWidget. Therefore, the caller must not destroy * this instance during a call of this method.
*/ void OnEventNeedingAckHandled(nsIWidget* aWidget, EventMessage aMessage,
uint32_t aCompositionId);
/** * RequestIMEToCommitComposition() requests aWidget to commit or cancel * composition. If it's handled synchronously, this returns true. * * @param aWidget The widget to be requested to commit or cancel * the composition. * @param aCancel When the caller tries to cancel the composition, true. * Otherwise, i.e., tries to commit the composition, false. * @param aCompositionId * The composition ID which should be committed or * canceled. * @param aCommittedString The committed string (i.e., the last data of * dispatched composition events during requesting * IME to commit composition. * @return Whether the composition is actually committed * synchronously.
*/ bool RequestIMEToCommitComposition(nsIWidget* aWidget, bool aCancel,
uint32_t aCompositionId,
nsAString& aCommittedString);
/** * MaybeNotifyIME() may notify IME of the notification. If child process * hasn't been handled all sending events yet, this stores the notification * and flush it later.
*/ void MaybeNotifyIME(nsIWidget* aWidget, const IMENotification& aNotification);
private: struct HandlingCompositionData;
// Return true when the widget in this process thinks that IME has // composition. So, this returns true when there is at least one handling // composition data and the last handling composition has not dispatched // composition commit event to the remote process yet.
[[nodiscard]] bool WidgetHasComposition() const { return !mHandlingCompositions.IsEmpty() &&
!mHandlingCompositions.LastElement().mSentCommitEvent;
}
// Return true if there is a pending composition which has already sent // a commit event to the remote process, but not yet handled by it.
[[nodiscard]] bool HasPendingCommit() const { for (const HandlingCompositionData& data : mHandlingCompositions) { if (data.mSentCommitEvent) { returntrue;
}
} returnfalse;
}
// Return the number of composition events and set selection events which were // sent to the remote process, but we've not verified that the remote process // finished handling it.
[[nodiscard]] uint32_t PendingEventsNeedingAck() const {
uint32_t ret = mPendingSetSelectionEventNeedingAck +
mPendingContentCommandEventNeedingAck; for (const HandlingCompositionData& data : mHandlingCompositions) {
ret += data.mPendingEventsNeedingAck;
} return ret;
}
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED // Log of event messages to be output to crash report.
nsTArray<EventMessage> mDispatchedEventMessages;
nsTArray<EventMessage> mReceivedEventMessages; // Log of RequestIMEToCommitComposition() in the last 2 compositions. enumclass RequestIMEToCommitCompositionResult : uint8_t {
eToOldCompositionReceived,
eToUnknownCompositionReceived,
eToCommittedCompositionReceived,
eReceivedAfterBrowserParentBlur,
eReceivedButNoTextComposition,
eReceivedButForDifferentTextComposition,
eHandledAsynchronously,
eHandledSynchronously,
}; constchar* ToReadableText(
RequestIMEToCommitCompositionResult aResult) const { switch (aResult) { case RequestIMEToCommitCompositionResult::eToOldCompositionReceived: return"Commit request is not handled because it's for " "older composition"; case RequestIMEToCommitCompositionResult::eToUnknownCompositionReceived: return"Commit request is not handled because it's for " "unknown composition"; case RequestIMEToCommitCompositionResult::eToCommittedCompositionReceived: return"Commit request is not handled because BrowserParent has " "already " "sent commit event for the composition"; case RequestIMEToCommitCompositionResult::eReceivedAfterBrowserParentBlur: return"Commit request is handled with stored composition string " "because BrowserParent has already lost focus"; case RequestIMEToCommitCompositionResult::eReceivedButNoTextComposition: return"Commit request is not handled because there is no " "TextComposition instance"; case RequestIMEToCommitCompositionResult::
eReceivedButForDifferentTextComposition: return"Commit request is handled with stored composition string " "because new TextComposition is active"; case RequestIMEToCommitCompositionResult::eHandledAsynchronously: return"Commit request is handled but IME doesn't commit current " "composition synchronously"; case RequestIMEToCommitCompositionResult::eHandledSynchronously: return"Commit request is handled synchronously"; default: return"Unknown reason";
}
}
nsTArray<RequestIMEToCommitCompositionResult>
mRequestIMEToCommitCompositionResults; #endif// MOZ_DIAGNOSTIC_ASSERT_ENABLED
// Stores pending compositions (meaning eCompositionStart was dispatched, but // eCompositionCommit(AsIs) has not been handled by the remote process yet). struct HandlingCompositionData { // The lasted composition string which was sent to the remote process.
nsString mCompositionString; // The composition ID of a handling composition with the instance.
uint32_t mCompositionId; // Increased when sending composition events and decreased when the // remote process finished handling the events.
uint32_t mPendingEventsNeedingAck = 0u; // true if eCompositionCommit(AsIs) has already been sent to the remote // process. bool mSentCommitEvent = false;
// mBrowserParent is owner of the instance.
dom::BrowserParent& MOZ_NON_OWNING_REF mBrowserParent; // This is not nullptr only while the instance is requesting IME to // composition. Then, data value of dispatched composition events should // be stored into the instance.
nsAString* mCommitStringByRequest; // mCompositionStartInChild stores current composition start offset in the // remote process.
Maybe<uint32_t> mCompositionStartInChild; // Increased when sending eSetSelection events and decreased when the remote // process finished handling the events. Note that eSetSelection may be // dispatched without composition. Therefore, we need to count it with this.
uint32_t mPendingSetSelectionEventNeedingAck = 0u; // Increased when sending WidgetContentCommandEvent events and decreased when // the remote process finished handling them.
uint32_t mPendingContentCommandEventNeedingAck = 0u; // mPendingCommitLength is commit string length of the first pending // composition. This is used by relative offset query events when querying // new composition start offset. // Note that when mHandlingCompositions has 2 or more elements, i.e., there // are 2 or more pending compositions, this cache won't be used because in // such case, anyway ContentCacheInParent cannot return proper character rect.
uint32_t mPendingCommitLength; // mIsChildIgnoringCompositionEvents is set to true if the child process // requests commit composition whose commit has already been sent to it. // Then, set to false when the child process ignores the commit event. bool mIsChildIgnoringCompositionEvents;
/** * When following methods' aRoundToExistingOffset is true, even if specified * offset or range is out of bounds, the result is computed with the existing * cache forcibly.
*/ bool GetCaretRect(uint32_t aOffset, bool aRoundToExistingOffset,
LayoutDeviceIntRect& aCaretRect) const; bool GetTextRect(uint32_t aOffset, bool aRoundToExistingOffset,
LayoutDeviceIntRect& aTextRect) const; bool GetUnionTextRects(uint32_t aOffset, uint32_t aLength, bool aRoundToExistingOffset,
LayoutDeviceIntRect& aUnionTextRect) const;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.