summaryrefslogtreecommitdiffstats
path: root/editor/libeditor
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:50 +0000
commitdef92d1b8e9d373e2f6f27c366d578d97d8960c6 (patch)
tree2ef34b9ad8bb9a9220e05d60352558b15f513894 /editor/libeditor
parentAdding debian version 125.0.3-1. (diff)
downloadfirefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.tar.xz
firefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'editor/libeditor')
-rw-r--r--editor/libeditor/EditAction.h25
-rw-r--r--editor/libeditor/EditorBase.cpp118
-rw-r--r--editor/libeditor/EditorBase.h2
-rw-r--r--editor/libeditor/HTMLEditUtils.cpp2
-rw-r--r--editor/libeditor/HTMLEditor.cpp12
-rw-r--r--editor/libeditor/tests/test_bug569988.html11
6 files changed, 114 insertions, 56 deletions
diff --git a/editor/libeditor/EditAction.h b/editor/libeditor/EditAction.h
index 6b900b0587..f74d1c6949 100644
--- a/editor/libeditor/EditAction.h
+++ b/editor/libeditor/EditAction.h
@@ -81,16 +81,19 @@ enum class EditAction {
// new non-empty composition string and IME selections.
eUpdateComposition,
- // eCommitComposition indicates that user commits composition.
+ // eUpdateCompositionToCommit indicates that user commits composition with
+ // the new data. That means that there will be no IME selections, but the
+ // composition continues until the following eCompositionEnd event.
+ eUpdateCompositionToCommit,
+
+ // eCommitComposition indicates that user commits composition and ends the
+ // composition.
eCommitComposition,
- // eCancelComposition indicates that user cancels composition.
+ // eCancelComposition indicates that user cancels composition and ends the
+ // composition with empty string.
eCancelComposition,
- // eDeleteByComposition indicates that user starts composition with
- // empty string and there was selected content.
- eDeleteByComposition,
-
// eUndo/eRedo indicate to undo/redo a transaction.
eUndo,
eRedo,
@@ -547,6 +550,7 @@ inline EditorInputType ToInputType(EditAction aEditAction) {
case EditAction::ePasteAsQuotation:
return EditorInputType::eInsertFromPasteAsQuotation;
case EditAction::eUpdateComposition:
+ case EditAction::eUpdateCompositionToCommit:
return EditorInputType::eInsertCompositionText;
case EditAction::eCommitComposition:
if (StaticPrefs::dom_input_events_conform_to_level_1()) {
@@ -558,13 +562,6 @@ inline EditorInputType ToInputType(EditAction aEditAction) {
return EditorInputType::eInsertCompositionText;
}
return EditorInputType::eDeleteCompositionText;
- case EditAction::eDeleteByComposition:
- if (StaticPrefs::dom_input_events_conform_to_level_1()) {
- // XXX Or EditorInputType::eDeleteContent? I don't know which IME may
- // causes this situation.
- return EditorInputType::eInsertCompositionText;
- }
- return EditorInputType::eDeleteByComposition;
case EditAction::eInsertLinkElement:
return EditorInputType::eInsertLink;
case EditAction::eDeleteWordBackward:
@@ -713,9 +710,9 @@ inline bool MayEditActionDeleteSelection(const EditAction aEditAction) {
return false;
case EditAction::eUpdateComposition:
+ case EditAction::eUpdateCompositionToCommit:
case EditAction::eCommitComposition:
case EditAction::eCancelComposition:
- case EditAction::eDeleteByComposition:
return true;
case EditAction::eUndo:
diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp
index e8120d93d9..cd1439e4c0 100644
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -34,6 +34,7 @@
#include "ErrorList.h"
#include "gfxFontUtils.h" // for gfxFontUtils
#include "mozilla/Assertions.h"
+#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/intl/BidiEmbeddingLevel.h"
#include "mozilla/BasePrincipal.h" // for BasePrincipal
#include "mozilla/CheckedInt.h" // for CheckedInt
@@ -3714,32 +3715,20 @@ nsresult EditorBase::OnCompositionChange(
return NS_ERROR_FAILURE;
}
- AutoEditActionDataSetter editActionData(*this,
- EditAction::eUpdateComposition);
+ AutoEditActionDataSetter editActionData(
+ *this,
+ // We need to distinguish whether the composition change is followed by
+ // compositionend or not (i.e., wether IME has already ended the
+ // composition or still has the composition) because we need to dispatch
+ // `textInput` event only for the last composition change.
+ aCompositionChangeEvent.IsFollowedByCompositionEnd()
+ ? EditAction::eUpdateCompositionToCommit
+ : EditAction::eUpdateComposition);
if (NS_WARN_IF(!editActionData.CanHandle())) {
return NS_ERROR_NOT_INITIALIZED;
}
-
- // If:
- // - new composition string is not empty,
- // - there is no composition string in the DOM tree,
- // - and there is non-collapsed Selection,
- // the selected content will be removed by this composition.
- if (aCompositionChangeEvent.mData.IsEmpty() &&
- mComposition->String().IsEmpty() && !SelectionRef().IsCollapsed()) {
- editActionData.UpdateEditAction(EditAction::eDeleteByComposition);
- }
-
- // If Input Events Level 2 is enabled, EditAction::eDeleteByComposition is
- // mapped to EditorInputType::eDeleteByComposition and it requires null
- // for InputEvent.data. Therefore, only otherwise, we should set data.
- if (ToInputType(editActionData.GetEditAction()) !=
- EditorInputType::eDeleteByComposition) {
- MOZ_ASSERT(ToInputType(editActionData.GetEditAction()) ==
- EditorInputType::eInsertCompositionText);
- MOZ_ASSERT(!aCompositionChangeEvent.mData.IsVoid());
- editActionData.SetData(aCompositionChangeEvent.mData);
- }
+ MOZ_ASSERT(!aCompositionChangeEvent.mData.IsVoid());
+ editActionData.SetData(aCompositionChangeEvent.mData);
// If we're an `HTMLEditor` and this is second or later composition change,
// we should set target range to the range of composition string.
@@ -6513,6 +6502,13 @@ nsresult EditorBase::AutoEditActionDataSetter::MaybeFlushPendingNotifications()
return NS_OK;
}
+void EditorBase::AutoEditActionDataSetter::MarkEditActionCanceled() {
+ mBeforeInputEventCanceled = true;
+ if (mEditorBase.IsHTMLEditor()) {
+ mEditorBase.AsHTMLEditor()->mHasBeforeInputBeenCanceled = true;
+ }
+}
+
nsresult EditorBase::AutoEditActionDataSetter::MaybeDispatchBeforeInputEvent(
nsIEditor::EDirection aDeleteDirectionAndAmount /* = nsIEditor::eNone */) {
MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent(),
@@ -6637,11 +6633,79 @@ nsresult EditorBase::AutoEditActionDataSetter::MaybeDispatchBeforeInputEvent(
NS_WARNING("nsContentUtils::DispatchInputEvent() failed");
return rv;
}
- mBeforeInputEventCanceled = status == nsEventStatus_eConsumeNoDefault;
- if (mBeforeInputEventCanceled && mEditorBase.IsHTMLEditor()) {
- mEditorBase.AsHTMLEditor()->mHasBeforeInputBeenCanceled = true;
+ if (status == nsEventStatus_eConsumeNoDefault) {
+ MarkEditActionCanceled();
+ return NS_ERROR_EDITOR_ACTION_CANCELED;
+ }
+
+ nsCOMPtr<nsIWidget> widget = editorBase->GetWidget();
+ if (!StaticPrefs::dom_events_textevent_enabled() ||
+ !targetElement->IsInComposedDoc() || !widget) {
+ return NS_OK;
+ }
+ nsString textInputData;
+ RefPtr<DataTransfer> textInputDataTransfer;
+ switch (inputType) {
+ case EditorInputType::eInsertCompositionText:
+ // If the composition is still being composed, we should not dispatch
+ // textInput event, but we need to dispatch it for the last composition
+ // change because web apps should know the inserting commit string as
+ // same as input from keyboard.
+ if (mEditAction == EditAction::eUpdateComposition) {
+ return NS_OK;
+ }
+ [[fallthrough]];
+ case EditorInputType::eInsertText:
+ textInputData = mData;
+ break;
+ case EditorInputType::eInsertFromDrop:
+ case EditorInputType::eInsertFromPaste:
+ case EditorInputType::eInsertFromPasteAsQuotation:
+ if (mDataTransfer) {
+ textInputDataTransfer = mDataTransfer;
+ } else {
+ textInputData = mData;
+ }
+ break;
+ case EditorInputType::eInsertLineBreak:
+ case EditorInputType::eInsertParagraph:
+ // Don't dispatch `textInput` on <input> because Chrome does not do it.
+ // On the other hand, we need to dispatch it on <textarea> and
+ // contenteditable.
+ if (mEditorBase.IsTextEditor() && mEditorBase.IsSingleLineEditor()) {
+ return NS_OK;
+ }
+ textInputData.Assign(u'\n');
+ break;
+ default:
+ return NS_OK;
+ }
+
+ InternalLegacyTextEvent textEvent(true, eLegacyTextInput, widget);
+ textEvent.mData = std::move(textInputData);
+ textEvent.mDataTransfer = std::move(textInputDataTransfer);
+ textEvent.mInputType = inputType;
+ // Make it always cancelable even though we ignore it when inserting or
+ // deleting composition. This is compatible with Chrome.
+ // However, if and only if it's unsafe, let's set it not cancelable because of
+ // asynchronous dispatching.
+ textEvent.mFlags.mCancelable = nsContentUtils::IsSafeToRunScript();
+
+ status = nsEventStatus_eIgnore;
+ rv = AsyncEventDispatcher::RunDOMEventWhenSafe(*targetElement, textEvent,
+ &status);
+ if (NS_WARN_IF(mEditorBase.Destroyed())) {
+ return NS_ERROR_EDITOR_DESTROYED;
}
- return mBeforeInputEventCanceled ? NS_ERROR_EDITOR_ACTION_CANCELED : NS_OK;
+ if (NS_FAILED(rv)) {
+ NS_WARNING("AsyncEventDispatcher::RunDOMEventWhenSafe() failed");
+ return rv;
+ }
+ if (status == nsEventStatus_eConsumeNoDefault) {
+ MarkEditActionCanceled();
+ return NS_ERROR_EDITOR_ACTION_CANCELED;
+ }
+ return NS_OK;
}
/*****************************************************************************
diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h
index 83f53ff215..f0335ac5d5 100644
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -1361,6 +1361,8 @@ class EditorBase : public nsIEditor,
}
}
+ void MarkEditActionCanceled();
+
EditorBase& mEditorBase;
RefPtr<Selection> mSelection;
nsTArray<OwningNonNull<Selection>> mRetiredSelections;
diff --git a/editor/libeditor/HTMLEditUtils.cpp b/editor/libeditor/HTMLEditUtils.cpp
index 8c3d09c1e2..c2cbbe6157 100644
--- a/editor/libeditor/HTMLEditUtils.cpp
+++ b/editor/libeditor/HTMLEditUtils.cpp
@@ -538,7 +538,7 @@ bool HTMLEditUtils::IsLink(const nsINode* aNode) {
return false;
}
- nsAutoString tmpText;
+ nsAutoCString tmpText;
anchor->GetHref(tmpText);
return !tmpText.IsEmpty();
}
diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp
index e9ea8887b7..fc88c79477 100644
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3773,10 +3773,14 @@ nsresult HTMLEditor::InsertLinkAroundSelectionAsAction(
return EditorBase::ToGenericNSResult(rv);
}
- nsAutoString href;
- anchor->GetHref(href);
- if (href.IsEmpty()) {
- return NS_OK;
+ // XXX Is this ok? Does this just want to check that we're a link? If so
+ // there are faster ways to do this.
+ {
+ nsAutoCString href;
+ anchor->GetHref(href);
+ if (href.IsEmpty()) {
+ return NS_OK;
+ }
}
AutoPlaceholderBatch treatAsOneTransaction(
diff --git a/editor/libeditor/tests/test_bug569988.html b/editor/libeditor/tests/test_bug569988.html
index 6181668374..2ff84d9d05 100644
--- a/editor/libeditor/tests/test_bug569988.html
+++ b/editor/libeditor/tests/test_bug569988.html
@@ -31,17 +31,9 @@ function runTest() {
var os = Services.obs;
os.addObserver(onPromptLoad, "common-dialog-loaded");
- os.addObserver(onPromptLoad, "tabmodal-dialog-loaded");
function onPromptLoad(subject) {
- let ui = subject.Dialog ? subject.Dialog.ui : undefined;
- if (!ui) {
- // subject is an tab prompt, find the elements ourselves
- ui = {
- loginTextbox: subject.querySelector(".tabmodalprompt-loginTextbox"),
- button0: subject.querySelector(".tabmodalprompt-button0"),
- };
- }
+ let ui = subject.Dialog.ui;
sendAsyncMessage("ok", [true, "onPromptLoad is called"]);
gPromptInput = ui.loginTextbox;
gPromptInput.addEventListener("focus", onPromptFocus);
@@ -77,7 +69,6 @@ function runTest() {
}
addMessageListener("destroy", function() {
- os.removeObserver(onPromptLoad, "tabmodal-dialog-loaded");
os.removeObserver(onPromptLoad, "common-dialog-loaded");
});
});