mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1158456 - Remove control characters from composition string, and add dom.compositionevent.allow_control_characters pref to control it. r=masayuki
This commit is contained in:
parent
75cd0709f1
commit
e108ae29ec
@ -14,6 +14,7 @@
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/IMEStateManager.h"
|
||||
#include "mozilla/MiscEvents.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TextComposition.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
|
||||
@ -43,6 +44,9 @@ TextComposition::TextComposition(nsPresContext* aPresContext,
|
||||
, mIsRequestingCancel(false)
|
||||
, mRequestedToCommitOrCancel(false)
|
||||
, mWasNativeCompositionEndEventDiscarded(false)
|
||||
, mAllowControlCharacters(
|
||||
Preferences::GetBool("dom.compositionevent.allow_control_characters",
|
||||
false))
|
||||
{
|
||||
}
|
||||
|
||||
@ -133,6 +137,60 @@ TextComposition::OnCompositionEventDiscarded(
|
||||
mWasNativeCompositionEndEventDiscarded = true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsControlChar(uint32_t aCharCode)
|
||||
{
|
||||
return aCharCode < ' ' || aCharCode == 0x7F;
|
||||
}
|
||||
|
||||
static size_t
|
||||
FindFirstControlCharacter(const nsAString& aStr)
|
||||
{
|
||||
const char16_t* sourceBegin = aStr.BeginReading();
|
||||
const char16_t* sourceEnd = aStr.EndReading();
|
||||
|
||||
for (const char16_t* source = sourceBegin; source < sourceEnd; ++source) {
|
||||
if (*source != '\t' && IsControlChar(*source)) {
|
||||
return source - sourceBegin;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
RemoveControlCharactersFrom(nsAString& aStr, TextRangeArray* aRanges)
|
||||
{
|
||||
size_t firstControlCharOffset = FindFirstControlCharacter(aStr);
|
||||
if (firstControlCharOffset == (size_t)-1) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString copy(aStr);
|
||||
const char16_t* sourceBegin = copy.BeginReading();
|
||||
const char16_t* sourceEnd = copy.EndReading();
|
||||
|
||||
char16_t* dest = aStr.BeginWriting();
|
||||
if (NS_WARN_IF(!dest)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char16_t* curDest = dest + firstControlCharOffset;
|
||||
size_t i = firstControlCharOffset;
|
||||
for (const char16_t* source = sourceBegin + firstControlCharOffset;
|
||||
source < sourceEnd; ++source) {
|
||||
if (*source == '\t' || !IsControlChar(*source)) {
|
||||
*curDest = *source;
|
||||
++curDest;
|
||||
++i;
|
||||
} else if (aRanges) {
|
||||
aRanges->RemoveCharacter(i);
|
||||
}
|
||||
}
|
||||
|
||||
aStr.SetLength(curDest - dest);
|
||||
}
|
||||
|
||||
void
|
||||
TextComposition::DispatchCompositionEvent(
|
||||
WidgetCompositionEvent* aCompositionEvent,
|
||||
@ -140,6 +198,10 @@ TextComposition::DispatchCompositionEvent(
|
||||
EventDispatchingCallback* aCallBack,
|
||||
bool aIsSynthesized)
|
||||
{
|
||||
if (!mAllowControlCharacters) {
|
||||
RemoveControlCharactersFrom(aCompositionEvent->mData,
|
||||
aCompositionEvent->mRanges);
|
||||
}
|
||||
if (aCompositionEvent->message == NS_COMPOSITION_COMMIT_AS_IS) {
|
||||
NS_ASSERTION(!aCompositionEvent->mRanges,
|
||||
"mRanges of NS_COMPOSITION_COMMIT_AS_IS should be null");
|
||||
|
@ -221,6 +221,13 @@ private:
|
||||
// discarded by PresShell due to not safe to dispatch events.
|
||||
bool mWasNativeCompositionEndEventDiscarded;
|
||||
|
||||
// Allow control characters appear in composition string.
|
||||
// When this is false, control characters except
|
||||
// CHARACTER TABULATION (horizontal tab) are removed from
|
||||
// both composition string and data attribute of compositionupdate
|
||||
// and compositionend events.
|
||||
bool mAllowControlCharacters;
|
||||
|
||||
// Hide the default constructor and copy constructor.
|
||||
TextComposition() {}
|
||||
TextComposition(const TextComposition& aOther);
|
||||
|
@ -4713,3 +4713,10 @@ pref("gfx.vsync.refreshdriver", true);
|
||||
#ifdef MOZ_SECUREELEMENT
|
||||
pref("dom.secureelement.enabled", false);
|
||||
#endif
|
||||
|
||||
// Allow control characters appear in composition string.
|
||||
// When this is false, control characters except
|
||||
// CHARACTER TABULATION (horizontal tab) are removed from
|
||||
// both composition string and data attribute of compositionupdate
|
||||
// and compositionend events.
|
||||
pref("dom.compositionevent.allow_control_characters", false);
|
||||
|
@ -174,6 +174,16 @@ struct TextRange
|
||||
mRangeType == aOther.mRangeType &&
|
||||
mRangeStyle == aOther.mRangeStyle;
|
||||
}
|
||||
|
||||
void RemoveCharacter(uint32_t aOffset)
|
||||
{
|
||||
if (mStartOffset > aOffset) {
|
||||
--mStartOffset;
|
||||
--mEndOffset;
|
||||
} else if (mEndOffset > aOffset) {
|
||||
--mEndOffset;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
@ -223,6 +233,13 @@ public:
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveCharacter(uint32_t aOffset)
|
||||
{
|
||||
for (size_t i = 0, len = Length(); i < len; i++) {
|
||||
ElementAt(i).RemoveCharacter(aOffset);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -3186,6 +3186,99 @@ function runNotRedundantChangeTest()
|
||||
textarea.removeEventListener("text", handler, true);
|
||||
}
|
||||
|
||||
function runControlCharTest()
|
||||
{
|
||||
textarea.focus();
|
||||
|
||||
var result = {};
|
||||
function clearResult()
|
||||
{
|
||||
result = { compositionupdate: null, compositionend: null };
|
||||
}
|
||||
|
||||
function handler(aEvent)
|
||||
{
|
||||
result[aEvent.type] = aEvent.data;
|
||||
}
|
||||
|
||||
textarea.addEventListener("compositionupdate", handler, true);
|
||||
textarea.addEventListener("compositionend", handler, true);
|
||||
|
||||
textarea.value = "";
|
||||
|
||||
var controlChars = String.fromCharCode.apply(null, Object.keys(Array.from({length:0x20}))) + "\x7F";
|
||||
var allowedChars = "\t";
|
||||
|
||||
var data = "AB" + controlChars + "CD" + controlChars + "EF";
|
||||
var removedData = "AB" + allowedChars + "CD" + allowedChars + "EF";
|
||||
|
||||
var DIndex = data.indexOf("D");
|
||||
var removedDIndex = removedData.indexOf("D");
|
||||
|
||||
// input string contains control characters
|
||||
clearResult();
|
||||
synthesizeCompositionChange(
|
||||
{ "composition":
|
||||
{ "string": data,
|
||||
"clauses":
|
||||
[
|
||||
{ "length": DIndex,
|
||||
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
||||
{ "length": data.length - DIndex,
|
||||
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
|
||||
]
|
||||
},
|
||||
"caret": { "start": DIndex, "length": 0 }
|
||||
});
|
||||
|
||||
checkSelection(removedDIndex, "", "runControlCharTest", "#1")
|
||||
|
||||
is(result.compositionupdate, removedData, "runControlCharTest: control characters in event.data should be removed in compositionupdate event #1");
|
||||
is(textarea.value, removedData, "runControlCharTest: control characters should not appear in textarea #1");
|
||||
|
||||
synthesizeComposition({ type: "compositioncommit", data: data });
|
||||
|
||||
is(result.compositionend, removedData, "runControlCharTest: control characters in event.data should be removed in compositionend event #2");
|
||||
is(textarea.value, removedData, "runControlCharTest: control characters should not appear in textarea #2");
|
||||
|
||||
textarea.value = "";
|
||||
|
||||
clearResult();
|
||||
|
||||
SpecialPowers.setBoolPref("dom.compositionevent.allow_control_characters", true);
|
||||
|
||||
// input string contains control characters, allowing control characters
|
||||
clearResult();
|
||||
synthesizeCompositionChange(
|
||||
{ "composition":
|
||||
{ "string": data,
|
||||
"clauses":
|
||||
[
|
||||
{ "length": DIndex,
|
||||
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
||||
{ "length": data.length - DIndex,
|
||||
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
|
||||
]
|
||||
},
|
||||
"caret": { "start": DIndex, "length": 0 }
|
||||
});
|
||||
|
||||
checkSelection(DIndex - 1 + kLFLen, "", "runControlCharTest", "#3")
|
||||
|
||||
is(result.compositionupdate, data, "runControlCharTest: control characters in event.data should not be removed in compositionupdate event #3");
|
||||
is(textarea.value, data.replace(/\r/g, "\n"), "runControlCharTest: control characters should appear in textarea #3");
|
||||
|
||||
synthesizeComposition({ type: "compositioncommit", data: data });
|
||||
|
||||
is(result.compositionend, data, "runControlCharTest: control characters in event.data should not be removed in compositionend event #4");
|
||||
is(textarea.value, data.replace(/\r/g, "\n"), "runControlCharTest: control characters should appear in textarea #4");
|
||||
|
||||
SpecialPowers.clearUserPref("dom.compositionevent.allow_control_characters");
|
||||
|
||||
textarea.removeEventListener("compositionupdate", handler, true);
|
||||
textarea.removeEventListener("compositionend", handler, true);
|
||||
}
|
||||
|
||||
function runRemoveContentTest(aCallback)
|
||||
{
|
||||
var events = [];
|
||||
@ -3760,6 +3853,7 @@ function runTest()
|
||||
runIsComposingTest();
|
||||
runRedundantChangeTest();
|
||||
runNotRedundantChangeTest();
|
||||
runControlCharTest();
|
||||
runAsyncForceCommitTest(function () {
|
||||
runRemoveContentTest(function () {
|
||||
runFrameTest();
|
||||
|
Loading…
Reference in New Issue
Block a user