From 2f2010005916befb241b2473107113bc656f044e Mon Sep 17 00:00:00 2001 From: Moonchild Date: Fri, 20 Mar 2026 13:26:18 +0100 Subject: [PATCH] Issue #3011 - Part 1: Add As{Text|Html}Editor() and AsEditorBase() This adds helper functions to get specific types of content editors (if possible) through the nsIEditor interface, as opposed to doing manual casting and the ad hoc `GetHTMLEditor()` function. Helper functions are spread out over multiple headers due to the circular dependency issues that would be triggered otherwise. --- dom/base/nsINode.cpp | 1 + editor/libeditor/EditorBase.cpp | 1 + editor/libeditor/EditorBase.h | 20 ++++++++++ editor/libeditor/HTMLEditRules.cpp | 9 ++++- editor/libeditor/HTMLEditor.cpp | 3 +- editor/libeditor/HTMLEditor.h | 16 ++++++++ editor/libeditor/HTMLEditorEventListener.cpp | 39 +++++++++++--------- editor/libeditor/HTMLEditorEventListener.h | 6 +-- editor/libeditor/TextEditor.cpp | 1 + editor/libeditor/TextEditor.h | 14 +++++++ editor/nsIEditor.idl | 38 +++++++++++++++++++ 11 files changed, 123 insertions(+), 25 deletions(-) diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 5218938ca3..1766e9b920 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -16,6 +16,7 @@ #include "mozilla/CORSMode.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventListenerManager.h" +#include "mozilla/HTMLEditor.h" #include "mozilla/InternalMutationEvent.h" #include "mozilla/Likely.h" #include "mozilla/MemoryReporting.h" diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index 60df3571ef..2a144e2738 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -142,6 +142,7 @@ EditorBase::EditorBase() , mDispatchInputEvent(true) , mIsInEditAction(false) , mHidingCaret(false) + , mIsHTMLEditorClass(false) { } diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h index 08a895dcdc..2d1a60dba1 100644 --- a/editor/libeditor/EditorBase.h +++ b/editor/libeditor/EditorBase.h @@ -114,6 +114,7 @@ class DeleteNodeTransaction; class DeleteTextTransaction; class EditAggregateTransaction; class ErrorResult; +class HTMLEditor; class InsertNodeTransaction; class InsertTextTransaction; class JoinNodeTransaction; @@ -121,6 +122,7 @@ class PlaceholderTransaction; class RemoveStyleSheetTransaction; class SplitNodeTransaction; class TextComposition; +class TextEditor; struct EditorDOMPoint; namespace dom { @@ -1113,14 +1115,32 @@ protected: bool mIsInEditAction; // Whether caret is hidden forcibly. bool mHidingCaret; + // Whether we are an HTML editor class. + bool mIsHTMLEditorClass; friend bool NSCanUnload(nsISupports* serviceMgr); friend class AutoRules; friend class AutoSelectionRestorer; friend class AutoTransactionsConserveSelection; friend class RangeUpdater; + friend class nsIEditor; }; } // namespace mozilla +// nsIEditor helper functions. +// Here because of code context. +mozilla::EditorBase* +nsIEditor::AsEditorBase() +{ + return static_cast(this); +} + +const mozilla::EditorBase* +nsIEditor::AsEditorBase() const +{ + return static_cast(this); +} + + #endif // #ifndef mozilla_EditorBase_h diff --git a/editor/libeditor/HTMLEditRules.cpp b/editor/libeditor/HTMLEditRules.cpp index a5b284d82f..c1f90d4483 100644 --- a/editor/libeditor/HTMLEditRules.cpp +++ b/editor/libeditor/HTMLEditRules.cpp @@ -233,9 +233,16 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLEditRules, TextEditRules, NS_IMETHODIMP HTMLEditRules::Init(TextEditor* aTextEditor) { + if (NS_WARN_IF(!aTextEditor)) { + return NS_ERROR_INVALID_ARG; + } + InitFields(); - mHTMLEditor = static_cast(aTextEditor); + mHTMLEditor = aTextEditor->AsHTMLEditor(); + if (NS_WARN_IF(!mHTMLEditor)) { + return NS_ERROR_INVALID_ARG; + } // call through to base class Init nsresult rv = TextEditRules::Init(aTextEditor); diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index 767856a1bc..1faccd1e15 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -132,6 +132,7 @@ HTMLEditor::HTMLEditor() , mPositionedObjectBorderTop(0) , mGridSize(0) { + mIsHTMLEditorClass = true; } HTMLEditor::~HTMLEditor() @@ -501,7 +502,7 @@ HTMLEditor::InitRules() // instantiate the rules for the html editor mRules = new HTMLEditRules(); } - return mRules->Init(static_cast(this)); + return mRules->Init(this); } NS_IMETHODIMP diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h index e90e7ebe6e..f314d24d00 100644 --- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -1108,4 +1108,20 @@ private: } // namespace mozilla +// nsIEditor helper functions. +// Here because of code context. +mozilla::HTMLEditor* +nsIEditor::AsHTMLEditor() +{ + return static_cast(this)->mIsHTMLEditorClass ? + static_cast(this) : nullptr; +} + +const mozilla::HTMLEditor* +nsIEditor::AsHTMLEditor() const +{ + return static_cast(this)->mIsHTMLEditorClass ? + static_cast(this) : nullptr; +} + #endif // #ifndef mozilla_HTMLEditor_h diff --git a/editor/libeditor/HTMLEditorEventListener.cpp b/editor/libeditor/HTMLEditorEventListener.cpp index aa767519c7..3699e11acb 100644 --- a/editor/libeditor/HTMLEditorEventListener.cpp +++ b/editor/libeditor/HTMLEditorEventListener.cpp @@ -30,24 +30,18 @@ namespace mozilla { using namespace dom; -#ifdef DEBUG nsresult HTMLEditorEventListener::Connect(EditorBase* aEditorBase) { - nsCOMPtr htmlEditor = do_QueryObject(aEditorBase); - nsCOMPtr htmlInlineTableEditor = - do_QueryObject(aEditorBase); - NS_PRECONDITION(htmlEditor && htmlInlineTableEditor, - "Set HTMLEditor or its sub class"); - return EditorEventListener::Connect(aEditorBase); -} -#endif - -HTMLEditor* -HTMLEditorEventListener::GetHTMLEditor() -{ - // mEditor must be HTMLEditor or its subclass. - return static_cast(mEditorBase); + if (NS_WARN_IF(!aEditorBase)) { + return NS_ERROR_INVALID_ARG; + } + // Guarantee that mEditorBase is always HTMLEditor. + HTMLEditor* htmlEditor = aEditorBase->AsHTMLEditor(); + if (NS_WARN_IF(!htmlEditor)) { + return NS_ERROR_INVALID_ARG; + } + return EditorEventListener::Connect(htmlEditor); } nsresult @@ -59,7 +53,8 @@ HTMLEditorEventListener::MouseUp(nsIDOMMouseEvent* aMouseEvent) // FYI: We need to notify HTML editor of mouseup even if it's consumed // because HTML editor always needs to release grabbing resizer. - HTMLEditor* htmlEditor = GetHTMLEditor(); + HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor(); + MOZ_ASSERT(htmlEditor); nsCOMPtr target; nsresult rv = aMouseEvent->AsEvent()->GetTarget(getter_AddRefs(target)); @@ -85,7 +80,9 @@ HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent) WidgetMouseEvent* mousedownEvent = aMouseEvent->AsEvent()->WidgetEventPtr()->AsMouseEvent(); - HTMLEditor* htmlEditor = GetHTMLEditor(); + HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor(); + MOZ_ASSERT(htmlEditor); + // Contenteditable should disregard mousedowns outside it. // IsAcceptableInputEvent() checks it for a mouse event. if (!htmlEditor->IsAcceptableInputEvent(mousedownEvent)) { @@ -221,13 +218,19 @@ HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent) nsresult HTMLEditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent) { + if (NS_WARN_IF(DetachedFromEditor())) { + return NS_OK; + } + nsCOMPtr target; nsresult rv = aMouseEvent->AsEvent()->GetTarget(getter_AddRefs(target)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER); nsCOMPtr element = do_QueryInterface(target); - GetHTMLEditor()->DoInlineTableEditingAction(element); + HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor(); + MOZ_ASSERT(htmlEditor); + htmlEditor->DoInlineTableEditingAction(element); return EditorEventListener::MouseClick(aMouseEvent); } diff --git a/editor/libeditor/HTMLEditorEventListener.h b/editor/libeditor/HTMLEditorEventListener.h index b97b675b51..69bbf705d9 100644 --- a/editor/libeditor/HTMLEditorEventListener.h +++ b/editor/libeditor/HTMLEditorEventListener.h @@ -25,17 +25,13 @@ public: { } -#ifdef DEBUG - // WARNING: You must be use HTMLEditor or its sub class for this class. + // Connect() fails if aEditorBase isn't an HTMLEditor instance. virtual nsresult Connect(EditorBase* aEditorBase) override; -#endif protected: virtual nsresult MouseDown(nsIDOMMouseEvent* aMouseEvent) override; virtual nsresult MouseUp(nsIDOMMouseEvent* aMouseEvent) override; virtual nsresult MouseClick(nsIDOMMouseEvent* aMouseEvent) override; - - inline HTMLEditor* GetHTMLEditor(); }; } // namespace mozilla diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp index 5f75e7e7e0..2b56ae4fc4 100644 --- a/editor/libeditor/TextEditor.cpp +++ b/editor/libeditor/TextEditor.cpp @@ -10,6 +10,7 @@ #include "gfxFontUtils.h" #include "mozilla/Assertions.h" #include "mozilla/EditorUtils.h" // AutoEditBatch, AutoRules +#include "mozilla/HTMLEditor.h" #include "mozilla/mozalloc.h" #include "mozilla/Preferences.h" #include "mozilla/TextEditRules.h" diff --git a/editor/libeditor/TextEditor.h b/editor/libeditor/TextEditor.h index 7bb594931b..f4a744d759 100644 --- a/editor/libeditor/TextEditor.h +++ b/editor/libeditor/TextEditor.h @@ -247,4 +247,18 @@ protected: } // namespace mozilla +// nsIEditor helper functions. +// Here because of code context. +mozilla::TextEditor* +nsIEditor::AsTextEditor() +{ + return static_cast(this); +} + +const mozilla::TextEditor* +nsIEditor::AsTextEditor() const +{ + return static_cast(this); +} + #endif // #ifndef mozilla_TextEditor_h diff --git a/editor/nsIEditor.idl b/editor/nsIEditor.idl index bb9026d0ee..d96a3954e9 100644 --- a/editor/nsIEditor.idl +++ b/editor/nsIEditor.idl @@ -21,6 +21,14 @@ interface nsIEditActionListener; interface nsIInlineSpellChecker; interface nsITransferable; +%{C++ +namespace mozilla { +class EditorBase; +class HTMLEditor; +class TextEditor; +} // namespace mozilla +%} + [scriptable, uuid(094be624-f0bf-400f-89e2-6a84baab9474)] interface nsIEditor : nsISupports { @@ -564,4 +572,34 @@ interface nsIEditor : nsISupports * or nsIEditorObserver::CancelEditAction(). Otherwise, false. */ [noscript] readonly attribute boolean isInEditAction; + +%{C++ + /** + * AsEditorBase() returns a pointer to EditorBase class. + * + * In order to avoid circular dependency issues, this method is defined + * in mozilla/EditorBase.h. Consumers need to #include that header. + */ + inline mozilla::EditorBase* AsEditorBase(); + inline const mozilla::EditorBase* AsEditorBase() const; + + /** + * AsTextEditor() returns a pointer to TextEditor class. + * + * In order to avoid circular dependency issues, this method is defined + * in mozilla/TextEditor.h. Consumers need to #include that header. + */ + inline mozilla::TextEditor* AsTextEditor(); + inline const mozilla::TextEditor* AsTextEditor() const; + + /** + * AsHTMLEditor() returns a pointer to HTMLEditor class. + * + * In order to avoid circular dependency issues, this method is defined + * in mozilla/HTMLEditor.h. Consumers need to #include that header. + */ + inline mozilla::HTMLEditor* AsHTMLEditor(); + inline const mozilla::HTMLEditor* AsHTMLEditor() const; +%} + };