зеркало из https://github.com/mozilla/pjs.git
Bug 329974: Share textdata for common textnode values. r/sr=jst
This commit is contained in:
Родитель
3ec83cda52
Коммит
ae80239e8f
|
@ -82,14 +82,10 @@ public:
|
|||
* Set the text to the given value. If aNotify is PR_TRUE then
|
||||
* the document is notified of the content change.
|
||||
*/
|
||||
virtual void SetText(const nsAString& aStr, PRBool aNotify) = 0;
|
||||
|
||||
/**
|
||||
* Set the text to the given value. If aNotify is PR_TRUE then
|
||||
* the document is notified of the content change.
|
||||
*/
|
||||
virtual void SetText(const char* aBuffer, PRUint32 aLength,
|
||||
PRBool aNotify) = 0;
|
||||
void SetText(const nsAString& aStr, PRBool aNotify)
|
||||
{
|
||||
return SetText(aStr.BeginReading(), aStr.Length(), aNotify);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query method to see if the frame is nothing but whitespace
|
||||
|
|
|
@ -439,30 +439,37 @@ nsGenericDOMDataNode::SubstringData(PRUint32 aStart, PRUint32 aCount,
|
|||
nsresult
|
||||
nsGenericDOMDataNode::AppendData(const nsAString& aData)
|
||||
{
|
||||
PRInt32 length = 0;
|
||||
// Apparently this is called often enough that we don't want to just simply
|
||||
// call SetText like ReplaceData does. See bug 77585 and comment in
|
||||
// ReplaceData.
|
||||
nsIDocument *document = GetCurrentDoc();
|
||||
|
||||
// FIXME, but 330872: We can't call BeginUpdate here because it confuses the
|
||||
// poor little nsHTMLContentSink.
|
||||
// mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, PR_TRUE);
|
||||
|
||||
// See bugzilla bug 77585.
|
||||
if (mText.Is2b() || (!IsASCII(aData))) {
|
||||
nsAutoString old_data;
|
||||
mText.AppendTo(old_data);
|
||||
length = old_data.Length();
|
||||
// XXXjag We'd like to just say |old_data + aData|, but due
|
||||
// to issues with dependent concatenation and sliding (sub)strings
|
||||
// we'll just have to copy for now. See bug 121841 for details.
|
||||
old_data.Append(aData);
|
||||
SetText(old_data, PR_FALSE);
|
||||
} else {
|
||||
// We know aData and the current data is ASCII, so use a
|
||||
// nsC*String, no need for any fancy unicode stuff here.
|
||||
nsCAutoString old_data;
|
||||
mText.AppendTo(old_data);
|
||||
length = old_data.Length();
|
||||
LossyAppendUTF16toASCII(aData, old_data);
|
||||
SetText(old_data.get(), old_data.Length(), PR_FALSE);
|
||||
PRBool haveMutationListeners =
|
||||
document && nsGenericElement::HasMutationListeners(this, NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED);
|
||||
|
||||
nsCOMPtr<nsIAtom> oldValue;
|
||||
if (haveMutationListeners) {
|
||||
oldValue = GetCurrentValueAtom();
|
||||
}
|
||||
|
||||
mText.Append(aData);
|
||||
|
||||
SetBidiStatus();
|
||||
|
||||
if (haveMutationListeners) {
|
||||
nsMutationEvent mutation(PR_TRUE, NS_MUTATION_CHARACTERDATAMODIFIED);
|
||||
|
||||
mutation.mPrevAttrValue = oldValue;
|
||||
mutation.mNewAttrValue = GetCurrentValueAtom();
|
||||
|
||||
nsEventDispatcher::Dispatch(this, nsnull, &mutation);
|
||||
}
|
||||
|
||||
// Trigger a reflow
|
||||
nsIDocument *document = GetCurrentDoc();
|
||||
if (document) {
|
||||
document->CharacterDataChanged(this, PR_TRUE);
|
||||
}
|
||||
|
@ -1086,110 +1093,26 @@ nsGenericDOMDataNode::SetText(const PRUnichar* aBuffer,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericDOMDataNode::SetText(const char* aBuffer, PRUint32 aLength,
|
||||
PRBool aNotify)
|
||||
{
|
||||
if (!aBuffer) {
|
||||
NS_ERROR("Null buffer passed to SetText()!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
nsIDocument *document = GetCurrentDoc();
|
||||
mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
|
||||
|
||||
PRBool haveMutationListeners =
|
||||
document && nsGenericElement::HasMutationListeners(this, NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED);
|
||||
|
||||
nsCOMPtr<nsIAtom> oldValue;
|
||||
if (haveMutationListeners) {
|
||||
oldValue = GetCurrentValueAtom();
|
||||
}
|
||||
|
||||
mText.SetTo(aBuffer, aLength);
|
||||
|
||||
if (haveMutationListeners) {
|
||||
nsMutationEvent mutation(PR_TRUE, NS_MUTATION_CHARACTERDATAMODIFIED);
|
||||
|
||||
mutation.mPrevAttrValue = oldValue;
|
||||
if (aLength > 0) {
|
||||
// Must use Substring() since nsDependentCString() requires null
|
||||
// terminated strings.
|
||||
mutation.mNewAttrValue =
|
||||
do_GetAtom(Substring(aBuffer, aBuffer + aLength));
|
||||
}
|
||||
|
||||
nsEventDispatcher::Dispatch(this, nsnull, &mutation);
|
||||
}
|
||||
|
||||
// Trigger a reflow
|
||||
if (aNotify && document) {
|
||||
document->CharacterDataChanged(this, PR_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericDOMDataNode::SetText(const nsAString& aStr,
|
||||
PRBool aNotify)
|
||||
{
|
||||
nsIDocument *document = GetCurrentDoc();
|
||||
mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
|
||||
|
||||
PRBool haveMutationListeners =
|
||||
document && nsGenericElement::HasMutationListeners(this, NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED);
|
||||
|
||||
nsCOMPtr<nsIAtom> oldValue;
|
||||
if (haveMutationListeners) {
|
||||
oldValue = GetCurrentValueAtom();
|
||||
}
|
||||
|
||||
mText = aStr;
|
||||
|
||||
SetBidiStatus();
|
||||
|
||||
if (haveMutationListeners) {
|
||||
nsMutationEvent mutation(PR_TRUE, NS_MUTATION_CHARACTERDATAMODIFIED);
|
||||
|
||||
mutation.mPrevAttrValue = oldValue;
|
||||
if (!aStr.IsEmpty())
|
||||
mutation.mNewAttrValue = do_GetAtom(aStr);
|
||||
nsEventDispatcher::Dispatch(this, nsnull, &mutation);
|
||||
}
|
||||
|
||||
// Trigger a reflow
|
||||
if (aNotify && document) {
|
||||
document->CharacterDataChanged(this, PR_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsGenericDOMDataNode::IsOnlyWhitespace()
|
||||
{
|
||||
nsTextFragment& frag = mText;
|
||||
if (frag.Is2b()) {
|
||||
const PRUnichar* cp = frag.Get2b();
|
||||
const PRUnichar* end = cp + frag.GetLength();
|
||||
if (mText.Is2b()) {
|
||||
// The fragment contains non-8bit characters and such characters
|
||||
// are never considered whitespace.
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
while (cp < end) {
|
||||
PRUnichar ch = *cp++;
|
||||
const char* cp = mText.Get1b();
|
||||
const char* end = cp + mText.GetLength();
|
||||
|
||||
if (!XP_IS_SPACE(ch)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
while (cp < end) {
|
||||
char ch = *cp;
|
||||
|
||||
if (!XP_IS_SPACE(ch)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
} else {
|
||||
const char* cp = frag.Get1b();
|
||||
const char* end = cp + frag.GetLength();
|
||||
|
||||
while (cp < end) {
|
||||
PRUnichar ch = PRUnichar(*(unsigned char*)cp);
|
||||
++cp;
|
||||
|
||||
if (!XP_IS_SPACE(ch)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
++cp;
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
|
|
|
@ -270,9 +270,11 @@ public:
|
|||
virtual PRUint32 TextLength();
|
||||
virtual void SetText(const PRUnichar* aBuffer, PRUint32 aLength,
|
||||
PRBool aNotify);
|
||||
virtual void SetText(const nsAString& aStr, PRBool aNotify);
|
||||
virtual void SetText(const char* aBuffer, PRUint32 aLength,
|
||||
PRBool aNotify);
|
||||
// Need to implement this here too to avoid hiding.
|
||||
void SetText(const nsAString& aStr, PRBool aNotify)
|
||||
{
|
||||
return SetText(aStr.BeginReading(), aStr.Length(), aNotify);
|
||||
}
|
||||
virtual PRBool IsOnlyWhitespace();
|
||||
virtual void AppendTextTo(nsAString& aResult);
|
||||
|
||||
|
|
|
@ -42,9 +42,58 @@
|
|||
#include "nsBidiUtils.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
|
||||
// Static buffer used for newline fragments
|
||||
static unsigned char sNewLineCharacter = '\n';
|
||||
#define TEXTFRAG_WHITE_AFTER_NEWLINE 50
|
||||
#define TEXTFRAG_MAX_NEWLINES 7
|
||||
|
||||
// Static buffer used for common fragments
|
||||
static char* sSpaceSharedString[TEXTFRAG_MAX_NEWLINES + 1];
|
||||
static char* sTabSharedString[TEXTFRAG_MAX_NEWLINES + 1];
|
||||
static char sSingleCharSharedString[256];
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsTextFragment::Init()
|
||||
{
|
||||
// Create whitespace strings
|
||||
PRUint32 i;
|
||||
for (i = 0; i <= TEXTFRAG_MAX_NEWLINES; ++i) {
|
||||
sSpaceSharedString[i] = new char[1 + i + TEXTFRAG_WHITE_AFTER_NEWLINE];
|
||||
sTabSharedString[i] = new char[1 + i + TEXTFRAG_WHITE_AFTER_NEWLINE];
|
||||
NS_ENSURE_TRUE(sSpaceSharedString[i] && sTabSharedString[i],
|
||||
NS_ERROR_OUT_OF_MEMORY);
|
||||
sSpaceSharedString[i][0] = ' ';
|
||||
sTabSharedString[i][0] = ' ';
|
||||
PRUint32 j;
|
||||
for (j = 1; j < 1 + i; ++j) {
|
||||
sSpaceSharedString[i][j] = '\n';
|
||||
sTabSharedString[i][j] = '\n';
|
||||
}
|
||||
for (; j < (1 + i + TEXTFRAG_WHITE_AFTER_NEWLINE); ++j) {
|
||||
sSpaceSharedString[i][j] = ' ';
|
||||
sTabSharedString[i][j] = '\t';
|
||||
}
|
||||
}
|
||||
|
||||
// Create single-char strings
|
||||
for (i = 0; i < 256; ++i) {
|
||||
sSingleCharSharedString[i] = i;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsTextFragment::Shutdown()
|
||||
{
|
||||
PRUint32 i;
|
||||
for (i = 0; i <= TEXTFRAG_MAX_NEWLINES; ++i) {
|
||||
delete [] sSpaceSharedString[i];
|
||||
delete [] sTabSharedString[i];
|
||||
sSpaceSharedString[i] = nsnull;
|
||||
sTabSharedString[i] = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
nsTextFragment::~nsTextFragment()
|
||||
{
|
||||
|
@ -55,9 +104,7 @@ void
|
|||
nsTextFragment::ReleaseText()
|
||||
{
|
||||
if (mState.mLength && m1b && mState.mInHeap) {
|
||||
unsigned char *buf = NS_CONST_CAST(unsigned char *, m1b);
|
||||
|
||||
nsMemory::Free(buf); // m1b == m2b as far as nsMemory is concerned
|
||||
nsMemory::Free(m2b); // m1b == m2b as far as nsMemory is concerned
|
||||
}
|
||||
|
||||
m1b = nsnull;
|
||||
|
@ -66,204 +113,129 @@ nsTextFragment::ReleaseText()
|
|||
mAllBits = 0;
|
||||
}
|
||||
|
||||
nsTextFragment::nsTextFragment(const nsTextFragment& aOther)
|
||||
: m1b(nsnull), mAllBits(0)
|
||||
{
|
||||
if (aOther.Is2b()) {
|
||||
SetTo(aOther.Get2b(), aOther.GetLength());
|
||||
} else {
|
||||
SetTo(aOther.Get1b(), aOther.GetLength());
|
||||
}
|
||||
}
|
||||
|
||||
nsTextFragment::nsTextFragment(const char *aString)
|
||||
: m1b(nsnull), mAllBits(0)
|
||||
{
|
||||
SetTo(aString, strlen(aString));
|
||||
}
|
||||
|
||||
nsTextFragment::nsTextFragment(const PRUnichar *aString)
|
||||
: m1b(nsnull), mAllBits(0)
|
||||
{
|
||||
SetTo(aString, nsCRT::strlen(aString));
|
||||
}
|
||||
|
||||
nsTextFragment::nsTextFragment(const nsString& aString)
|
||||
: m1b(nsnull), mAllBits(0)
|
||||
{
|
||||
SetTo(aString.get(), aString.Length());
|
||||
}
|
||||
|
||||
nsTextFragment&
|
||||
nsTextFragment::operator=(const nsTextFragment& aOther)
|
||||
{
|
||||
if (aOther.Is2b()) {
|
||||
SetTo(aOther.Get2b(), aOther.GetLength());
|
||||
} else {
|
||||
SetTo(aOther.Get1b(), aOther.GetLength());
|
||||
}
|
||||
|
||||
if (aOther.mState.mIsBidi) {
|
||||
// Carry over BIDI state from aOther
|
||||
mState.mIsBidi = PR_TRUE;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
nsTextFragment&
|
||||
nsTextFragment::operator=(const char *aString)
|
||||
{
|
||||
SetTo(aString, strlen(aString));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
nsTextFragment&
|
||||
nsTextFragment::operator=(const PRUnichar *aString)
|
||||
{
|
||||
SetTo(aString, nsCRT::strlen(aString));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
nsTextFragment&
|
||||
nsTextFragment::operator=(const nsAString& aString)
|
||||
{
|
||||
ReleaseText();
|
||||
|
||||
PRUint32 length = aString.Length();
|
||||
if (aOther.mState.mLength) {
|
||||
if (!aOther.mState.mInHeap) {
|
||||
m1b = aOther.m1b; // This will work even if aOther is using m2b
|
||||
}
|
||||
else {
|
||||
m2b = NS_STATIC_CAST(PRUnichar*,
|
||||
nsMemory::Clone(aOther.m2b, aOther.mState.mLength *
|
||||
(aOther.mState.mIs2b ? sizeof(PRUnichar) : sizeof(char)));
|
||||
|
||||
if (length > 0) {
|
||||
PRBool in_heap = PR_TRUE;
|
||||
|
||||
if (IsASCII(aString)) {
|
||||
if (length == 1 && aString.First() == '\n') {
|
||||
m1b = &sNewLineCharacter;
|
||||
|
||||
in_heap = PR_FALSE;
|
||||
} else {
|
||||
m1b = (unsigned char *)ToNewCString(aString);
|
||||
if (aOther.mState.mIs2b) {
|
||||
}
|
||||
else {
|
||||
m1b = NS_STATIC_CAST(char *,
|
||||
nsMemory::Clone(aOther.m1b, aOther.mState.mLength * sizeof(char)));
|
||||
}
|
||||
|
||||
mState.mIs2b = PR_FALSE;
|
||||
} else {
|
||||
m2b = ToNewUnicode(aString);
|
||||
mState.mIs2b = PR_TRUE;
|
||||
}
|
||||
|
||||
mState.mInHeap = in_heap;
|
||||
mState.mLength = length;
|
||||
if (m1b) {
|
||||
mAllBits = aOther.mAllBits;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
nsTextFragment::SetTo(PRUnichar *aBuffer, PRInt32 aLength, PRBool aRelease)
|
||||
{
|
||||
ReleaseText();
|
||||
|
||||
m2b = aBuffer;
|
||||
mState.mIs2b = PR_TRUE;
|
||||
mState.mInHeap = aRelease;
|
||||
mState.mLength = aLength;
|
||||
}
|
||||
|
||||
void
|
||||
nsTextFragment::SetTo(const PRUnichar* aBuffer, PRInt32 aLength)
|
||||
{
|
||||
ReleaseText();
|
||||
|
||||
if (aLength != 0) {
|
||||
// See if we need to store the data in ucs2 or not
|
||||
PRBool need2 = PR_FALSE;
|
||||
const PRUnichar *ucp = aBuffer;
|
||||
const PRUnichar *uend = aBuffer + aLength;
|
||||
while (ucp < uend) {
|
||||
PRUnichar ch = *ucp++;
|
||||
if (ch >> 8) {
|
||||
need2 = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (need2) {
|
||||
// Use ucs2 storage because we have to
|
||||
m2b = (const PRUnichar *)nsMemory::Clone(aBuffer,
|
||||
aLength * sizeof(PRUnichar));
|
||||
|
||||
if (!m2b) {
|
||||
NS_ERROR("Failed to clone string buffer!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup our fields
|
||||
mState.mIs2b = PR_TRUE;
|
||||
mState.mInHeap = PR_TRUE;
|
||||
mState.mLength = aLength;
|
||||
} else {
|
||||
// Use 1 byte storage because we can
|
||||
|
||||
PRBool in_heap = PR_TRUE;
|
||||
|
||||
if (aLength == 1 && *aBuffer == '\n') {
|
||||
m1b = &sNewLineCharacter;
|
||||
|
||||
in_heap = PR_FALSE;
|
||||
} else {
|
||||
unsigned char *nt =
|
||||
(unsigned char *)nsMemory::Alloc(aLength * sizeof(char));
|
||||
|
||||
if (!nt) {
|
||||
NS_ERROR("Failed to allocate string buffer!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy data
|
||||
for (PRUint32 i = 0; i < (PRUint32)aLength; ++i) {
|
||||
nt[i] = (unsigned char)aBuffer[i];
|
||||
}
|
||||
|
||||
m1b = nt;
|
||||
}
|
||||
|
||||
// Setup our fields
|
||||
mState.mIs2b = PR_FALSE;
|
||||
mState.mInHeap = in_heap;
|
||||
mState.mLength = aLength;
|
||||
}
|
||||
if (aLength == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsTextFragment::SetTo(const char *aBuffer, PRInt32 aLength)
|
||||
{
|
||||
ReleaseText();
|
||||
if (aLength != 0) {
|
||||
PRBool in_heap = PR_TRUE;
|
||||
|
||||
if (aLength == 1 && *aBuffer == '\n') {
|
||||
m1b = &sNewLineCharacter;
|
||||
|
||||
in_heap = PR_FALSE;
|
||||
} else {
|
||||
m1b = (unsigned char *)nsMemory::Clone(aBuffer, aLength * sizeof(char));
|
||||
|
||||
if (!m1b) {
|
||||
NS_ERROR("Failed to allocate string buffer!");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup our fields
|
||||
|
||||
PRUnichar firstChar = *aBuffer;
|
||||
if (aLength == 1 && firstChar < 256) {
|
||||
m1b = sSingleCharSharedString + firstChar;
|
||||
mState.mInHeap = PR_FALSE;
|
||||
mState.mIs2b = PR_FALSE;
|
||||
mState.mInHeap = in_heap;
|
||||
mState.mLength = aLength;
|
||||
mState.mLength = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const PRUnichar *ucp = aBuffer;
|
||||
const PRUnichar *uend = aBuffer + aLength;
|
||||
|
||||
// Check if we can use a shared string
|
||||
if (firstChar == ' ' || firstChar == '\n' || firstChar == '\t') {
|
||||
if (firstChar == ' ') {
|
||||
++ucp;
|
||||
}
|
||||
|
||||
const PRUnichar* start = ucp;
|
||||
while (ucp < uend && *ucp == '\n') {
|
||||
++ucp;
|
||||
}
|
||||
const PRUnichar* endNewLine = ucp;
|
||||
|
||||
PRUnichar space = ucp < uend && *ucp == '\t' ? '\t' : ' ';
|
||||
while (ucp < uend && *ucp == space) {
|
||||
++ucp;
|
||||
}
|
||||
|
||||
if (ucp == uend &&
|
||||
endNewLine - start <= TEXTFRAG_MAX_NEWLINES &&
|
||||
ucp - endNewLine <= TEXTFRAG_WHITE_AFTER_NEWLINE) {
|
||||
char** strings = space == ' ' ? sSpaceSharedString : sTabSharedString;
|
||||
m1b = strings[endNewLine - start];
|
||||
|
||||
// If we didn't find a space in the beginning, skip it now.
|
||||
if (firstChar != ' ') {
|
||||
++m1b;
|
||||
}
|
||||
|
||||
mState.mInHeap = PR_FALSE;
|
||||
mState.mIs2b = PR_FALSE;
|
||||
mState.mLength = aLength;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// See if we need to store the data in ucs2 or not
|
||||
PRBool need2 = PR_FALSE;
|
||||
while (ucp < uend) {
|
||||
PRUnichar ch = *ucp++;
|
||||
if (ch >= 256) {
|
||||
need2 = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (need2) {
|
||||
// Use ucs2 storage because we have to
|
||||
m2b = (PRUnichar *)nsMemory::Clone(aBuffer,
|
||||
aLength * sizeof(PRUnichar));
|
||||
if (!m2b) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Use 1 byte storage because we can
|
||||
char* buff = (char *)nsMemory::Alloc(aLength * sizeof(char));
|
||||
if (!buff) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy data
|
||||
for (PRUint32 i = 0; i < (PRUint32)aLength; ++i) {
|
||||
buff[i] = (char)aBuffer[i];
|
||||
}
|
||||
m1b = buff;
|
||||
}
|
||||
|
||||
// Setup our fields
|
||||
mState.mInHeap = PR_TRUE;
|
||||
mState.mIs2b = need2;
|
||||
mState.mLength = aLength;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -272,23 +244,11 @@ nsTextFragment::AppendTo(nsAString& aString) const
|
|||
if (mState.mIs2b) {
|
||||
aString.Append(m2b, mState.mLength);
|
||||
} else {
|
||||
AppendASCIItoUTF16(Substring((char *)m1b, ((char *)m1b) + mState.mLength),
|
||||
AppendASCIItoUTF16(Substring(m1b, m1b + mState.mLength),
|
||||
aString);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsTextFragment::AppendTo(nsACString& aCString) const
|
||||
{
|
||||
if (mState.mIs2b) {
|
||||
LossyAppendUTF16toASCII(Substring((PRUnichar *)m2b,
|
||||
(PRUnichar *)m2b + mState.mLength),
|
||||
aCString);
|
||||
} else {
|
||||
aCString.Append((char *)m1b, mState.mLength);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsTextFragment::CopyTo(PRUnichar *aDest, PRInt32 aOffset, PRInt32 aCount)
|
||||
{
|
||||
|
@ -307,40 +267,111 @@ nsTextFragment::CopyTo(PRUnichar *aDest, PRInt32 aOffset, PRInt32 aCount)
|
|||
if (mState.mIs2b) {
|
||||
memcpy(aDest, m2b + aOffset, sizeof(PRUnichar) * aCount);
|
||||
} else {
|
||||
unsigned const char *cp = m1b + aOffset;
|
||||
unsigned const char *end = cp + aCount;
|
||||
const char *cp = m1b + aOffset;
|
||||
const char *end = cp + aCount;
|
||||
while (cp < end) {
|
||||
*aDest++ = PRUnichar(*cp++);
|
||||
*aDest++ = unsigned char(*cp++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsTextFragment::CopyTo(char *aDest, PRInt32 aOffset, PRInt32 aCount)
|
||||
nsTextFragment::Append(const nsAString& aData)
|
||||
{
|
||||
NS_ASSERTION(aOffset >= 0, "Bad offset passed to nsTextFragment::CopyTo()!");
|
||||
NS_ASSERTION(aCount >= 0, "Bad count passed to nsTextFragment::CopyTo()!");
|
||||
PRUint32 len = aData.Length();
|
||||
const PRUnichar* start = aData.BeginReading();
|
||||
|
||||
if (aOffset < 0) {
|
||||
aOffset = 0;
|
||||
// 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) {
|
||||
SetTo(start, len);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (aOffset + aCount > GetLength()) {
|
||||
aCount = mState.mLength - aOffset;
|
||||
// Should we optimize for aData.Length() == 0?
|
||||
|
||||
if (mState.mIs2b) {
|
||||
// Already a 2-byte string so the result will be too
|
||||
PRUnichar* buff = (PRUnichar*)nsMemory::Realloc(m2b, (mState.mLength + len) * sizeof(PRUnichar));
|
||||
if (!buff) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(buff + mState.mLength, start, len * sizeof(PRUnichar));
|
||||
mState.mLength += len;
|
||||
m2b = buff;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (aCount != 0) {
|
||||
if (mState.mIs2b) {
|
||||
const PRUnichar *cp = m2b + aOffset;
|
||||
const PRUnichar *end = cp + aCount;
|
||||
while (cp < end) {
|
||||
*aDest++ = (char)(*cp++);
|
||||
}
|
||||
} else {
|
||||
memcpy(aDest, m1b + aOffset, sizeof(char) * aCount);
|
||||
// Current string is a 1-byte string, check if the new data fits in one byte too.
|
||||
|
||||
const PRUnichar* ucp = start;
|
||||
const PRUnichar* uend = ucp + len;
|
||||
PRBool need2 = PR_FALSE;
|
||||
while (ucp < uend) {
|
||||
PRUnichar ch = *ucp++;
|
||||
if (ch >= 256) {
|
||||
need2 = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (need2) {
|
||||
// The old data was 1-byte, but the new is not so we have to expand it
|
||||
// all to 2-byte
|
||||
PRUnichar* buff = (PRUnichar*)nsMemory::Alloc((mState.mLength + len) *
|
||||
sizeof(PRUnichar));
|
||||
if (!buff) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy data
|
||||
for (PRUint32 i = 0; i < mState.mLength; ++i) {
|
||||
buff[i] = (unsigned char)m1b[i];
|
||||
}
|
||||
|
||||
memcpy(buff + mState.mLength, start, len * sizeof(PRUnichar));
|
||||
|
||||
mState.mLength += len;
|
||||
mState.mIs2b = PR_TRUE;
|
||||
|
||||
if (!mState.mInHeap) {
|
||||
nsMemory::Free(m2b);
|
||||
}
|
||||
m2b = buff;
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The new and the old data is all 1-byte
|
||||
char* buff;
|
||||
if (mState.mInHeap) {
|
||||
buff = (char*)nsMemory::Realloc(NS_CONST_CAST(char*, m1b),
|
||||
(mState.mLength + len) * sizeof(char));
|
||||
if (!buff) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
buff = (char*)nsMemory::Alloc((mState.mLength + len) * sizeof(char));
|
||||
if (!buff) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(buff, m1b, mState.mLength);
|
||||
}
|
||||
|
||||
for (PRUint32 i = 0; i < len; ++i) {
|
||||
buff[mState.mLength + i] = (char)start[i];
|
||||
}
|
||||
|
||||
m1b = buff;
|
||||
mState.mLength += len;
|
||||
|
||||
}
|
||||
|
||||
// To save time we only do this when we really want to know, not during
|
||||
|
|
|
@ -72,6 +72,9 @@ class nsCString;
|
|||
*/
|
||||
class nsTextFragment {
|
||||
public:
|
||||
static nsresult Init();
|
||||
static void Shutdown();
|
||||
|
||||
/**
|
||||
* Default constructor. Initialize the fragment to be empty.
|
||||
*/
|
||||
|
@ -82,54 +85,12 @@ public:
|
|||
|
||||
~nsTextFragment();
|
||||
|
||||
/**
|
||||
* Initialize the contents of this fragment to be a copy of
|
||||
* the argument fragment.
|
||||
*/
|
||||
nsTextFragment(const nsTextFragment& aOther);
|
||||
|
||||
/**
|
||||
* Initialize the contents of this fragment to be a copy of
|
||||
* the argument 7bit ascii string.
|
||||
*/
|
||||
nsTextFragment(const char *aString);
|
||||
|
||||
/**
|
||||
* Initialize the contents of this fragment to be a copy of
|
||||
* the argument ucs2 string.
|
||||
*/
|
||||
nsTextFragment(const PRUnichar *aString);
|
||||
|
||||
/**
|
||||
* Initialize the contents of this fragment to be a copy of
|
||||
* the argument string.
|
||||
*/
|
||||
nsTextFragment(const nsString& aString);
|
||||
|
||||
/**
|
||||
* Change the contents of this fragment to be a copy of the
|
||||
* the argument fragment.
|
||||
*/
|
||||
nsTextFragment& operator=(const nsTextFragment& aOther);
|
||||
|
||||
/**
|
||||
* Change the contents of this fragment to be a copy of the
|
||||
* the argument 7bit ascii string.
|
||||
*/
|
||||
nsTextFragment& operator=(const char *aString);
|
||||
|
||||
/**
|
||||
* Change the contents of this fragment to be a copy of the
|
||||
* the argument ucs2 string.
|
||||
*/
|
||||
nsTextFragment& operator=(const PRUnichar *aString);
|
||||
|
||||
/**
|
||||
* Change the contents of this fragment to be a copy of the
|
||||
* the argument string.
|
||||
*/
|
||||
nsTextFragment& operator=(const nsAString& aString);
|
||||
|
||||
/**
|
||||
* Return PR_TRUE if this fragment is represented by PRUnichar data
|
||||
*/
|
||||
|
@ -175,14 +136,6 @@ public:
|
|||
return PRInt32(mState.mLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the contents of this fragment to be the given buffer and
|
||||
* length. The memory becomes owned by the fragment. In addition,
|
||||
* the memory for aBuffer must have been allocated using the
|
||||
* nsIMemory interface.
|
||||
*/
|
||||
void SetTo(PRUnichar *aBuffer, PRInt32 aLength, PRBool aRelease);
|
||||
|
||||
/**
|
||||
* Change the contents of this fragment to be a copy of the given
|
||||
* buffer. Like operator= except a length is specified instead of
|
||||
|
@ -191,23 +144,15 @@ public:
|
|||
void SetTo(const PRUnichar* aBuffer, PRInt32 aLength);
|
||||
|
||||
/**
|
||||
* Change the contents of this fragment to be a copy of the given
|
||||
* buffer. Like operator= except a length is specified instead of
|
||||
* assuming 0 termination.
|
||||
* Append aData to the end of this fragment.
|
||||
*/
|
||||
void SetTo(const char *aBuffer, PRInt32 aLength);
|
||||
void Append(const nsAString& aData);
|
||||
|
||||
/**
|
||||
* Append the contents of this string fragment to aString
|
||||
*/
|
||||
void AppendTo(nsAString& aString) const;
|
||||
|
||||
/**
|
||||
* Append the contents of this string fragment to aCString. This
|
||||
* method will do a lossy conversion from UTF-16 to ASCII.
|
||||
*/
|
||||
void AppendTo(nsACString& aCString) const;
|
||||
|
||||
/**
|
||||
* Make a copy of the fragments contents starting at offset for
|
||||
* count characters. The offset and count will be adjusted to
|
||||
|
@ -216,14 +161,6 @@ public:
|
|||
*/
|
||||
void CopyTo(PRUnichar *aDest, PRInt32 aOffset, PRInt32 aCount);
|
||||
|
||||
/**
|
||||
* Make a copy of the fragments contents starting at offset for
|
||||
* count characters. The offset and count will be adjusted to
|
||||
* lie within the fragments data. The fragments data is converted if
|
||||
* necessary.
|
||||
*/
|
||||
void CopyTo(char *aDest, PRInt32 aOffset, PRInt32 aCount);
|
||||
|
||||
/**
|
||||
* Return the character in the text-fragment at the given
|
||||
* index. This always returns a PRUnichar.
|
||||
|
@ -231,7 +168,7 @@ public:
|
|||
PRUnichar CharAt(PRInt32 aIndex) const
|
||||
{
|
||||
NS_ASSERTION(PRUint32(aIndex) < mState.mLength, "bad index");
|
||||
return mState.mIs2b ? m2b[aIndex] : PRUnichar(m1b[aIndex]);
|
||||
return mState.mIs2b ? m2b[aIndex] : NS_STATIC_CAST(unsigned char, m1b[aIndex]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -247,12 +184,12 @@ public:
|
|||
PRUint32 mLength : 29;
|
||||
};
|
||||
|
||||
protected:
|
||||
private:
|
||||
void ReleaseText();
|
||||
|
||||
union {
|
||||
const PRUnichar *m2b;
|
||||
const unsigned char *m1b;
|
||||
PRUnichar *m2b;
|
||||
const char *m1b; // This is const since it can point to shared data
|
||||
};
|
||||
|
||||
union {
|
||||
|
|
|
@ -300,6 +300,15 @@ Initialize(nsIModule* aSelf)
|
|||
return rv;
|
||||
}
|
||||
|
||||
rv = nsTextFragment::Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Could not initialize nsAttrValue");
|
||||
|
||||
Shutdown();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Register all of our atoms once
|
||||
nsCSSAnonBoxes::AddRefAtoms();
|
||||
nsCSSPseudoClasses::AddRefAtoms();
|
||||
|
@ -423,6 +432,7 @@ Shutdown()
|
|||
NS_IF_RELEASE(nsRuleNode::gLangService);
|
||||
nsGenericHTMLElement::Shutdown();
|
||||
|
||||
nsTextFragment::Shutdown();
|
||||
nsAttrValue::Shutdown();
|
||||
nsContentUtils::Shutdown();
|
||||
nsLayoutStylesheetCache::Shutdown();
|
||||
|
|
|
@ -1723,7 +1723,8 @@ nsTextTransformer::SelfTest(nsPresContext* aPresContext)
|
|||
cp++;
|
||||
}
|
||||
|
||||
nsTextFragment frag(st->text);
|
||||
nsTextFragment frag;
|
||||
frag.SetTo(st->text, nsCRT::strlen(st->text));
|
||||
nsTextTransformer tx(aPresContext);
|
||||
|
||||
for (PRInt32 preMode = 0; preMode < NUM_MODES; preMode++) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче