mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-12 10:40:12 +00:00
Bug 1391538 - nsTextFragment for text nodes in <input> or <textarea> shouldn't store text as single byte characters even if all characters are less than U+0100 r=smaug
nsTextFrame stores text as single byte character array if all characters are less than U+0100. Although, this saves footprint, but retrieving and modifying text needs converting cost. Therefore, if it's created for a text node in <input> or <textarea>, it should store text as char16_t array. MozReview-Commit-ID: 9Z82rketT7g --HG-- extra : rebase_source : 59f59ac1488c21a57d95d253cc794a011d672c95
This commit is contained in:
parent
cb018b1b7f
commit
a7240d8532
@ -5902,6 +5902,13 @@ nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsTextNode>
|
||||
nsIDocument::CreateEmptyTextNode() const
|
||||
{
|
||||
RefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
|
||||
return text.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsTextNode>
|
||||
nsIDocument::CreateTextNode(const nsAString& aData) const
|
||||
{
|
||||
|
@ -319,12 +319,18 @@ nsGenericDOMDataNode::SetTextInternal(uint32_t aOffset, uint32_t aCount,
|
||||
if (aOffset == 0 && endOffset == textLength) {
|
||||
// Replacing whole text or old text was empty. Don't bother to check for
|
||||
// bidi in this string if the document already has bidi enabled.
|
||||
bool ok = mText.SetTo(aBuffer, aLength, !document || !document->GetBidiEnabled());
|
||||
// If this is marked as "maybe modified frequently", the text should be
|
||||
// stored as char16_t since converting char* to char16_t* is expensive.
|
||||
bool ok =
|
||||
mText.SetTo(aBuffer, aLength, !document || !document->GetBidiEnabled(),
|
||||
HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY));
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
else if (aOffset == textLength) {
|
||||
// Appending to existing
|
||||
bool ok = mText.Append(aBuffer, aLength, !document || !document->GetBidiEnabled());
|
||||
bool ok =
|
||||
mText.Append(aBuffer, aLength, !document || !document->GetBidiEnabled(),
|
||||
HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY));
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
else {
|
||||
@ -345,7 +351,11 @@ nsGenericDOMDataNode::SetTextInternal(uint32_t aOffset, uint32_t aCount,
|
||||
mText.CopyTo(to + aOffset + aLength, endOffset, textLength - endOffset);
|
||||
}
|
||||
|
||||
bool ok = mText.SetTo(to, newLength, !document || !document->GetBidiEnabled());
|
||||
// If this is marked as "maybe modified frequently", the text should be
|
||||
// stored as char16_t since converting char* to char16_t* is expensive.
|
||||
bool ok =
|
||||
mText.SetTo(to, newLength, !document || !document->GetBidiEnabled(),
|
||||
HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY));
|
||||
|
||||
delete [] to;
|
||||
|
||||
|
@ -55,10 +55,14 @@ enum {
|
||||
// This bit is set if there is a FlowLengthProperty attached to the node
|
||||
// (used by nsTextFrame).
|
||||
NS_HAS_FLOWLENGTH_PROPERTY = DATA_NODE_FLAG_BIT(5),
|
||||
|
||||
// This bit is set if the node may be modified frequently. This is typically
|
||||
// specified if the instance is in <input> or <textarea>.
|
||||
NS_MAYBE_MODIFIED_FREQUENTLY = DATA_NODE_FLAG_BIT(6),
|
||||
};
|
||||
|
||||
// Make sure we have enough space for those bits
|
||||
ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET + 6);
|
||||
ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET + 7);
|
||||
|
||||
#undef DATA_NODE_FLAG_BIT
|
||||
|
||||
@ -72,6 +76,11 @@ public:
|
||||
explicit nsGenericDOMDataNode(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
|
||||
explicit nsGenericDOMDataNode(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
|
||||
|
||||
void MarkAsMaybeModifiedFrequently()
|
||||
{
|
||||
SetFlags(NS_MAYBE_MODIFIED_FREQUENTLY);
|
||||
}
|
||||
|
||||
virtual void GetNodeValueInternal(nsAString& aNodeValue) override;
|
||||
virtual void SetNodeValueInternal(const nsAString& aNodeValue,
|
||||
mozilla::ErrorResult& aError) override;
|
||||
|
@ -2740,6 +2740,7 @@ public:
|
||||
already_AddRefed<mozilla::dom::DocumentFragment>
|
||||
CreateDocumentFragment() const;
|
||||
already_AddRefed<nsTextNode> CreateTextNode(const nsAString& aData) const;
|
||||
already_AddRefed<nsTextNode> CreateEmptyTextNode() const;
|
||||
already_AddRefed<mozilla::dom::Comment>
|
||||
CreateComment(const nsAString& aData) const;
|
||||
already_AddRefed<mozilla::dom::ProcessingInstruction>
|
||||
|
@ -196,7 +196,8 @@ FirstNon8Bit(const char16_t *str, const char16_t *end)
|
||||
}
|
||||
|
||||
bool
|
||||
nsTextFragment::SetTo(const char16_t* aBuffer, int32_t aLength, bool aUpdateBidi)
|
||||
nsTextFragment::SetTo(const char16_t* aBuffer, int32_t aLength,
|
||||
bool aUpdateBidi, bool aForce2b)
|
||||
{
|
||||
ReleaseText();
|
||||
|
||||
@ -205,7 +206,7 @@ nsTextFragment::SetTo(const char16_t* aBuffer, int32_t aLength, bool aUpdateBidi
|
||||
}
|
||||
|
||||
char16_t firstChar = *aBuffer;
|
||||
if (aLength == 1 && firstChar < 256) {
|
||||
if (!aForce2b && aLength == 1 && firstChar < 256) {
|
||||
m1b = sSingleCharSharedString + firstChar;
|
||||
mState.mInHeap = false;
|
||||
mState.mIs2b = false;
|
||||
@ -218,7 +219,8 @@ nsTextFragment::SetTo(const char16_t* aBuffer, int32_t aLength, bool aUpdateBidi
|
||||
const char16_t *uend = aBuffer + aLength;
|
||||
|
||||
// Check if we can use a shared string
|
||||
if (aLength <= 1 + TEXTFRAG_WHITE_AFTER_NEWLINE + TEXTFRAG_MAX_NEWLINES &&
|
||||
if (!aForce2b &&
|
||||
aLength <= 1 + TEXTFRAG_WHITE_AFTER_NEWLINE + TEXTFRAG_MAX_NEWLINES &&
|
||||
(firstChar == ' ' || firstChar == '\n' || firstChar == '\t')) {
|
||||
if (firstChar == ' ') {
|
||||
++ucp;
|
||||
@ -255,7 +257,7 @@ nsTextFragment::SetTo(const char16_t* aBuffer, int32_t aLength, bool aUpdateBidi
|
||||
}
|
||||
|
||||
// See if we need to store the data in ucs2 or not
|
||||
int32_t first16bit = FirstNon8Bit(ucp, uend);
|
||||
int32_t first16bit = aForce2b ? 0 : FirstNon8Bit(ucp, uend);
|
||||
|
||||
if (first16bit != -1) { // aBuffer contains no non-8bit character
|
||||
// Use ucs2 storage because we have to
|
||||
@ -324,12 +326,13 @@ nsTextFragment::CopyTo(char16_t *aDest, int32_t aOffset, int32_t aCount)
|
||||
}
|
||||
|
||||
bool
|
||||
nsTextFragment::Append(const char16_t* aBuffer, uint32_t aLength, bool aUpdateBidi)
|
||||
nsTextFragment::Append(const char16_t* aBuffer, uint32_t aLength,
|
||||
bool aUpdateBidi, bool aForce2b)
|
||||
{
|
||||
// This is a common case because some callsites create a textnode
|
||||
// with a value by creating the node and then calling AppendData.
|
||||
if (mState.mLength == 0) {
|
||||
return SetTo(aBuffer, aLength, aUpdateBidi);
|
||||
return SetTo(aBuffer, aLength, aUpdateBidi, aForce2b);
|
||||
}
|
||||
|
||||
// Should we optimize for aData.Length() == 0?
|
||||
@ -365,7 +368,7 @@ nsTextFragment::Append(const char16_t* aBuffer, uint32_t aLength, bool aUpdateBi
|
||||
}
|
||||
|
||||
// Current string is a 1-byte string, check if the new data fits in one byte too.
|
||||
int32_t first16bit = FirstNon8Bit(aBuffer, aBuffer + aLength);
|
||||
int32_t first16bit = aForce2b ? 0 : FirstNon8Bit(aBuffer, aBuffer + aLength);
|
||||
|
||||
if (first16bit != -1) { // aBuffer contains no non-8bit character
|
||||
length *= sizeof(char16_t);
|
||||
|
@ -112,15 +112,23 @@ public:
|
||||
* Change the contents of this fragment to be a copy of the given
|
||||
* buffer. If aUpdateBidi is true, contents of the fragment will be scanned,
|
||||
* and mState.mIsBidi will be turned on if it includes any Bidi characters.
|
||||
* If aForce2b is true, aBuffer will be stored as char16_t as is. Then,
|
||||
* you can access the value faster but may waste memory if all characters
|
||||
* are less than U+0100.
|
||||
*/
|
||||
bool SetTo(const char16_t* aBuffer, int32_t aLength, bool aUpdateBidi);
|
||||
bool SetTo(const char16_t* aBuffer, int32_t aLength, bool aUpdateBidi,
|
||||
bool aForce2b);
|
||||
|
||||
/**
|
||||
* Append aData to the end of this fragment. If aUpdateBidi is true, contents
|
||||
* of the fragment will be scanned, and mState.mIsBidi will be turned on if
|
||||
* it includes any Bidi characters.
|
||||
* If aForce2b is true, the string will be stored as char16_t as is. Then,
|
||||
* you can access the value faster but may waste memory if all characters
|
||||
* are less than U+0100.
|
||||
*/
|
||||
bool Append(const char16_t* aBuffer, uint32_t aLength, bool aUpdateBidi);
|
||||
bool Append(const char16_t* aBuffer, uint32_t aLength, bool aUpdateBidi,
|
||||
bool aForce2b);
|
||||
|
||||
/**
|
||||
* Append the contents of this string fragment to aString
|
||||
|
@ -2359,6 +2359,7 @@ nsTextEditorState::CreateEmptyDivNode()
|
||||
|
||||
// Create the text node for DIV
|
||||
RefPtr<nsTextNode> textNode = new nsTextNode(pNodeInfoManager);
|
||||
textNode->MarkAsMaybeModifiedFrequently();
|
||||
|
||||
element->AppendChildTo(textNode, false);
|
||||
|
||||
|
@ -2514,7 +2514,8 @@ EditorBase::InsertTextImpl(const nsAString& aStringToInsert,
|
||||
CheckedInt<int32_t> newOffset;
|
||||
if (!node->IsNodeOfType(nsINode::eTEXT)) {
|
||||
// create a text node
|
||||
RefPtr<nsTextNode> newNode = aDoc->CreateTextNode(EmptyString());
|
||||
RefPtr<nsTextNode> newNode =
|
||||
EditorBase::CreateTextNode(*aDoc, EmptyString());
|
||||
// then we insert it into the dom tree
|
||||
nsresult rv = InsertNode(*newNode, *node, offset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -2541,7 +2542,8 @@ EditorBase::InsertTextImpl(const nsAString& aStringToInsert,
|
||||
} else {
|
||||
// we are inserting text into a non-text node. first we have to create a
|
||||
// textnode (this also populates it with the text)
|
||||
RefPtr<nsTextNode> newNode = aDoc->CreateTextNode(aStringToInsert);
|
||||
RefPtr<nsTextNode> newNode =
|
||||
EditorBase::CreateTextNode(*aDoc, aStringToInsert);
|
||||
// then we insert it into the dom tree
|
||||
nsresult rv = InsertNode(*newNode, *node, offset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -4730,6 +4732,18 @@ EditorBase::CreateHTMLContent(nsIAtom* aTag)
|
||||
kNameSpaceID_XHTML);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<nsTextNode>
|
||||
EditorBase::CreateTextNode(nsIDocument& aDocument,
|
||||
const nsAString& aData)
|
||||
{
|
||||
RefPtr<nsTextNode> text = aDocument.CreateEmptyTextNode();
|
||||
text->MarkAsMaybeModifiedFrequently();
|
||||
// Don't notify; this node is still being created.
|
||||
text->SetText(aData, false);
|
||||
return text.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditorBase::SetAttributeOrEquivalent(nsIDOMElement* aElement,
|
||||
const nsAString& aAttribute,
|
||||
|
@ -348,6 +348,12 @@ public:
|
||||
*/
|
||||
already_AddRefed<Element> CreateHTMLContent(nsIAtom* aTag);
|
||||
|
||||
/**
|
||||
* Creates text node which is marked as "maybe modified frequently".
|
||||
*/
|
||||
static already_AddRefed<nsTextNode> CreateTextNode(nsIDocument& aDocument,
|
||||
const nsAString& aData);
|
||||
|
||||
/**
|
||||
* IME event handlers.
|
||||
*/
|
||||
|
@ -4613,7 +4613,8 @@ HTMLEditRules::CreateStyleForInsertText(Selection& aSelection,
|
||||
if (!mHTMLEditor->IsContainer(node)) {
|
||||
return NS_OK;
|
||||
}
|
||||
OwningNonNull<Text> newNode = aDoc.CreateTextNode(EmptyString());
|
||||
OwningNonNull<Text> newNode =
|
||||
EditorBase::CreateTextNode(aDoc, EmptyString());
|
||||
NS_ENSURE_STATE(mHTMLEditor);
|
||||
nsresult rv = mHTMLEditor->InsertNode(*newNode, *node, offset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -878,7 +878,7 @@ TextEditRules::WillSetText(Selection& aSelection,
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return NS_OK;
|
||||
}
|
||||
RefPtr<nsTextNode> newNode = doc->CreateTextNode(tString);
|
||||
RefPtr<nsTextNode> newNode = EditorBase::CreateTextNode(*doc, tString);
|
||||
if (NS_WARN_IF(!newNode)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1171,6 +1171,7 @@ nsTextControlFrame::UpdateValueDisplay(bool aNotify,
|
||||
// Set up a textnode with our value
|
||||
RefPtr<nsTextNode> textNode =
|
||||
new nsTextNode(mContent->NodeInfo()->NodeInfoManager());
|
||||
textNode->MarkAsMaybeModifiedFrequently();
|
||||
|
||||
NS_ASSERTION(textNode, "Must have textcontent!\n");
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user