Bug 1053048 Implement nsIEditor.isInEditAction readonly attribute r=ehsan, sr=smaug
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -659,18 +659,16 @@ public:
/** SetEditor gives an address to the editor that will be accessed
* @param aEditor the editor this listener calls for editing operations
*/
void SetFrame(nsTextControlFrame *aFrame){mFrame = aFrame;}
void SettingValue(bool aValue) { mSettingValue = aValue; }
void SetValueChanged(bool aSetValueChanged) { mSetValueChanged = aSetValueChanged; }
- bool IsInEditAction() const { return mInEditAction; }
-
NS_DECL_ISUPPORTS
NS_DECL_NSISELECTIONLISTENER
NS_DECL_NSIDOMEVENTLISTENER
NS_DECL_NSIEDITOROBSERVER
@@ -705,36 +703,31 @@ protected:
* refrain from calling OnValueChanged.
*/
bool mSettingValue;
/**
* Whether we are in the process of a SetValue call that doesn't want
* |SetValueChanged| to be called.
*/
bool mSetValueChanged;
- /**
- * mInEditAction is true while editor handling an edit action.
- */
- bool mInEditAction;
};
/*
* nsTextInputListener implementation
*/
nsTextInputListener::nsTextInputListener(nsITextControlElement* aTxtCtrlElement)
: mFrame(nullptr)
, mTxtCtrlElement(aTxtCtrlElement)
, mSelectionWasCollapsed(true)
, mHadUndoItems(false)
, mHadRedoItems(false)
, mSettingValue(false)
, mSetValueChanged(true)
-, mInEditAction(false)
{
}
nsTextInputListener::~nsTextInputListener()
{
}
NS_IMPL_ISUPPORTS(nsTextInputListener,
@@ -891,18 +884,16 @@ nsTextInputListener::HandleEvent(nsIDOME
return NS_OK;
}
// BEGIN nsIEditorObserver
NS_IMETHODIMP
nsTextInputListener::EditAction()
{
- mInEditAction = false;
-
nsWeakFrame weakFrame = mFrame;
nsITextControlFrame* frameBase = do_QueryFrame(mFrame);
nsTextControlFrame* frame = static_cast<nsTextControlFrame*> (frameBase);
NS_ASSERTION(frame, "Where is our frame?");
//
// Update the undo / redo menus
//
@@ -938,24 +929,22 @@ nsTextInputListener::EditAction()
}
return NS_OK;
}
NS_IMETHODIMP
nsTextInputListener::BeforeEditAction()
{
- mInEditAction = true;
return NS_OK;
}
NS_IMETHODIMP
nsTextInputListener::CancelEditAction()
{
- mInEditAction = false;
return NS_OK;
}
// END nsIEditorObserver
nsresult
nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate,
@@ -1524,20 +1513,21 @@ void
nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame)
{
NS_ENSURE_TRUE_VOID(mBoundFrame);
// If it was, however, it should be unbounded from the same frame.
NS_ASSERTION(!aFrame || aFrame == mBoundFrame, "Unbinding from the wrong frame");
NS_ENSURE_TRUE_VOID(!aFrame || aFrame == mBoundFrame);
- // If the editor is modified, we need to notify it here because editor may be
- // destroyed before EditAction() is called if selection listener causes
- // flushing layout.
- if (mTextListener && mTextListener->IsInEditAction()) {
+ // If the editor is modified but nsIEditorObserver::EditAction() hasn't been
+ // called yet, we need to notify it here because editor may be destroyed
+ // before EditAction() is called if selection listener causes flushing layout.
+ if (mTextListener && mEditor && mEditorInitialized &&
+ mEditor->GetIsInEditAction()) {
mTextListener->EditAction();
}
// We need to start storing the value outside of the editor if we're not
// going to use it anymore, so retrieve it for now.
nsAutoString value;
GetValue(value, true);
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -79,17 +79,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
IMEContentObserver::IMEContentObserver()
: mESM(nullptr)
, mPreCharacterDataChangeLength(-1)
- , mIsEditorInTransaction(false)
, mIsSelectionChangeEventPending(false)
, mSelectionChangeCausedOnlyByComposition(false)
, mIsPositionChangeEventPending(false)
, mIsFlushingPendingNotifications(false)
{
#ifdef DEBUG
TestMergingTextChangeData();
#endif
@@ -906,36 +905,33 @@ IMEContentObserver::AttributeChanged(nsI
TextChangeData data(start, start + mPreAttrChangeLength,
start + postAttrChangeLength, causedByComposition);
MaybeNotifyIMEOfTextChange(data);
}
NS_IMETHODIMP
IMEContentObserver::EditAction()
{
- mIsEditorInTransaction = false;
mEndOfAddedTextCache.Clear();
mStartOfRemovingTextRangeCache.Clear();
FlushMergeableNotifications();
return NS_OK;
}
NS_IMETHODIMP
IMEContentObserver::BeforeEditAction()
{
- mIsEditorInTransaction = true;
mEndOfAddedTextCache.Clear();
mStartOfRemovingTextRangeCache.Clear();
return NS_OK;
}
NS_IMETHODIMP
IMEContentObserver::CancelEditAction()
{
- mIsEditorInTransaction = false;
mEndOfAddedTextCache.Clear();
mStartOfRemovingTextRangeCache.Clear();
FlushMergeableNotifications();
return NS_OK;
}
void
IMEContentObserver::MaybeNotifyIMEOfTextChange(const TextChangeData& aData)
@@ -983,20 +979,24 @@ public:
private:
nsRefPtr<IMEContentObserver> mIMEContentObserver;
};
void
IMEContentObserver::FlushMergeableNotifications()
{
- // If we're in handling an edit action, this method will be called later.
// If this is already detached from the widget, this doesn't need to notify
// anything.
- if (mIsEditorInTransaction || !mWidget) {
+ if (!mWidget) {
+ return;
+ }
+
+ // If we're in handling an edit action, this method will be called later.
+ if (mEditor && mEditor->GetIsInEditAction()) {
return;
}
// Notifying something may cause nested call of this method. For example,
// when somebody notified one of the notifications may dispatch query content
// event. Then, it causes flushing layout which may cause another layout
// change notification.
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -212,17 +212,16 @@ private:
TextChangeData mTextChangeData;
EventStateManager* mESM;
nsIMEUpdatePreference mUpdatePreference;
uint32_t mPreAttrChangeLength;
int64_t mPreCharacterDataChangeLength;
- bool mIsEditorInTransaction;
bool mIsSelectionChangeEventPending;
bool mSelectionChangeCausedOnlyByComposition;
bool mIsPositionChangeEventPending;
bool mIsFlushingPendingNotifications;
};
} // namespace mozilla
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -142,16 +142,17 @@ nsEditor::nsEditor()
, mIMETextOffset(0)
, mDirection(eNone)
, mDocDirtyState(-1)
, mSpellcheckCheckboxState(eTriUnset)
, mShouldTxnSetSelection(true)
, mDidPreDestroy(false)
, mDidPostCreate(false)
, mDispatchInputEvent(true)
+, mIsInEditAction(false)
{
}
nsEditor::~nsEditor()
{
NS_ASSERTION(!mDocWeak || mDidPreDestroy, "Why PreDestroy hasn't been called?");
mTxnMgr = nullptr;
@@ -1827,32 +1828,35 @@ private:
bool mIsComposing;
};
void
nsEditor::NotifyEditorObservers(NotificationForEditorObservers aNotification)
{
switch (aNotification) {
case eNotifyEditorObserversOfEnd:
+ mIsInEditAction = false;
for (int32_t i = 0; i < mEditorObservers.Count(); i++) {
mEditorObservers[i]->EditAction();
}
if (!mDispatchInputEvent) {
return;
}
FireInputEvent();
break;
case eNotifyEditorObserversOfBefore:
+ mIsInEditAction = true;
for (int32_t i = 0; i < mEditorObservers.Count(); i++) {
mEditorObservers[i]->BeforeEditAction();
}
break;
case eNotifyEditorObserversOfCancel:
+ mIsInEditAction = false;
for (int32_t i = 0; i < mEditorObservers.Count(); i++) {
mEditorObservers[i]->CancelEditAction();
}
break;
default:
MOZ_CRASH("Handle all notifications here");
break;
}
@@ -5268,8 +5272,16 @@ nsEditor::GetSuppressDispatchingInputEve
}
NS_IMETHODIMP
nsEditor::SetSuppressDispatchingInputEvent(bool aSuppress)
{
mDispatchInputEvent = !aSuppress;
return NS_OK;
}
+
+NS_IMETHODIMP
+nsEditor::GetIsInEditAction(bool* aIsInEditAction)
+{
+ MOZ_ASSERT(aIsInEditAction, "aIsInEditAction must not be null");
+ *aIsInEditAction = mIsInEditAction;
+ return NS_OK;
+}
--- a/editor/libeditor/nsEditor.h
+++ b/editor/libeditor/nsEditor.h
@@ -890,16 +890,17 @@ protected:
EDirection mDirection; // the current direction of editor action
int8_t mDocDirtyState; // -1 = not initialized
uint8_t mSpellcheckCheckboxState; // a Tristate value
bool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns
bool mDidPreDestroy; // whether PreDestroy has been called
bool mDidPostCreate; // whether PostCreate has been called
bool mDispatchInputEvent;
+ bool mIsInEditAction; // true while the instance is handling an edit action
friend bool NSCanUnload(nsISupports* serviceMgr);
friend class nsAutoTxnsConserveSelection;
friend class nsAutoSelectionReset;
friend class nsAutoRules;
friend class nsRangeUpdater;
};
--- a/editor/nsIEditor.idl
+++ b/editor/nsIEditor.idl
@@ -16,17 +16,17 @@ interface nsIDocumentStateListener;
interface nsIOutputStream;
interface nsITransactionManager;
interface nsITransaction;
interface nsIEditorObserver;
interface nsIEditActionListener;
interface nsIInlineSpellChecker;
interface nsITransferable;
-[scriptable, uuid(65523eab-db1f-44aa-893e-dfe57ad306f0)]
+[builtinclass, scriptable, uuid(c3b61bc9-ccdd-4bcd-acd8-1b8dcbe6a247)]
interface nsIEditor : nsISupports
{
%{C++
typedef short EDirection;
typedef short EStripWrappers;
%}
const short eNone = 0;
@@ -544,9 +544,16 @@ interface nsIEditor : nsISupports
/* Run unit tests. Noop in optimized builds */
void debugUnitTests(out long outNumTests, out long outNumTestsFailed);
/* checks if a node is read-only or not */
[notxpcom] boolean isModifiableNode(in nsIDOMNode aNode);
/* Set true if you want to suppress dispatching input event. */
attribute boolean suppressDispatchingInputEvent;
+
+ /**
+ * True if an edit action is being handled (in other words, between calls of
+ * nsIEditorObserver::BeforeEditAction() and nsIEditorObserver::EditAction()
+ * or nsIEditorObserver::CancelEditAction(). Otherwise, false.
+ */
+ [infallible, noscript] readonly attribute boolean isInEditAction;
};
--- a/editor/nsIHTMLEditor.idl
+++ b/editor/nsIHTMLEditor.idl
@@ -17,17 +17,17 @@ namespace mozilla {
namespace dom {
class Element;
}
}
%}
[ptr] native Element (mozilla::dom::Element);
-[scriptable, uuid(833f30de-94c7-4630-a852-2300ef329d7b)]
+[builtinclass, scriptable, uuid(9470bee7-cad3-4382-8fb4-6bdda9f0273a)]
interface nsIHTMLEditor : nsISupports
{
%{C++
typedef short EAlignment;
%}
// used by GetAlignment()
--- a/editor/nsIPlaintextEditor.idl
+++ b/editor/nsIPlaintextEditor.idl
@@ -1,16 +1,16 @@
/* -*- 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/. */
#include "nsISupports.idl"
-[scriptable, uuid(07b6d070-ccea-4a00-84b4-f4b94dd9eb52)]
+[builtinclass, scriptable, uuid(da8f244b-6ffc-4be1-8b1a-667abfe1d304)]
interface nsIPlaintextEditor : nsISupports
{
// XXX Why aren't these in nsIEditor?
// only plain text entry is allowed via events
const long eEditorPlaintextMask = 0x0001;
// enter key and CR-LF handled specially
const long eEditorSingleLineMask = 0x0002;