From 24a5b45445c1d3ffa0a7c683d3695919af282668 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Thu, 11 Mar 2021 10:27:04 +0000 Subject: [PATCH] Bug 1692673 - Avoid enabling Cut/Copy menu commands in text editor context when there's no selection, unless a listener is present. r=masayuki Differential Revision: https://phabricator.services.mozilla.com/D107480 --- editor/libeditor/TextEditor.cpp | 47 ++++++++++++++++++++++++++++++--- editor/libeditor/TextEditor.h | 11 ++++++-- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp index 4cac42a23938..86cd733ae1e4 100644 --- a/editor/libeditor/TextEditor.cpp +++ b/editor/libeditor/TextEditor.cpp @@ -13,9 +13,11 @@ #include "PlaceholderTransaction.h" #include "gfxFontUtils.h" #include "mozilla/Assertions.h" +#include "mozilla/ContentEvents.h" #include "mozilla/ContentIterator.h" #include "mozilla/EditAction.h" #include "mozilla/EditorDOMPoint.h" +#include "mozilla/EventDispatcher.h" #include "mozilla/HTMLEditor.h" #include "mozilla/IMEStateManager.h" #include "mozilla/LookAndFeel.h" @@ -54,6 +56,7 @@ #include "nsIWeakReferenceUtils.h" #include "nsNameSpaceManager.h" #include "nsLiteralString.h" +#include "nsPresContext.h" #include "nsReadableUtils.h" #include "nsServiceManagerUtils.h" #include "nsString.h" @@ -1214,17 +1217,52 @@ bool TextEditor::AreClipboardCommandsUnconditionallyEnabled() const { return document && document->AreClipboardCommandsUnconditionallyEnabled(); } +bool TextEditor::CheckForClipboardCommandListener( + nsAtom* aCommand, EventMessage aEventMessage) const { + RefPtr document = GetDocument(); + if (!document) { + return false; + } + RefPtr presShell = document->GetObservingPresShell(); + if (!presShell) { + return false; + } + RefPtr presContext = presShell->GetPresContext(); + if (!presContext) { + return false; + } + + RefPtr et = GetDOMEventTarget(); + while (et) { + EventListenerManager* elm = et->GetOrCreateListenerManager(); + if (elm && elm->HasListenersFor(aCommand)) { + return true; + } + InternalClipboardEvent event(true, aEventMessage); + EventChainPreVisitor visitor(presContext, &event, nullptr, + nsEventStatus_eIgnore, false, et); + et->GetEventTargetParent(visitor); + et = visitor.GetParentTarget(); + } + + return false; +} + bool TextEditor::IsCutCommandEnabled() const { AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing); if (NS_WARN_IF(!editActionData.CanHandle())) { return false; } - if (AreClipboardCommandsUnconditionallyEnabled()) { + if (IsModifiable() && IsCopyToClipboardAllowedInternal()) { return true; } - return IsModifiable() && IsCopyToClipboardAllowedInternal(); + // If there's an event listener for "cut", we always enable the command + // as we don't really know what the listener may want to do in response. + // We look up the event target chain for a possible listener on a parent + // in addition to checking the immediate target. + return CheckForClipboardCommandListener(nsGkAtoms::oncut, eCut); } NS_IMETHODIMP TextEditor::Copy() { @@ -1246,11 +1284,12 @@ bool TextEditor::IsCopyCommandEnabled() const { return false; } - if (AreClipboardCommandsUnconditionallyEnabled()) { + if (IsCopyToClipboardAllowedInternal()) { return true; } - return IsCopyToClipboardAllowedInternal(); + // Like "cut", always enable "copy" if there's a listener. + return CheckForClipboardCommandListener(nsGkAtoms::oncopy, eCopy); } bool TextEditor::CanDeleteSelection() const { diff --git a/editor/libeditor/TextEditor.h b/editor/libeditor/TextEditor.h index a08f57952cdd..14c93eac6d0e 100644 --- a/editor/libeditor/TextEditor.h +++ b/editor/libeditor/TextEditor.h @@ -96,7 +96,7 @@ class TextEditor : public EditorBase, public nsITimerCallback, public nsINamed { * disabled. This always returns true if we're in non-chrome HTML/XHTML * document. Otherwise, same as the result of `IsCopyToClipboardAllowed()`. */ - bool IsCutCommandEnabled() const; + MOZ_CAN_RUN_SCRIPT bool IsCutCommandEnabled() const; NS_IMETHOD Copy() override; @@ -105,7 +105,7 @@ class TextEditor : public EditorBase, public nsITimerCallback, public nsINamed { * This always returns true if we're in non-chrome HTML/XHTML document. * Otherwise, same as the result of `IsCopyToClipboardAllowed()`. */ - bool IsCopyCommandEnabled() const; + MOZ_CAN_RUN_SCRIPT bool IsCopyCommandEnabled() const; /** * IsCopyToClipboardAllowed() returns true if the selected content can @@ -786,6 +786,13 @@ class TextEditor : public EditorBase, public nsITimerCallback, public nsINamed { bool aNotify, bool aForceStartMasking); + /** + * Helper for Is{Cut|Copy}CommandEnabled. + * Look for a listener for the given command, including up the target chain. + */ + MOZ_CAN_RUN_SCRIPT bool CheckForClipboardCommandListener( + nsAtom* aCommand, EventMessage aEventMessage) const; + protected: mutable nsCOMPtr mCachedDocumentEncoder;