mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
Bug 1863611 Make EventStateManager
send content command events to focused remote process r=smaug
Currently, `EventStateManager` handles some content commands only in the parent process. Therefore, if `eSetSelection` event or something is being handled in the focused remote process, the command may be disabled yet. Avoiding the race conditions, we should handle the content command events in the focused process. Differential Revision: https://phabricator.services.mozilla.com/D222789
This commit is contained in:
parent
03a65b5028
commit
1078a6796e
@ -6747,24 +6747,31 @@ nsresult EventStateManager::DoContentCommandEvent(
|
|||||||
nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
|
nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
|
||||||
NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
|
||||||
const char* cmd;
|
const char* cmd;
|
||||||
|
bool maybeNeedToHandleInRemote = false;
|
||||||
switch (aEvent->mMessage) {
|
switch (aEvent->mMessage) {
|
||||||
case eContentCommandCut:
|
case eContentCommandCut:
|
||||||
cmd = "cmd_cut";
|
cmd = "cmd_cut";
|
||||||
|
maybeNeedToHandleInRemote = true;
|
||||||
break;
|
break;
|
||||||
case eContentCommandCopy:
|
case eContentCommandCopy:
|
||||||
cmd = "cmd_copy";
|
cmd = "cmd_copy";
|
||||||
|
maybeNeedToHandleInRemote = true;
|
||||||
break;
|
break;
|
||||||
case eContentCommandPaste:
|
case eContentCommandPaste:
|
||||||
cmd = "cmd_paste";
|
cmd = "cmd_paste";
|
||||||
|
maybeNeedToHandleInRemote = true;
|
||||||
break;
|
break;
|
||||||
case eContentCommandDelete:
|
case eContentCommandDelete:
|
||||||
cmd = "cmd_delete";
|
cmd = "cmd_delete";
|
||||||
|
maybeNeedToHandleInRemote = true;
|
||||||
break;
|
break;
|
||||||
case eContentCommandUndo:
|
case eContentCommandUndo:
|
||||||
cmd = "cmd_undo";
|
cmd = "cmd_undo";
|
||||||
|
maybeNeedToHandleInRemote = true;
|
||||||
break;
|
break;
|
||||||
case eContentCommandRedo:
|
case eContentCommandRedo:
|
||||||
cmd = "cmd_redo";
|
cmd = "cmd_redo";
|
||||||
|
maybeNeedToHandleInRemote = true;
|
||||||
break;
|
break;
|
||||||
case eContentCommandPasteTransferable:
|
case eContentCommandPasteTransferable:
|
||||||
cmd = "cmd_pasteTransferable";
|
cmd = "cmd_pasteTransferable";
|
||||||
@ -6775,6 +6782,20 @@ nsresult EventStateManager::DoContentCommandEvent(
|
|||||||
default:
|
default:
|
||||||
return NS_ERROR_NOT_IMPLEMENTED;
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
if (XRE_IsParentProcess() && maybeNeedToHandleInRemote) {
|
||||||
|
if (BrowserParent* remote = BrowserParent::GetFocused()) {
|
||||||
|
if (!aEvent->mOnlyEnabledCheck) {
|
||||||
|
remote->SendSimpleContentCommandEvent(*aEvent);
|
||||||
|
}
|
||||||
|
// XXX The command may be disabled in the parent process. Perhaps, we
|
||||||
|
// should set actual enabled state in the parent process here and there
|
||||||
|
// should be another bool flag which indicates whether the content is sent
|
||||||
|
// to a remote process.
|
||||||
|
aEvent->mIsEnabled = true;
|
||||||
|
aEvent->mSucceeded = true;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
// If user tries to do something, user must try to do it in visible window.
|
// If user tries to do something, user must try to do it in visible window.
|
||||||
// So, let's retrieve controller of visible window.
|
// So, let's retrieve controller of visible window.
|
||||||
nsCOMPtr<nsIController> controller;
|
nsCOMPtr<nsIController> controller;
|
||||||
@ -6864,8 +6885,12 @@ nsresult EventStateManager::DoContentCommandInsertTextEvent(
|
|||||||
if (XRE_IsParentProcess()) {
|
if (XRE_IsParentProcess()) {
|
||||||
// Handle it in focused content process if there is.
|
// Handle it in focused content process if there is.
|
||||||
if (BrowserParent* remote = BrowserParent::GetFocused()) {
|
if (BrowserParent* remote = BrowserParent::GetFocused()) {
|
||||||
remote->SendInsertText(aEvent->mString.ref());
|
if (!aEvent->mOnlyEnabledCheck) {
|
||||||
aEvent->mIsEnabled = true; // XXX it can be a lie...
|
remote->SendInsertText(*aEvent);
|
||||||
|
}
|
||||||
|
// XXX The remote process may be not editable right now. Therefore, this
|
||||||
|
// may be different from actual state in the remote process.
|
||||||
|
aEvent->mIsEnabled = true;
|
||||||
aEvent->mSucceeded = true;
|
aEvent->mSucceeded = true;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -6901,10 +6926,12 @@ nsresult EventStateManager::DoContentCommandReplaceTextEvent(
|
|||||||
if (XRE_IsParentProcess()) {
|
if (XRE_IsParentProcess()) {
|
||||||
// Handle it in focused content process if there is.
|
// Handle it in focused content process if there is.
|
||||||
if (BrowserParent* remote = BrowserParent::GetFocused()) {
|
if (BrowserParent* remote = BrowserParent::GetFocused()) {
|
||||||
Unused << remote->SendReplaceText(
|
if (!aEvent->mOnlyEnabledCheck) {
|
||||||
aEvent->mSelection.mReplaceSrcString, aEvent->mString.ref(),
|
Unused << remote->SendReplaceText(*aEvent);
|
||||||
aEvent->mSelection.mOffset, aEvent->mSelection.mPreventSetSelection);
|
}
|
||||||
aEvent->mIsEnabled = true; // XXX it can be a lie...
|
// XXX The remote process may be not editable right now. Therefore, this
|
||||||
|
// may be different from actual state in the remote process.
|
||||||
|
aEvent->mIsEnabled = true;
|
||||||
aEvent->mSucceeded = true;
|
aEvent->mSucceeded = true;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -2312,6 +2312,20 @@ mozilla::ipc::IPCResult BrowserChild::RecvNormalPrioritySelectionEvent(
|
|||||||
return RecvSelectionEvent(aEvent);
|
return RecvSelectionEvent(aEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult BrowserChild::RecvSimpleContentCommandEvent(
|
||||||
|
const EventMessage& aMessage) {
|
||||||
|
WidgetContentCommandEvent localEvent(true, aMessage, mPuppetWidget);
|
||||||
|
DispatchWidgetEventViaAPZ(localEvent);
|
||||||
|
Unused << SendOnEventNeedingAckHandled(aMessage, 0u);
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
BrowserChild::RecvNormalPrioritySimpleContentCommandEvent(
|
||||||
|
const EventMessage& aMessage) {
|
||||||
|
return RecvSimpleContentCommandEvent(aMessage);
|
||||||
|
}
|
||||||
|
|
||||||
mozilla::ipc::IPCResult BrowserChild::RecvInsertText(
|
mozilla::ipc::IPCResult BrowserChild::RecvInsertText(
|
||||||
const nsAString& aStringToInsert) {
|
const nsAString& aStringToInsert) {
|
||||||
// Use normal event path to reach focused document.
|
// Use normal event path to reach focused document.
|
||||||
|
@ -400,6 +400,12 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
|
|||||||
mozilla::ipc::IPCResult RecvNormalPrioritySelectionEvent(
|
mozilla::ipc::IPCResult RecvNormalPrioritySelectionEvent(
|
||||||
const mozilla::WidgetSelectionEvent& aEvent);
|
const mozilla::WidgetSelectionEvent& aEvent);
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult RecvSimpleContentCommandEvent(
|
||||||
|
const mozilla::EventMessage& aMessage);
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult RecvNormalPrioritySimpleContentCommandEvent(
|
||||||
|
const mozilla::EventMessage& aMessage);
|
||||||
|
|
||||||
mozilla::ipc::IPCResult RecvInsertText(const nsAString& aStringToInsert);
|
mozilla::ipc::IPCResult RecvInsertText(const nsAString& aStringToInsert);
|
||||||
|
|
||||||
mozilla::ipc::IPCResult RecvUpdateRemoteStyle(
|
mozilla::ipc::IPCResult RecvUpdateRemoteStyle(
|
||||||
|
@ -3294,13 +3294,49 @@ bool BrowserParent::SendSelectionEvent(WidgetSelectionEvent& aEvent) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BrowserParent::SendInsertText(const nsString& aStringToInsert) {
|
bool BrowserParent::SendSimpleContentCommandEvent(
|
||||||
|
const mozilla::WidgetContentCommandEvent& aEvent) {
|
||||||
|
MOZ_ASSERT(aEvent.mMessage != eContentCommandInsertText);
|
||||||
|
MOZ_ASSERT(aEvent.mMessage != eContentCommandReplaceText);
|
||||||
|
MOZ_ASSERT(aEvent.mMessage != eContentCommandPasteTransferable);
|
||||||
|
MOZ_ASSERT(aEvent.mMessage != eContentCommandLookUpDictionary);
|
||||||
|
MOZ_ASSERT(aEvent.mMessage != eContentCommandScroll);
|
||||||
|
|
||||||
if (mIsDestroyed) {
|
if (mIsDestroyed) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
mContentCache.OnContentCommandEvent(aEvent);
|
||||||
return Manager()->IsInputPriorityEventEnabled()
|
return Manager()->IsInputPriorityEventEnabled()
|
||||||
? PBrowserParent::SendInsertText(aStringToInsert)
|
? PBrowserParent::SendSimpleContentCommandEvent(aEvent.mMessage)
|
||||||
: PBrowserParent::SendNormalPriorityInsertText(aStringToInsert);
|
: PBrowserParent::SendNormalPrioritySimpleContentCommandEvent(
|
||||||
|
aEvent.mMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserParent::SendInsertText(const WidgetContentCommandEvent& aEvent) {
|
||||||
|
if (mIsDestroyed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mContentCache.OnContentCommandEvent(aEvent);
|
||||||
|
return Manager()->IsInputPriorityEventEnabled()
|
||||||
|
? PBrowserParent::SendInsertText(aEvent.mString.ref())
|
||||||
|
: PBrowserParent::SendNormalPriorityInsertText(
|
||||||
|
aEvent.mString.ref());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserParent::SendReplaceText(const WidgetContentCommandEvent& aEvent) {
|
||||||
|
if (mIsDestroyed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mContentCache.OnContentCommandEvent(aEvent);
|
||||||
|
return Manager()->IsInputPriorityEventEnabled()
|
||||||
|
? PBrowserParent::SendReplaceText(
|
||||||
|
aEvent.mSelection.mReplaceSrcString, aEvent.mString.ref(),
|
||||||
|
aEvent.mSelection.mOffset,
|
||||||
|
aEvent.mSelection.mPreventSetSelection)
|
||||||
|
: PBrowserParent::SendNormalPriorityReplaceText(
|
||||||
|
aEvent.mSelection.mReplaceSrcString, aEvent.mString.ref(),
|
||||||
|
aEvent.mSelection.mOffset,
|
||||||
|
aEvent.mSelection.mPreventSetSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BrowserParent::SendPasteTransferable(IPCTransferable&& aTransferable) {
|
bool BrowserParent::SendPasteTransferable(IPCTransferable&& aTransferable) {
|
||||||
|
@ -613,7 +613,10 @@ class BrowserParent final : public PBrowserParent,
|
|||||||
|
|
||||||
bool HandleQueryContentEvent(mozilla::WidgetQueryContentEvent& aEvent);
|
bool HandleQueryContentEvent(mozilla::WidgetQueryContentEvent& aEvent);
|
||||||
|
|
||||||
bool SendInsertText(const nsString& aStringToInsert);
|
bool SendSimpleContentCommandEvent(
|
||||||
|
const mozilla::WidgetContentCommandEvent& aEvent);
|
||||||
|
bool SendInsertText(const mozilla::WidgetContentCommandEvent& aEvent);
|
||||||
|
bool SendReplaceText(const mozilla::WidgetContentCommandEvent& aEvent);
|
||||||
|
|
||||||
bool SendPasteTransferable(IPCTransferable&& aTransferable);
|
bool SendPasteTransferable(IPCTransferable&& aTransferable);
|
||||||
|
|
||||||
|
@ -849,6 +849,10 @@ child:
|
|||||||
[Priority=input] async SelectionEvent(WidgetSelectionEvent event);
|
[Priority=input] async SelectionEvent(WidgetSelectionEvent event);
|
||||||
async NormalPrioritySelectionEvent(WidgetSelectionEvent event);
|
async NormalPrioritySelectionEvent(WidgetSelectionEvent event);
|
||||||
|
|
||||||
|
|
||||||
|
[Priority=input] async SimpleContentCommandEvent(EventMessage message);
|
||||||
|
async NormalPrioritySimpleContentCommandEvent(EventMessage message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatch eContentCommandInsertText event in the remote process.
|
* Dispatch eContentCommandInsertText event in the remote process.
|
||||||
*/
|
*/
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "mozilla/IMEStateManager.h"
|
#include "mozilla/IMEStateManager.h"
|
||||||
#include "mozilla/IntegerPrintfMacros.h"
|
#include "mozilla/IntegerPrintfMacros.h"
|
||||||
#include "mozilla/Logging.h"
|
#include "mozilla/Logging.h"
|
||||||
|
#include "mozilla/MiscEvents.h"
|
||||||
#include "mozilla/RefPtr.h"
|
#include "mozilla/RefPtr.h"
|
||||||
#include "mozilla/TextComposition.h"
|
#include "mozilla/TextComposition.h"
|
||||||
#include "mozilla/dom/BrowserParent.h"
|
#include "mozilla/dom/BrowserParent.h"
|
||||||
@ -1419,6 +1420,28 @@ void ContentCacheInParent::OnSelectionEvent(
|
|||||||
mPendingSetSelectionEventNeedingAck++;
|
mPendingSetSelectionEventNeedingAck++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContentCacheInParent::OnContentCommandEvent(
|
||||||
|
const WidgetContentCommandEvent& aContentCommandEvent) {
|
||||||
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
||||||
|
("0x%p OnContentCommandEvent(aEvent={ "
|
||||||
|
"mMessage=%s, mString=\"%s\", mSelection={ mReplaceSrcString=\"%s\" "
|
||||||
|
"mOffset=%u, mPreventSetSelection=%s }, mOnlyEnabledCheck=%s })",
|
||||||
|
this, ToChar(aContentCommandEvent.mMessage),
|
||||||
|
ToString(aContentCommandEvent.mString).c_str(),
|
||||||
|
ToString(aContentCommandEvent.mSelection.mReplaceSrcString).c_str(),
|
||||||
|
aContentCommandEvent.mSelection.mOffset,
|
||||||
|
GetBoolName(aContentCommandEvent.mSelection.mPreventSetSelection),
|
||||||
|
GetBoolName(aContentCommandEvent.mOnlyEnabledCheck)));
|
||||||
|
|
||||||
|
MOZ_ASSERT(!aContentCommandEvent.mOnlyEnabledCheck);
|
||||||
|
|
||||||
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED && !defined(FUZZING_SNAPSHOT)
|
||||||
|
mDispatchedEventMessages.AppendElement(aContentCommandEvent.mMessage);
|
||||||
|
#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||||
|
|
||||||
|
mPendingContentCommandEventNeedingAck++;
|
||||||
|
}
|
||||||
|
|
||||||
void ContentCacheInParent::OnEventNeedingAckHandled(nsIWidget* aWidget,
|
void ContentCacheInParent::OnEventNeedingAckHandled(nsIWidget* aWidget,
|
||||||
EventMessage aMessage,
|
EventMessage aMessage,
|
||||||
uint32_t aCompositionId) {
|
uint32_t aCompositionId) {
|
||||||
@ -1588,6 +1611,21 @@ void ContentCacheInParent::OnEventNeedingAckHandled(nsIWidget* aWidget,
|
|||||||
} else {
|
} else {
|
||||||
mPendingSetSelectionEventNeedingAck--;
|
mPendingSetSelectionEventNeedingAck--;
|
||||||
}
|
}
|
||||||
|
} else if (aMessage >= eContentCommandEventFirst &&
|
||||||
|
aMessage <= eContentCommandEventLast) {
|
||||||
|
if (NS_WARN_IF(!mPendingContentCommandEventNeedingAck)) {
|
||||||
|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED && !defined(FUZZING_SNAPSHOT)
|
||||||
|
nsAutoCString info(
|
||||||
|
"\nThere is no pending content command events but received from the "
|
||||||
|
"remote child\n\n");
|
||||||
|
AppendEventMessageLog(info);
|
||||||
|
CrashReporter::AppendAppNotesToCrashReport(info);
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(
|
||||||
|
false, "No pending event message but received unexpected event");
|
||||||
|
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||||
|
} else {
|
||||||
|
mPendingContentCommandEventNeedingAck--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PendingEventsNeedingAck()) {
|
if (!PendingEventsNeedingAck()) {
|
||||||
|
@ -423,6 +423,13 @@ class ContentCacheInParent final : public ContentCache {
|
|||||||
*/
|
*/
|
||||||
void OnSelectionEvent(const WidgetSelectionEvent& aSelectionEvent);
|
void OnSelectionEvent(const WidgetSelectionEvent& aSelectionEvent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OnContentCommandEvent() should called before sending a content command
|
||||||
|
* event.
|
||||||
|
*/
|
||||||
|
void OnContentCommandEvent(
|
||||||
|
const WidgetContentCommandEvent& aContentCommandEvent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OnEventNeedingAckHandled() should be called after the child process
|
* OnEventNeedingAckHandled() should be called after the child process
|
||||||
* handles a sent event which needs acknowledging.
|
* handles a sent event which needs acknowledging.
|
||||||
@ -489,7 +496,8 @@ class ContentCacheInParent final : public ContentCache {
|
|||||||
// sent to the remote process, but we've not verified that the remote process
|
// sent to the remote process, but we've not verified that the remote process
|
||||||
// finished handling it.
|
// finished handling it.
|
||||||
[[nodiscard]] uint32_t PendingEventsNeedingAck() const {
|
[[nodiscard]] uint32_t PendingEventsNeedingAck() const {
|
||||||
uint32_t ret = mPendingSetSelectionEventNeedingAck;
|
uint32_t ret = mPendingSetSelectionEventNeedingAck +
|
||||||
|
mPendingContentCommandEventNeedingAck;
|
||||||
for (const HandlingCompositionData& data : mHandlingCompositions) {
|
for (const HandlingCompositionData& data : mHandlingCompositions) {
|
||||||
ret += data.mPendingEventsNeedingAck;
|
ret += data.mPendingEventsNeedingAck;
|
||||||
}
|
}
|
||||||
@ -599,6 +607,9 @@ class ContentCacheInParent final : public ContentCache {
|
|||||||
// process finished handling the events. Note that eSetSelection may be
|
// process finished handling the events. Note that eSetSelection may be
|
||||||
// dispatched without composition. Therefore, we need to count it with this.
|
// dispatched without composition. Therefore, we need to count it with this.
|
||||||
uint32_t mPendingSetSelectionEventNeedingAck = 0u;
|
uint32_t mPendingSetSelectionEventNeedingAck = 0u;
|
||||||
|
// Increased when sending WidgetContentCommandEvent events and decreased when
|
||||||
|
// the remote process finished handling them.
|
||||||
|
uint32_t mPendingContentCommandEventNeedingAck = 0u;
|
||||||
// mPendingCommitLength is commit string length of the first pending
|
// mPendingCommitLength is commit string length of the first pending
|
||||||
// composition. This is used by relative offset query events when querying
|
// composition. This is used by relative offset query events when querying
|
||||||
// new composition start offset.
|
// new composition start offset.
|
||||||
|
@ -358,6 +358,8 @@ NS_EVENT_MESSAGE(eContentCommandLookUpDictionary)
|
|||||||
// scrollable ancestor element can only be scrolled vertically, and horizontal
|
// scrollable ancestor element can only be scrolled vertically, and horizontal
|
||||||
// scrolling is requested using this event, no scrolling will occur.
|
// scrolling is requested using this event, no scrolling will occur.
|
||||||
NS_EVENT_MESSAGE(eContentCommandScroll)
|
NS_EVENT_MESSAGE(eContentCommandScroll)
|
||||||
|
NS_EVENT_MESSAGE_FIRST_LAST(eContentCommandEvent, eContentCommandCut,
|
||||||
|
eContentCommandScroll)
|
||||||
|
|
||||||
// Event to gesture notification
|
// Event to gesture notification
|
||||||
NS_EVENT_MESSAGE(eGestureNotify)
|
NS_EVENT_MESSAGE(eGestureNotify)
|
||||||
|
@ -78,9 +78,20 @@ class WidgetContentCommandEvent : public WidgetGUIEvent {
|
|||||||
bool mPreventSetSelection = false; // [in]
|
bool mPreventSetSelection = false; // [in]
|
||||||
} mSelection;
|
} mSelection;
|
||||||
|
|
||||||
|
// If set to true, the event checks whether the command is enabled in the
|
||||||
|
// process or not without executing the command. I.e., if it's in the parent
|
||||||
|
// process when a remote process has focus, mIsEnabled may be different from
|
||||||
|
// the latest state of the command in the remote process.
|
||||||
bool mOnlyEnabledCheck; // [in]
|
bool mOnlyEnabledCheck; // [in]
|
||||||
|
|
||||||
bool mSucceeded; // [out]
|
bool mSucceeded; // [out]
|
||||||
|
|
||||||
|
// If the command is enabled, set to true. Otherwise, false. This is set
|
||||||
|
// synchronously in the process. If it's in the parent process when a remote
|
||||||
|
// process has focus, this returns the command state in the parent process
|
||||||
|
// which may be different from the remote process.
|
||||||
|
// XXX When mOnlyEnabledCheck is set to true, this may be always set to true
|
||||||
|
// even when the command is disabled in the parent process.
|
||||||
bool mIsEnabled; // [out]
|
bool mIsEnabled; // [out]
|
||||||
|
|
||||||
void AssignContentCommandEventData(const WidgetContentCommandEvent& aEvent,
|
void AssignContentCommandEventData(const WidgetContentCommandEvent& aEvent,
|
||||||
|
@ -19,6 +19,8 @@ skip-if = [
|
|||||||
["browser_test_consumed_alt_char_on_windows.js"]
|
["browser_test_consumed_alt_char_on_windows.js"]
|
||||||
skip-if = ["os != 'win'"]
|
skip-if = ["os != 'win'"]
|
||||||
|
|
||||||
|
["browser_test_contentCommand_immediately_after_setSelection.js"]
|
||||||
|
|
||||||
["browser_test_fullscreen_size.js"]
|
["browser_test_fullscreen_size.js"]
|
||||||
|
|
||||||
["browser_test_ime_state_after_body_removed_and_reconnected_in_designMode.js"]
|
["browser_test_ime_state_after_body_removed_and_reconnected_in_designMode.js"]
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
add_task(async function () {
|
||||||
|
await BrowserTestUtils.withNewTab(
|
||||||
|
"data:text/html,<html><body><textarea></textarea></body></html>",
|
||||||
|
async function (browser) {
|
||||||
|
await SimpleTest.promiseFocus(browser);
|
||||||
|
|
||||||
|
for (const test of [
|
||||||
|
{ command: "cut" },
|
||||||
|
{ command: "delete" },
|
||||||
|
{ command: "insertText", data: "B" },
|
||||||
|
]) {
|
||||||
|
// ab[]c
|
||||||
|
await SpecialPowers.spawn(browser, [], () => {
|
||||||
|
const textarea = content.document.querySelector("textarea");
|
||||||
|
textarea.value = "abc";
|
||||||
|
textarea.focus();
|
||||||
|
textarea.selectionStart = "ab".length;
|
||||||
|
textarea.selectionEnd = textarea.selectionStart;
|
||||||
|
content.wrappedJSObject.promiseInput = new Promise(resolve =>
|
||||||
|
textarea.addEventListener("input", () => resolve(textarea.value), {
|
||||||
|
once: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// a[b]c
|
||||||
|
window.windowUtils.sendSelectionSetEvent("a".length, "b".length);
|
||||||
|
// a[]c or a[B]c
|
||||||
|
window.windowUtils.sendContentCommandEvent(
|
||||||
|
test.command,
|
||||||
|
null,
|
||||||
|
test.data
|
||||||
|
);
|
||||||
|
|
||||||
|
info("Waiting for input event on <textarea>...");
|
||||||
|
const textareaValue = await SpecialPowers.spawn(browser, [], () => {
|
||||||
|
return content.wrappedJSObject.promiseInput;
|
||||||
|
});
|
||||||
|
Assert.equal(
|
||||||
|
textareaValue,
|
||||||
|
`a${test.data || ""}c`,
|
||||||
|
`${test.command} immediately after setting selection should affect to the focused <textarea> in the remote process`
|
||||||
|
);
|
||||||
|
|
||||||
|
// wait for a while to flush content cache.
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user