/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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/. */
/** * TextEventDispatcher is a helper class for dispatching widget events defined * in TextEvents.h. Currently, this is a helper for dispatching * WidgetCompositionEvent and WidgetKeyboardEvent. This manages the behavior * of them for conforming to DOM Level 3 Events. * An instance of this class is created by nsIWidget instance and owned by it. * This is typically created only by the top level widgets because only they * handle IME.
*/
class TextEventDispatcher final {
~TextEventDispatcher() = default;
/** * Initializes the instance for IME or automated test. Either IME or tests * need to call one of them before starting composition. If they return * NS_ERROR_ALREADY_INITIALIZED, it means that the listener already listens * notifications from TextEventDispatcher for same purpose (for IME or tests). * If this returns another error, the caller shouldn't keep starting * composition. * * @param aListener Specify the listener to listen notifications and * requests. This must not be null. * NOTE: aListener is stored as weak reference in * TextEventDispatcher. See mListener * definition below.
*/
nsresult BeginInputTransaction(TextEventDispatcherListener* aListener);
nsresult BeginTestInputTransaction(TextEventDispatcherListener* aListener, bool aIsAPZAware);
nsresult BeginNativeInputTransaction();
/** * BeginInputTransactionFor() should be used when aPuppetWidget dispatches * a composition or keyboard event coming from its parent process.
*/
nsresult BeginInputTransactionFor(const WidgetGUIEvent* aEvent,
PuppetWidget* aPuppetWidget);
/** * EndInputTransaction() should be called when the listener stops using * the TextEventDispatcher. * * @param aListener The listener using the TextEventDispatcher instance.
*/ void EndInputTransaction(TextEventDispatcherListener* aListener);
/** * OnDestroyWidget() is called when mWidget is being destroyed.
*/ void OnDestroyWidget();
nsIWidget* GetWidget() const { return mWidget; }
/** * Return true starting from ending handling focus notification and until * receiving blur notification.
*/ bool HasFocus() const { return mHasFocus; }
/** * OnWidgetChangeIMENotificationRequests() is called when aWidget's * IMENotificationRequest is maybe modified by unusual path. E.g., * modified in an async path.
*/ void OnWidgetChangeIMENotificationRequests(nsIWidget* aWidget) {
MOZ_ASSERT(aWidget); if (mWidget == aWidget) {
UpdateNotificationRequests();
}
}
/** * GetState() returns current state of this class. * * @return NS_OK: Fine to compose text. * NS_ERROR_NOT_INITIALIZED: BeginInputTransaction() or * BeginInputTransactionForTests() * should be called. * NS_ERROR_NOT_AVAILABLE: The widget isn't available for * composition.
*/
nsresult GetState() const;
/** * IsComposing() returns true after calling StartComposition() and before * calling CommitComposition(). In other words, native IME has composition * when this returns true.
*/ bool IsComposing() const { return mIsComposing; }
/** * IsHandlingComposition() returns true after calling StartComposition() and * content has not handled eCompositionCommit(AsIs) event. In other words, * our content has composition when this returns true.
*/ bool IsHandlingComposition() const { return mIsHandlingComposition; }
/** * IsInNativeInputTransaction() returns true if native IME handler began a * transaction and it's not finished yet.
*/ bool IsInNativeInputTransaction() const { return mInputTransactionType == eNativeInputTransaction;
}
/** * IsDispatchingEvent() returns true while this instance dispatching an event.
*/ bool IsDispatchingEvent() const { return mDispatchingEvent > 0; }
/** * GetPseudoIMEContext() returns pseudo native IME context if there is an * input transaction whose type is not for native event handler. * Otherwise, returns nullptr.
*/ void* GetPseudoIMEContext() const { if (mInputTransactionType == eNoInputTransaction ||
mInputTransactionType == eNativeInputTransaction) { return nullptr;
} returnconst_cast<TextEventDispatcher*>(this);
}
/** * Return writing mode at selection while this has focus. Otherwise, or * never exists selection ranges, this returns Nothing.
*/ const Maybe<WritingMode>& MaybeWritingModeRefAtSelection() const { return mWritingMode;
}
/** * MaybeQueryWritingModeAtSelection() returns writing mode at current * selection even if this does not have focus. If this is not focused, this * queries selection. Then, chrome script can run due to flushing the layout * if an element in chrome has focus (but it should not cause any problem * hopefully).
*/
MOZ_CAN_RUN_SCRIPT Maybe<WritingMode> MaybeQueryWritingModeAtSelection() const;
/** * StartComposition() starts composition explicitly. * * @param aEventTime If this is not nullptr, WidgetCompositionEvent will * be initialized with this. Otherwise, initialized * with the time at initializing.
*/
nsresult StartComposition(nsEventStatus& aStatus, const WidgetEventTime* aEventTime = nullptr);
/** * CommitComposition() commits composition. * * @param aCommitString If this is null, commits with the last composition * string. Otherwise, commits the composition with * this value. * @param aEventTime If this is not nullptr, WidgetCompositionEvent will * be initialized with this. Otherwise, initialized * with the time at initializing.
*/
nsresult CommitComposition(nsEventStatus& aStatus, const nsAString* aCommitString = nullptr, const WidgetEventTime* aEventTime = nullptr);
/** * SetPendingCompositionString() sets new composition string which will be * dispatched with eCompositionChange event by calling Flush(). * * @param aString New composition string.
*/
nsresult SetPendingCompositionString(const nsAString& aString) { return mPendingComposition.SetString(aString);
}
/** * AppendClauseToPendingComposition() appends a clause information to * the pending composition string. * * @param aLength Length of the clause. * @param aTextRangeType One of TextRangeType::eRawClause, * TextRangeType::eSelectedRawClause, * TextRangeType::eConvertedClause or * TextRangeType::eSelectedClause.
*/
nsresult AppendClauseToPendingComposition(uint32_t aLength,
TextRangeType aTextRangeType) { return mPendingComposition.AppendClause(aLength, aTextRangeType);
}
/** * SetCaretInPendingComposition() sets caret position in the pending * composition string and its length. This is optional. If IME doesn't * want to show caret, it shouldn't need to call this. * * @param aOffset Offset of the caret in the pending composition * string. This should not be larger than the length * of the pending composition string. * @param aLength Caret width. If this is 0, caret will be collapsed. * Note that Gecko doesn't supported wide caret yet, * therefore, this is ignored for now.
*/
nsresult SetCaretInPendingComposition(uint32_t aOffset, uint32_t aLength) { return mPendingComposition.SetCaret(aOffset, aLength);
}
/** * SetPendingComposition() is useful if native IME handler already creates * array of clauses and/or caret information. * * @param aString Composition string. This may include native line * breakers since they will be replaced with XP line * breakers automatically. * @param aRanges This should include the ranges of clauses and/or * a range of caret. Note that this method allows * some ranges overlap each other and the range order * is not from start to end.
*/
nsresult SetPendingComposition(const nsAString& aString, const TextRangeArray* aRanges) { return mPendingComposition.Set(aString, aRanges);
}
/** * FlushPendingComposition() sends the pending composition string * to the widget of the store DOM window. Before calling this, IME needs to * set pending composition string with SetPendingCompositionString(), * AppendClauseToPendingComposition() and/or * SetCaretInPendingComposition(). * * @param aEventTime If this is not nullptr, WidgetCompositionEvent will * be initialized with this. Otherwise, initialized * with the time at initializing.
*/
nsresult FlushPendingComposition(
nsEventStatus& aStatus, const WidgetEventTime* aEventTime = nullptr) { return mPendingComposition.Flush(this, aStatus, aEventTime);
}
/** * ClearPendingComposition() makes this instance forget pending composition.
*/ void ClearPendingComposition() { mPendingComposition.Clear(); }
/** * GetPendingCompositionClauses() returns text ranges which was appended by * AppendClauseToPendingComposition() or SetPendingComposition().
*/ const TextRangeArray* GetPendingCompositionClauses() const { return mPendingComposition.GetClauses();
}
/** * @see nsIWidget::NotifyIME()
*/ // Calling NotifyIME may call OS's API so that everything could happen. // We should mark it MOZ_CAN_RUN_SCRIPT later.
MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
NotifyIME(const IMENotification& aIMENotification);
/** * DispatchKeyboardEvent() maybe dispatches aKeyboardEvent. * * @param aMessage Must be eKeyDown or eKeyUp. * Use MaybeDispatchKeypressEvents() for dispatching * eKeyPress. * @param aKeyboardEvent A keyboard event. * @param aStatus If dispatching event should be marked as consumed, * set nsEventStatus_eConsumeNoDefault. Otherwise, * set nsEventStatus_eIgnore. After dispatching * a event and it's consumed this returns * nsEventStatus_eConsumeNoDefault. * @param aData Calling this method may cause calling * WillDispatchKeyboardEvent() of the listener. * aData will be set to its argument. * @return true if an event is dispatched. Otherwise, false.
*/ bool DispatchKeyboardEvent(EventMessage aMessage, const WidgetKeyboardEvent& aKeyboardEvent,
nsEventStatus& aStatus, void* aData = nullptr);
/** * MaybeDispatchKeypressEvents() maybe dispatches a keypress event which is * generated from aKeydownEvent. * * @param aKeyboardEvent A keyboard event. * @param aStatus Sets the result when the caller dispatches * aKeyboardEvent. Note that if the value is * nsEventStatus_eConsumeNoDefault, this does NOT * dispatch keypress events. * When this method dispatches one or more keypress * events and one of them is consumed, this returns * nsEventStatus_eConsumeNoDefault. * @param aData Calling this method may cause calling * WillDispatchKeyboardEvent() of the listener. * aData will be set to its argument. * @param aNeedsCallback Set true when caller needs to initialize each * eKeyPress event immediately before dispatch. * Then, WillDispatchKeyboardEvent() is always called. * @return true if one or more events are dispatched. * Otherwise, false.
*/ bool MaybeDispatchKeypressEvents(const WidgetKeyboardEvent& aKeyboardEvent,
nsEventStatus& aStatus, void* aData = nullptr, bool aNeedsCallback = false);
private: // mWidget is owner of the instance. When this is created, this is set. // And when mWidget is released, this is cleared by OnDestroyWidget(). // Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may // return true).
nsIWidget* mWidget; // mListener is a weak reference to TextEventDispatcherListener. That might // be referred by JS. Therefore, the listener might be difficult to release // itself if this is a strong reference. Additionally, it's difficult to // check if a method to uninstall the listener is called by valid instance. // So, using weak reference is the best way in this case.
nsWeakPtr mListener; // mIMENotificationRequests should store current IME's notification requests. // So, this may be invalid when IME doesn't have focus.
IMENotificationRequests mIMENotificationRequests; // mWritingMode stores writing mode at current selection starting from // receiving focus notification and until receiving blur notification. When // selection is changed, this is updated by every selection change // notification.
Maybe<WritingMode> mWritingMode;
// mPendingComposition stores new composition string temporarily. // These values will be used for dispatching eCompositionChange event // in Flush(). When Flush() is called, the members will be cleared // automatically. class PendingComposition { public:
PendingComposition();
nsresult SetString(const nsAString& aString);
nsresult AppendClause(uint32_t aLength, TextRangeType aTextRangeType);
nsresult SetCaret(uint32_t aOffset, uint32_t aLength);
nsresult Set(const nsAString& aString, const TextRangeArray* aRanges);
nsresult Flush(TextEventDispatcher* aDispatcher, nsEventStatus& aStatus, const WidgetEventTime* aEventTime); const TextRangeArray* GetClauses() const { return mClauses; } void Clear();
/** * ReplaceNativeLineBreakers() replaces "\r\n" and "\r" to "\n" and adjust * each clause information and the caret information.
*/ void ReplaceNativeLineBreakers();
/** * AdjustRange() adjusts aRange as in the string with XP line breakers. * * @param aRange The reference to a range in aNativeString. * This will be modified. * @param aNativeString The string with native line breakers. * This may include "\r\n" and/or "\r".
*/ staticvoid AdjustRange(TextRange& aRange, const nsAString& aNativeString);
};
PendingComposition mPendingComposition;
// While dispatching an event, this is incremented.
uint16_t mDispatchingEvent;
enum InputTransactionType : uint8_t { // No input transaction has been started.
eNoInputTransaction, // Input transaction for native IME or keyboard event handler. Note that // keyboard events may be dispatched via parent process if there is. // In remote processes, this is also used when events come from the parent // process and are not for tests because we cannot distinguish if // TextEventDispatcher has which type of transaction when it dispatches // (eNativeInputTransaction or eSameProcessSyncInputTransaction).
eNativeInputTransaction, // Input transaction for automated tests which are APZ-aware. Note that // keyboard events may be dispatched via parent process if there is.
eAsyncTestInputTransaction, // Input transaction for automated tests which assume events are fired // synchronously. I.e., keyboard events are always dispatched in the // current process. // In remote processes, this is also used when events come from the parent // process and are not dispatched by the instance itself for APZ-aware // tests because this instance won't dispatch the events via the parent // process again.
eSameProcessSyncTestInputTransaction, // Input transaction for others (currently, only FuzzingFunctions). // Events are fired synchronously in the process. // XXX Should we make this async for testing default action handlers in // the main process?
eSameProcessSyncInputTransaction
};
// ShouldSendInputEventToAPZ() returns true when WidgetInputEvent should // be dispatched via its parent process (if there is) for APZ. Otherwise, // when the input transaction is for IME of B2G or automated tests which // isn't APZ-aware, WidgetInputEvent should be dispatched form current // process directly. bool ShouldSendInputEventToAPZ() const { switch (mInputTransactionType) { case eNativeInputTransaction: case eAsyncTestInputTransaction: returntrue; case eSameProcessSyncTestInputTransaction: case eSameProcessSyncInputTransaction: returnfalse; case eNoInputTransaction:
NS_WARNING( "Why does the caller need to dispatch an event when " "there is no input transaction?"); returntrue; default:
MOZ_CRASH("Define the behavior of new InputTransactionType");
}
}
// See IsComposing(). bool mIsComposing;
// See IsHandlingComposition(). bool mIsHandlingComposition;
// true while NOTIFY_IME_OF_FOCUS is received but NOTIFY_IME_OF_BLUR has not // received yet. Otherwise, false. bool mHasFocus;
/** * StartCompositionAutomaticallyIfNecessary() starts composition if it hasn't * been started it yet. * * @param aStatus If it succeeded to start composition normally, this * returns nsEventStatus_eIgnore. Otherwise, e.g., * the composition is canceled during dispatching * compositionstart event, this returns * nsEventStatus_eConsumeNoDefault. In this case, * the caller shouldn't keep doing its job. * @param aEventTime If this is not nullptr, WidgetCompositionEvent will * be initialized with this. Otherwise, initialized * with the time at initializing. * @return Only when something unexpected occurs, this returns * an error. Otherwise, returns NS_OK even if aStatus * is nsEventStatus_eConsumeNoDefault.
*/
nsresult StartCompositionAutomaticallyIfNecessary(
nsEventStatus& aStatus, const WidgetEventTime* aEventTime);
/** * DispatchKeyboardEventInternal() maybe dispatches aKeyboardEvent. * * @param aMessage Must be eKeyDown, eKeyUp or eKeyPress. * @param aKeyboardEvent A keyboard event. If aMessage is eKeyPress and * the event is for second or later character, its * mKeyValue should be empty string. * @param aStatus If dispatching event should be marked as consumed, * set nsEventStatus_eConsumeNoDefault. Otherwise, * set nsEventStatus_eIgnore. After dispatching * a event and it's consumed this returns * nsEventStatus_eConsumeNoDefault. * @param aData Calling this method may cause calling * WillDispatchKeyboardEvent() of the listener. * aData will be set to its argument. * @param aIndexOfKeypress This must be 0 if aMessage isn't eKeyPress or * aKeyboard.mKeyNameIndex isn't * KEY_NAME_INDEX_USE_STRING. Otherwise, i.e., * when an eKeyPress event causes inputting * text, this must be between 0 and * mKeyValue.Length() - 1 since keypress events * sending only one character per event. * @param aNeedsCallback Set true when caller needs to initialize each * eKeyPress event immediately before dispatch. * Then, WillDispatchKeyboardEvent() is always called. * @return true if an event is dispatched. Otherwise, false.
*/ // TODO: Mark this as MOZ_CAN_RUN_SCRIPT instead.
MOZ_CAN_RUN_SCRIPT_BOUNDARY bool DispatchKeyboardEventInternal(
EventMessage aMessage, const WidgetKeyboardEvent& aKeyboardEvent,
nsEventStatus& aStatus, void* aData, uint32_t aIndexOfKeypress = 0, bool aNeedsCallback = false);
/** * UpdateNotificationRequests() updates mIMENotificationRequests with * current state. If the instance doesn't have focus, this clears * mIMENotificationRequests. Otherwise, updates it with both requests of * current listener and native listener.
*/ void UpdateNotificationRequests();
};
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.