mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-25 20:01:50 +00:00
Bug 1815969 - part 5: Make editors handle pasting something in new focused editor if a paste
event listener moves focus r=m_kato
If a `paste` event listener moves focus, Chrome makes new editor keep handling the pasting. For the compatibility, we should follow it unless the new focused element is in different document because user should allow to paste it explicitly. On the other hand, this just stops handling "cut" in same situation because handling it requires to update clipboard without user's activation. Therefore, the clipboard content and/or the new editor content may be lost from the users point of view. Note that `nsContentUtils::GetActiveEditor` may return `HTMLEditor` instance when focused element does not have `TextEditor` even when non-editable element has focus. Therefore, if it returns an `HTMLEditor`, we need to check whether it's active in the DOM window with a call of `HTMEditor::IsActiveInDOMWindow`. Differential Revision: https://phabricator.services.mozilla.com/D176741
This commit is contained in:
parent
c36cb31246
commit
d092d9ee9a
@ -1652,6 +1652,12 @@ nsresult EditorBase::CutAsAction(nsIPrincipal* aPrincipal) {
|
||||
}
|
||||
|
||||
{
|
||||
RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
|
||||
if (NS_WARN_IF(!focusManager)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();
|
||||
|
||||
Result<ClipboardEventResult, nsresult> ret =
|
||||
DispatchClipboardEventAndUpdateClipboard(
|
||||
eCut, nsIClipboard::kGlobalClipboard);
|
||||
@ -1671,6 +1677,24 @@ nsresult EditorBase::CutAsAction(nsIPrincipal* aPrincipal) {
|
||||
case ClipboardEventResult::DefaultPreventedOfPaste:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid result for eCut");
|
||||
}
|
||||
|
||||
// If focus is changed by a "cut" event listener, we should stop handling
|
||||
// the cut.
|
||||
const RefPtr<Element> newFocusedElement = focusManager->GetFocusedElement();
|
||||
if (MOZ_UNLIKELY(focusedElement != newFocusedElement)) {
|
||||
if (focusManager->GetFocusedWindow() != GetWindow()) {
|
||||
return NS_OK;
|
||||
}
|
||||
RefPtr<EditorBase> editorBase =
|
||||
nsContentUtils::GetActiveEditor(GetPresContext());
|
||||
if (!editorBase || (editorBase->IsHTMLEditor() &&
|
||||
!editorBase->AsHTMLEditor()->IsActiveInDOMWindow())) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (editorBase != this) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch "beforeinput" event after dispatching "cut" event.
|
||||
@ -1789,6 +1813,12 @@ nsresult EditorBase::PasteAsAction(int32_t aClipboardType,
|
||||
}
|
||||
|
||||
if (aDispatchPasteEvent == DispatchPasteEvent::Yes) {
|
||||
RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
|
||||
if (NS_WARN_IF(!focusManager)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();
|
||||
|
||||
Result<ClipboardEventResult, nsresult> ret =
|
||||
DispatchClipboardEventAndUpdateClipboard(ePaste, aClipboardType);
|
||||
if (MOZ_UNLIKELY(ret.isErr())) {
|
||||
@ -1806,6 +1836,31 @@ nsresult EditorBase::PasteAsAction(int32_t aClipboardType,
|
||||
case ClipboardEventResult::CopyOrCutHandled:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid result for ePaste");
|
||||
}
|
||||
|
||||
// If focus is changed by a "paste" event listener, we should keep handling
|
||||
// the "pasting" in new focused editor because Chrome works as so.
|
||||
const RefPtr<Element> newFocusedElement = focusManager->GetFocusedElement();
|
||||
if (MOZ_UNLIKELY(focusedElement != newFocusedElement)) {
|
||||
// For the privacy reason, let's top handling it if new focused element is
|
||||
// in different document.
|
||||
if (focusManager->GetFocusedWindow() != GetWindow()) {
|
||||
return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
|
||||
}
|
||||
RefPtr<EditorBase> editorBase =
|
||||
nsContentUtils::GetActiveEditor(GetPresContext());
|
||||
if (!editorBase || (editorBase->IsHTMLEditor() &&
|
||||
!editorBase->AsHTMLEditor()->IsActiveInDOMWindow())) {
|
||||
return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
|
||||
}
|
||||
if (editorBase != this) {
|
||||
nsresult rv = editorBase->PasteAsAction(
|
||||
aClipboardType, DispatchPasteEvent::No, aPrincipal);
|
||||
NS_WARNING_ASSERTION(
|
||||
NS_SUCCEEDED(rv),
|
||||
"EditorBase::PasteAsAction(DispatchPasteEvent::No) failed");
|
||||
return EditorBase::ToGenericNSResult(rv);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The caller must already have dispatched a "paste" event.
|
||||
editActionData.NotifyOfDispatchingClipboardEvent();
|
||||
@ -1833,6 +1888,12 @@ nsresult EditorBase::PasteAsQuotationAsAction(
|
||||
}
|
||||
|
||||
if (aDispatchPasteEvent == DispatchPasteEvent::Yes) {
|
||||
RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
|
||||
if (NS_WARN_IF(!focusManager)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();
|
||||
|
||||
Result<ClipboardEventResult, nsresult> ret =
|
||||
DispatchClipboardEventAndUpdateClipboard(ePaste, aClipboardType);
|
||||
if (MOZ_UNLIKELY(ret.isErr())) {
|
||||
@ -1850,6 +1911,31 @@ nsresult EditorBase::PasteAsQuotationAsAction(
|
||||
case ClipboardEventResult::CopyOrCutHandled:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid result for ePaste");
|
||||
}
|
||||
|
||||
// If focus is changed by a "paste" event listener, we should keep handling
|
||||
// the "pasting" in new focused editor because Chrome works as so.
|
||||
const RefPtr<Element> newFocusedElement = focusManager->GetFocusedElement();
|
||||
if (MOZ_UNLIKELY(focusedElement != newFocusedElement)) {
|
||||
// For the privacy reason, let's top handling it if new focused element is
|
||||
// in different document.
|
||||
if (focusManager->GetFocusedWindow() != GetWindow()) {
|
||||
return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
|
||||
}
|
||||
RefPtr<EditorBase> editorBase =
|
||||
nsContentUtils::GetActiveEditor(GetPresContext());
|
||||
if (!editorBase || (editorBase->IsHTMLEditor() &&
|
||||
!editorBase->AsHTMLEditor()->IsActiveInDOMWindow())) {
|
||||
return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
|
||||
}
|
||||
if (editorBase != this) {
|
||||
nsresult rv = editorBase->PasteAsQuotationAsAction(
|
||||
aClipboardType, DispatchPasteEvent::No, aPrincipal);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"EditorBase::PasteAsQuotationAsAction("
|
||||
"DispatchPasteEvent::No) failed");
|
||||
return EditorBase::ToGenericNSResult(rv);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The caller must already have dispatched a "paste" event.
|
||||
editActionData.NotifyOfDispatchingClipboardEvent();
|
||||
@ -1862,7 +1948,8 @@ nsresult EditorBase::PasteAsQuotationAsAction(
|
||||
}
|
||||
|
||||
nsresult EditorBase::PasteTransferableAsAction(
|
||||
nsITransferable* aTransferable, nsIPrincipal* aPrincipal /* = nullptr */) {
|
||||
nsITransferable* aTransferable, DispatchPasteEvent aDispatchPasteEvent,
|
||||
nsIPrincipal* aPrincipal /* = nullptr */) {
|
||||
// FIXME: This may be called as a call of nsIEditor::PasteTransferable.
|
||||
// In this case, we should keep handling the paste even in the readonly mode.
|
||||
if (IsHTMLEditor() && IsReadonly()) {
|
||||
@ -1875,7 +1962,13 @@ nsresult EditorBase::PasteTransferableAsAction(
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
{
|
||||
if (aDispatchPasteEvent == DispatchPasteEvent::Yes) {
|
||||
RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
|
||||
if (NS_WARN_IF(!focusManager)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();
|
||||
|
||||
// Use an invalid value for the clipboard type as data comes from
|
||||
// aTransferable and we don't currently implement a way to put that in the
|
||||
// data transfer in TextEditor yet.
|
||||
@ -1897,6 +1990,34 @@ nsresult EditorBase::PasteTransferableAsAction(
|
||||
case ClipboardEventResult::CopyOrCutHandled:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid result for ePaste");
|
||||
}
|
||||
|
||||
// If focus is changed by a "paste" event listener, we should keep handling
|
||||
// the "pasting" in new focused editor because Chrome works as so.
|
||||
const RefPtr<Element> newFocusedElement = focusManager->GetFocusedElement();
|
||||
if (MOZ_UNLIKELY(focusedElement != newFocusedElement)) {
|
||||
// For the privacy reason, let's top handling it if new focused element is
|
||||
// in different document.
|
||||
if (focusManager->GetFocusedWindow() != GetWindow()) {
|
||||
return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
|
||||
}
|
||||
RefPtr<EditorBase> editorBase =
|
||||
nsContentUtils::GetActiveEditor(GetPresContext());
|
||||
if (!editorBase || (editorBase->IsHTMLEditor() &&
|
||||
!editorBase->AsHTMLEditor()->IsActiveInDOMWindow())) {
|
||||
return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
|
||||
}
|
||||
if (editorBase != this) {
|
||||
nsresult rv = editorBase->PasteTransferableAsAction(
|
||||
aTransferable, DispatchPasteEvent::No, aPrincipal);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"EditorBase::PasteTransferableAsAction("
|
||||
"DispatchPasteEvent::No) failed");
|
||||
return EditorBase::ToGenericNSResult(rv);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The caller must already have dispatched a "paste" event.
|
||||
editActionData.NotifyOfDispatchingClipboardEvent();
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!aTransferable)) {
|
||||
@ -1983,9 +2104,11 @@ EditorBase::SafeToInsertData EditorBase::IsSafeToInsertData(
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EditorBase::PasteTransferable(nsITransferable* aTransferable) {
|
||||
nsresult rv = PasteTransferableAsAction(aTransferable);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"EditorBase::PasteTransferableAsAction() failed");
|
||||
nsresult rv =
|
||||
PasteTransferableAsAction(aTransferable, DispatchPasteEvent::Yes);
|
||||
NS_WARNING_ASSERTION(
|
||||
NS_SUCCEEDED(rv),
|
||||
"EditorBase::PasteTransferableAsAction(DispatchPasteEvent::Yes) failed");
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -762,12 +762,15 @@ class EditorBase : public nsIEditor,
|
||||
* Paste aTransferable at Selection.
|
||||
*
|
||||
* @param aTransferable Must not be nullptr.
|
||||
* @param aDispatchPasteEvent Yes if this should dispatch ePaste event
|
||||
* before pasting. Otherwise, No.
|
||||
* @param aPrincipal Set subject principal if it may be called by
|
||||
* JS. If set to nullptr, will be treated as
|
||||
* called by system.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT nsresult PasteTransferableAsAction(
|
||||
nsITransferable* aTransferable, nsIPrincipal* aPrincipal = nullptr);
|
||||
nsITransferable* aTransferable, DispatchPasteEvent aDispatchPasteEvent,
|
||||
nsIPrincipal* aPrincipal = nullptr);
|
||||
|
||||
/**
|
||||
* PasteAsQuotationAsAction() pastes content in clipboard as quotation.
|
||||
|
@ -507,10 +507,11 @@ nsresult PasteTransferableCommand::DoCommandParam(
|
||||
if (NS_WARN_IF(!aTransferableParam)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
nsresult rv =
|
||||
aEditorBase.PasteTransferableAsAction(aTransferableParam, aPrincipal);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"EditorBase::PasteTransferableAsAction() failed");
|
||||
nsresult rv = aEditorBase.PasteTransferableAsAction(
|
||||
aTransferableParam, EditorBase::DispatchPasteEvent::Yes, aPrincipal);
|
||||
NS_WARNING_ASSERTION(
|
||||
NS_SUCCEEDED(rv),
|
||||
"EditorBase::PasteTransferableAsAction(DispatchPasteEvent::Yes) failed");
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -234,14 +234,17 @@ class HTMLEditor final : public EditorBase,
|
||||
* PasteNoFormattingAsAction() pastes content in clipboard without any style
|
||||
* information.
|
||||
*
|
||||
* @param aSelectionType nsIClipboard::kGlobalClipboard or
|
||||
* @param aClipboardType nsIClipboard::kGlobalClipboard or
|
||||
* nsIClipboard::kSelectionClipboard.
|
||||
* @param aDispatchPasteEvent Yes if this should dispatch ePaste event
|
||||
* before pasting. Otherwise, No.
|
||||
* @param aPrincipal Set subject principal if it may be called by
|
||||
* JS. If set to nullptr, will be treated as
|
||||
* called by system.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT nsresult PasteNoFormattingAsAction(
|
||||
int32_t aSelectionType, nsIPrincipal* aPrincipal = nullptr);
|
||||
int32_t aClipboardType, DispatchPasteEvent aDispatchPasteEvent,
|
||||
nsIPrincipal* aPrincipal = nullptr);
|
||||
|
||||
bool CanPasteTransferable(nsITransferable* aTransferable) final;
|
||||
|
||||
|
@ -128,10 +128,12 @@ nsresult PasteNoFormattingCommand::DoCommand(Command aCommand,
|
||||
}
|
||||
// Known live because we hold a ref above in "editor"
|
||||
nsresult rv = MOZ_KnownLive(htmlEditor)
|
||||
->PasteNoFormattingAsAction(nsIClipboard::kGlobalClipboard,
|
||||
aPrincipal);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"HTMLEditor::PasteNoFormattingAsAction() failed");
|
||||
->PasteNoFormattingAsAction(
|
||||
nsIClipboard::kGlobalClipboard,
|
||||
EditorBase::DispatchPasteEvent::Yes, aPrincipal);
|
||||
NS_WARNING_ASSERTION(
|
||||
NS_SUCCEEDED(rv),
|
||||
"HTMLEditor::PasteNoFormattingAsAction(DispatchPasteEvent::Yes) failed");
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "nsDebug.h"
|
||||
#include "nsDependentSubstring.h"
|
||||
#include "nsError.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsIClipboard.h"
|
||||
#include "nsIContent.h"
|
||||
@ -2424,8 +2425,9 @@ nsresult HTMLEditor::HandlePasteTransferable(
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult HTMLEditor::PasteNoFormattingAsAction(int32_t aSelectionType,
|
||||
nsIPrincipal* aPrincipal) {
|
||||
nsresult HTMLEditor::PasteNoFormattingAsAction(
|
||||
int32_t aClipboardType, DispatchPasteEvent aDispatchPasteEvent,
|
||||
nsIPrincipal* aPrincipal) {
|
||||
if (IsReadonly()) {
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2436,12 +2438,18 @@ nsresult HTMLEditor::PasteNoFormattingAsAction(int32_t aSelectionType,
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
editActionData.InitializeDataTransferWithClipboard(
|
||||
SettingDataTransfer::eWithoutFormat, aSelectionType);
|
||||
SettingDataTransfer::eWithoutFormat, aClipboardType);
|
||||
|
||||
if (aDispatchPasteEvent == DispatchPasteEvent::Yes) {
|
||||
RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
|
||||
if (NS_WARN_IF(!focusManager)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();
|
||||
|
||||
{
|
||||
Result<ClipboardEventResult, nsresult> ret =
|
||||
DispatchClipboardEventAndUpdateClipboard(ePasteNoFormatting,
|
||||
aSelectionType);
|
||||
aClipboardType);
|
||||
if (MOZ_UNLIKELY(ret.isErr())) {
|
||||
NS_WARNING(
|
||||
"EditorBase::DispatchClipboardEventAndUpdateClipboard("
|
||||
@ -2457,6 +2465,41 @@ nsresult HTMLEditor::PasteNoFormattingAsAction(int32_t aSelectionType,
|
||||
case ClipboardEventResult::CopyOrCutHandled:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid result for ePaste");
|
||||
}
|
||||
|
||||
// If focus is changed by a "paste" event listener, we should keep handling
|
||||
// the "pasting" in new focused editor because Chrome works as so.
|
||||
const RefPtr<Element> newFocusedElement = focusManager->GetFocusedElement();
|
||||
if (MOZ_UNLIKELY(focusedElement != newFocusedElement)) {
|
||||
// For the privacy reason, let's top handling it if new focused element is
|
||||
// in different document.
|
||||
if (focusManager->GetFocusedWindow() != GetWindow()) {
|
||||
return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
|
||||
}
|
||||
RefPtr<EditorBase> editorBase =
|
||||
nsContentUtils::GetActiveEditor(GetPresContext());
|
||||
if (!editorBase || (editorBase->IsHTMLEditor() &&
|
||||
!editorBase->AsHTMLEditor()->IsActiveInDOMWindow())) {
|
||||
return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_ACTION_CANCELED);
|
||||
}
|
||||
if (editorBase != this) {
|
||||
if (editorBase->IsHTMLEditor()) {
|
||||
nsresult rv =
|
||||
MOZ_KnownLive(editorBase->AsHTMLEditor())
|
||||
->PasteNoFormattingAsAction(
|
||||
aClipboardType, DispatchPasteEvent::No, aPrincipal);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"HTMLEditor::PasteNoFormattingAsAction("
|
||||
"DispatchPasteEvent::No) failed");
|
||||
return EditorBase::ToGenericNSResult(rv);
|
||||
}
|
||||
nsresult rv = editorBase->PasteAsAction(
|
||||
aClipboardType, DispatchPasteEvent::No, aPrincipal);
|
||||
NS_WARNING_ASSERTION(
|
||||
NS_SUCCEEDED(rv),
|
||||
"EditorBase::PasteAsAction(DispatchPasteEvent::No) failed");
|
||||
return EditorBase::ToGenericNSResult(rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch "beforeinput" event after "paste" event. And perhaps, before
|
||||
@ -2508,7 +2551,7 @@ nsresult HTMLEditor::PasteNoFormattingAsAction(int32_t aSelectionType,
|
||||
}
|
||||
|
||||
// Get the Data from the clipboard
|
||||
rv = clipboard->GetData(transferable, aSelectionType);
|
||||
rv = clipboard->GetData(transferable, aClipboardType);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("nsIClipboard::GetData() failed");
|
||||
return rv;
|
||||
|
@ -316,6 +316,7 @@ skip-if = xorigin # Testing internal API for comm-central
|
||||
[test_pasteImgTextarea.html]
|
||||
[test_paste_as_quote_in_text_control.html]
|
||||
[test_paste_no_formatting.html]
|
||||
[test_paste_redirect_focus_in_paste_event_listener.html]
|
||||
[test_pasting_in_root_element.xhtml]
|
||||
[test_pasting_in_temporarily_created_div_outside_body.html]
|
||||
[test_pasting_table_rows.html]
|
||||
|
@ -0,0 +1,143 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testing handling "paste" command when a "paste" event listener moves focus</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(async () => {
|
||||
info("Waiting for initializing clipboard...");
|
||||
await SimpleTest.promiseClipboardChange(
|
||||
"plain text",
|
||||
() => SpecialPowers.clipboardCopyString("plain text")
|
||||
);
|
||||
|
||||
const transferable =
|
||||
SpecialPowers.Cc["@mozilla.org/widget/transferable;1"].createInstance(SpecialPowers.Ci.nsITransferable);
|
||||
transferable.init(
|
||||
SpecialPowers.wrap(window).docShell.QueryInterface(SpecialPowers.Ci.nsILoadContext)
|
||||
);
|
||||
const supportString =
|
||||
SpecialPowers.Cc["@mozilla.org/supports-string;1"].createInstance(SpecialPowers.Ci.nsISupportsString);
|
||||
supportString.data = "plain text";
|
||||
transferable.setTransferData("text/plain", supportString);
|
||||
|
||||
function getValue(aElement) {
|
||||
if (aElement.tagName.toLowerCase() == "input" ||
|
||||
aElement.tagName.toLowerCase() == "textarea") {
|
||||
return aElement.value;
|
||||
}
|
||||
return aElement.textContent;
|
||||
}
|
||||
function setValue(aElement, aValue) {
|
||||
if (aElement.tagName.toLowerCase() == "input" ||
|
||||
aElement.tagName.toLowerCase() == "textarea") {
|
||||
aElement.value = aValue;
|
||||
return;
|
||||
}
|
||||
aElement.innerHTML = aValue === "" ? "<br>" : aValue;
|
||||
}
|
||||
|
||||
for (const command of [
|
||||
"cmd_paste",
|
||||
"cmd_pasteNoFormatting",
|
||||
"cmd_pasteQuote",
|
||||
"cmd_pasteTransferable"
|
||||
]) {
|
||||
for (const editableSelector of [
|
||||
"#src > input",
|
||||
"#src > textarea",
|
||||
"#src > div[contenteditable]"
|
||||
]) {
|
||||
const editableElement = document.querySelector(editableSelector);
|
||||
const editableElementDesc = `<${
|
||||
editableElement.tagName.toLocaleLowerCase()
|
||||
}${editableElement.hasAttribute("contenteditable") ? " contenteditable" : ""}>`;
|
||||
(test_from_editableElement_to_input => {
|
||||
const input = document.querySelector("#dest > input");
|
||||
editableElement.focus();
|
||||
editableElement.addEventListener(
|
||||
"paste",
|
||||
() => input.focus(),
|
||||
{once: true}
|
||||
);
|
||||
SpecialPowers.doCommand(window, command, transferable);
|
||||
is(
|
||||
getValue(editableElement).replace(/\n/g, ""),
|
||||
"",
|
||||
`${command}: ${
|
||||
editableElementDesc
|
||||
} should not have the pasted text because focus is redirected to <input> in a "paste" event listener`
|
||||
);
|
||||
is(
|
||||
input.value.replace("> ", ""),
|
||||
"plain text",
|
||||
`${command}: new focused <input> (moved from ${
|
||||
editableElementDesc
|
||||
}) should have the pasted text`
|
||||
);
|
||||
setValue(editableElement, "");
|
||||
input.value = "";
|
||||
})();
|
||||
|
||||
(test_from_editableElement_to_contenteditable => {
|
||||
const contentEditable = document.querySelector("#dest > div[contenteditable]");
|
||||
editableElement.focus();
|
||||
editableElement.addEventListener(
|
||||
"paste",
|
||||
() => contentEditable.focus(),
|
||||
{once: true}
|
||||
);
|
||||
SpecialPowers.doCommand(window, command, transferable);
|
||||
is(
|
||||
getValue(editableElement).replace(/\n/g, ""),
|
||||
"",
|
||||
`${command}: ${
|
||||
editableElementDesc
|
||||
} should not have the pasted text because focus is redirected to <div contenteditable> in a "paste" event listener`
|
||||
);
|
||||
is(
|
||||
contentEditable.textContent.replace(/\n/g, "").replace("> ", ""),
|
||||
"plain text",
|
||||
`${command}: new focused <div contenteditable> (moved from ${
|
||||
editableElementDesc
|
||||
}) should have the pasted text`
|
||||
);
|
||||
setValue(editableElement, "");
|
||||
contentEditable.innerHTML = "<br>";
|
||||
})();
|
||||
|
||||
(test_from_editableElement_to_non_editable => {
|
||||
const button = document.querySelector("#dest > button");
|
||||
editableElement.focus();
|
||||
editableElement.addEventListener(
|
||||
"paste",
|
||||
() => button.focus(),
|
||||
{once: true}
|
||||
);
|
||||
SpecialPowers.doCommand(window, command, transferable);
|
||||
is(
|
||||
getValue(editableElement).replace(/\n/g, ""),
|
||||
"",
|
||||
`${command}: ${
|
||||
editableElementDesc
|
||||
} should not have the pasted text because focus is redirected to <button> in a "paste" event listener`
|
||||
);
|
||||
setValue(editableElement, "");
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="src"><input><textarea></textarea><div contenteditable><br></div></div>
|
||||
<div id="dest"><input><div contenteditable><br></div><button>button</button></div>
|
||||
</body>
|
||||
</html>
|
@ -2159,10 +2159,16 @@ export class SpecialPowersChild extends JSWindowActorChild {
|
||||
case "cmd_highlight":
|
||||
case "cmd_insertImageNoUI":
|
||||
case "cmd_insertLinkNoUI":
|
||||
case "cmd_paragraphState":
|
||||
let params = Cu.createCommandParams();
|
||||
case "cmd_paragraphState": {
|
||||
const params = Cu.createCommandParams();
|
||||
params.setStringValue("state_attribute", param);
|
||||
return window.docShell.doCommandWithParams(cmd, params);
|
||||
}
|
||||
case "cmd_pasteTransferable": {
|
||||
const params = Cu.createCommandParams();
|
||||
params.setISupportsValue("transferable", param);
|
||||
return window.docShell.doCommandWithParams(cmd, params);
|
||||
}
|
||||
default:
|
||||
return window.docShell.doCommand(cmd);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user