Use a hashtable instead of searching an array, to make RemoveBidiContinuation() not be O(n^2). Bug 212827, r+sr=bzbarsky.

This commit is contained in:
smontagu%netscape.com 2003-07-24 18:33:50 +00:00
Родитель 2710b0cafe
Коммит f165e1286f
4 изменённых файлов: 244 добавлений и 104 удалений

Просмотреть файл

@ -78,7 +78,7 @@ nsBidiPresUtils::nsBidiPresUtils() : mArraySize(8),
mBidiEngine(nsnull)
{
mBidiEngine = new nsBidi();
if (mBidiEngine) {
if (mBidiEngine && mContentToFrameIndex.Init()) {
mSuccess = NS_OK;
}
}
@ -141,7 +141,37 @@ CreateBidiContinuation(nsIPresContext* aPresContext,
return NS_OK;
}
/*
* Overview of the implementation of Resolve():
*
* Walk through the descendants of aBlockFrame and build:
* * mLogicalArray: an nsVoidArray of nsIFrame* pointers in logical order
* * mBuffer: an nsAutoString containing a representation of
* the content of the frames.
* In the case of text frames, this is the actual text context of the
* frames, but some other elements are represented in a symbolic form which
* will make the Unicode Bidi Algorithm give the correct results.
* Bidi embeddings and overrides set by CSS or <bdo> elements are
* represented by the corresponding Unicode control characters.
* <br> elements are represented by U+2028 LINE SEPARATOR
* Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
* CHARACTER
*
* Then pass mBuffer to the Bidi engine for resolving of embedding levels
* by nsBidi::SetPara() and division into directional runs by
* nsBidi::CountRuns().
*
* Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
* correlate them with the frames indexed in mLogicalArray, setting the
* baseLevel, embeddingLevel, and charType properties according to the results
* returned by the Bidi engine and CalculateCharType().
*
* The rendering layer requires each text frame to contain text in only one
* direction and of only one character type, so we may need to call
* EnsureBidiContinuation() to split frames. We may also need to call
* RemoveBidiContinuation() to delete frames created by
* EnsureBidiContinuation() in previous reflows.
*/
nsresult
nsBidiPresUtils::Resolve(nsIPresContext* aPresContext,
nsIFrame* aBlockFrame,
@ -151,6 +181,7 @@ nsBidiPresUtils::Resolve(nsIPresContext* aPresContext,
{
aForceReflow = PR_FALSE;
mLogicalFrames.Clear();
mContentToFrameIndex.Clear();
// handle bidi-override being set on the block itself before calling
// InitLogicalArray.
@ -307,13 +338,15 @@ nsBidiPresUtils::Resolve(nsIPresContext* aPresContext,
} // if (runLength < fragmentLength)
else {
frame->AdjustOffsetsForBidi(contentOffset, contentOffset + fragmentLength);
frame->GetBidiProperty(aPresContext, nsLayoutAtoms::nextBidi,
(void**) &nextBidi,sizeof(nextBidi));
if (RemoveBidiContinuation(aPresContext, frame, nextBidi,
content, frameIndex, temp) ) {
PRInt32 newIndex = 0;
mContentToFrameIndex.Get(content, &newIndex);
if (newIndex > frameIndex) {
RemoveBidiContinuation(aPresContext, frame,
frameIndex, newIndex, temp);
aForceReflow = PR_TRUE;
runLength -= temp;
lineOffset += temp;
frameIndex = newIndex;
}
}
} // isTextFrame
@ -385,7 +418,16 @@ nsBidiPresUtils::InitLogicalArray(nsIPresContext* aPresContext,
frame->FirstChild(aPresContext, nsnull, &kid);
res = InitLogicalArray(aPresContext, kid, aNextInFlow, aAddMarkers);
}
else { // bidi leaf
else {
/* Bidi leaf frame: add the frame to the mLogicalFrames array,
* and add its index to the mContentToFrameIndex hashtable. This
* will be used in RemoveBidiContinuation() to identify the last
* frame in the array with a given content.
*/
nsIContent* content = frame->GetContent();
if (content) {
mContentToFrameIndex.Put(content, mLogicalFrames.Count());
}
mLogicalFrames.AppendElement(frame);
}
@ -742,42 +784,24 @@ nsBidiPresUtils::EnsureBidiContinuation(nsIPresContext* aPresContext,
return PR_TRUE;
}
PRBool
void
nsBidiPresUtils::RemoveBidiContinuation(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIFrame* aNextFrame,
nsIContent* aContent,
PRInt32& aFrameIndex,
PRInt32 aFirstIndex,
PRInt32 aLastIndex,
PRInt32& aOffset) const
{
if (!aFrame) {
return PR_FALSE;
}
nsCOMPtr<nsIAtom> frameType;
nsIFrame* frame;
PRInt32 index;
PRInt32 newIndex = -1;
PRInt32 frameCount = mLogicalFrames.Count();
for (index = aFrameIndex + 1; index < frameCount; index++) {
// frames with the same content may not be adjacent. So check all frames.
frame = (nsIFrame*) mLogicalFrames[index];
if (frame->GetContent() == aContent) {
newIndex = index;
}
}
if (-1 == newIndex) {
return PR_FALSE;
}
nsIFrame* nextBidi;
nsIFrame* parent = aFrame->GetParent();
nsCOMPtr<nsIAtom> frameType;
nsIFrame* frame;
PRInt32 index;
nsIFrame* parent = aFrame->GetParent();
nsCOMPtr<nsIPresShell> presShell;
aPresContext->GetShell(getter_AddRefs(presShell) );
aOffset = 0;
for (index = newIndex; index > aFrameIndex; index--) {
for (index = aLastIndex; index > aFirstIndex; index--) {
frame = (nsIFrame*) mLogicalFrames[index];
frame->GetFrameType(getter_AddRefs(frameType) );
if (nsLayoutAtoms::directionalFrame == frameType.get() ) {
@ -797,10 +821,14 @@ nsBidiPresUtils::RemoveBidiContinuation(nsIPresContext* aPresContext,
}
}
}
if (aNextFrame) {
nsCOMPtr<nsIFrameManager> frameManager;
presShell->GetFrameManager(getter_AddRefs(frameManager) );
if (frameManager) {
nsIFrame* thisFramesNextBidiFrame;
nsIFrame* previousFramesNextBidiFrame;
nsIFrameManager* frameManager = presShell->GetFrameManager();
if (frameManager) {
frameManager->GetFrameProperty(aFrame, nsLayoutAtoms::nextBidi,
0, (void**) &thisFramesNextBidiFrame);
if (thisFramesNextBidiFrame) {
// Remove nextBidi property, associated with the current frame
// and with all of its prev-in-flow.
frame = aFrame;
@ -811,14 +839,10 @@ nsBidiPresUtils::RemoveBidiContinuation(nsIPresContext* aPresContext,
break;
}
frameManager->GetFrameProperty(frame, nsLayoutAtoms::nextBidi,
0, (void**) &nextBidi);
} while (aNextFrame == nextBidi);
} // if (frameManager)
} // if (aNextFrame)
aFrameIndex = newIndex;
return PR_TRUE;
0, (void**) &previousFramesNextBidiFrame);
} while (thisFramesNextBidiFrame == previousFramesNextBidiFrame);
} // if (thisFramesNextBidiFrame)
} // if (frameManager)
}
nsresult

Просмотреть файл

@ -31,6 +31,7 @@
#include "nsBidi.h"
#include "nsBidiUtils.h"
#include "nsCOMPtr.h"
#include "nsDataHashtable.h"
class nsBidiPresUtils {
public:
@ -39,7 +40,17 @@ public:
PRBool IsSuccessful(void) const;
/**
* Make Bidi engine calculate embedding levels of the frames.
* Make Bidi engine calculate the embedding levels of the frames that are
* descendants of a given block frame.
*
* @param aPresContext The presContext
* @param aBlockFrame The block frame
* @param aFirstChild The first child frame of aBlockFrame
* @param aForceReflow [OUT] Set if we delete frames and will need to
* reflow the block frame
* @param aIsVisualFormControl [IN] Set if we are in a form control on a
* visual page.
* @see nsHTMLReflowState::IsBidiFormControl
*
* @lina 06/18/2000
*/
@ -155,17 +166,51 @@ private:
nsIFrame* aContainer,
PRInt32& aMinX,
PRInt32& aMaxX) const;
/**
* Helper method for Resolve()
* Truncate a text frame and possibly create a continuation frame with the
* remainder of its content.
*
* @param aPresContext the pres context
* @param aContent the content of the frame
* @param aFrame the original frame
* @param aNewFrame [OUT] the new frame that was created
* @param aFrameIndex [IN/OUT] index of aFrame in mLogicalFrames
*
* If there is already a bidi continuation for this frame in mLogicalFrames,
* no new frame will be created. On exit aNewFrame will point to the existing
* bidi continuation and aFrameIndex will contain its index.
* @see Resolve()
* @see RemoveBidiContinuation()
*/
PRBool EnsureBidiContinuation(nsIPresContext* aPresContext,
nsIContent* aContent,
nsIFrame* aFrame,
nsIFrame** aNewFrame,
PRInt32& aFrameIndex);
PRBool RemoveBidiContinuation(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIFrame* aNextFrame,
nsIContent* aContent,
PRInt32& aFrameIndex,
PRInt32& aOffset) const;
/**
* Helper method for Resolve()
* Delete one or more bidi continuation frames created in a previous reflow by
* EnsureBidiContinuation().
* @param aPresContext the pres context
* @param aFrame the frame whose continuations are to be removed
* @param aFirstIndex index of aFrame in mLogicalFrames
* @param aLastIndex index of the last frame to be removed
* @param aOffset [OUT] count of directional frames removed. Since
* directional frames have control characters
* corresponding to them in mBuffer, the pointers to
* mBuffer in Resolve() will need to be updated after
* deleting the frames.
*
* @see Resolve()
* @see EnsureBidiContinuation()
*/
void RemoveBidiContinuation(nsIPresContext* aPresContext,
nsIFrame* aFrame,
PRInt32 aFirstIndex,
PRInt32 aLastIndex,
PRInt32& aOffset) const;
void CalculateCharType(PRInt32& aOffset,
PRInt32 aCharTypeLimit,
PRInt32& aRunLimit,
@ -179,6 +224,7 @@ private:
nsAutoString mBuffer;
nsVoidArray mLogicalFrames;
nsVoidArray mVisualFrames;
nsDataHashtable<nsISupportsHashKey, PRInt32> mContentToFrameIndex;
PRInt32 mArraySize;
PRInt32* mIndexMap;
PRUint8* mLevels;

Просмотреть файл

@ -31,6 +31,7 @@
#include "nsBidi.h"
#include "nsBidiUtils.h"
#include "nsCOMPtr.h"
#include "nsDataHashtable.h"
class nsBidiPresUtils {
public:
@ -39,7 +40,17 @@ public:
PRBool IsSuccessful(void) const;
/**
* Make Bidi engine calculate embedding levels of the frames.
* Make Bidi engine calculate the embedding levels of the frames that are
* descendants of a given block frame.
*
* @param aPresContext The presContext
* @param aBlockFrame The block frame
* @param aFirstChild The first child frame of aBlockFrame
* @param aForceReflow [OUT] Set if we delete frames and will need to
* reflow the block frame
* @param aIsVisualFormControl [IN] Set if we are in a form control on a
* visual page.
* @see nsHTMLReflowState::IsBidiFormControl
*
* @lina 06/18/2000
*/
@ -155,17 +166,51 @@ private:
nsIFrame* aContainer,
PRInt32& aMinX,
PRInt32& aMaxX) const;
/**
* Helper method for Resolve()
* Truncate a text frame and possibly create a continuation frame with the
* remainder of its content.
*
* @param aPresContext the pres context
* @param aContent the content of the frame
* @param aFrame the original frame
* @param aNewFrame [OUT] the new frame that was created
* @param aFrameIndex [IN/OUT] index of aFrame in mLogicalFrames
*
* If there is already a bidi continuation for this frame in mLogicalFrames,
* no new frame will be created. On exit aNewFrame will point to the existing
* bidi continuation and aFrameIndex will contain its index.
* @see Resolve()
* @see RemoveBidiContinuation()
*/
PRBool EnsureBidiContinuation(nsIPresContext* aPresContext,
nsIContent* aContent,
nsIFrame* aFrame,
nsIFrame** aNewFrame,
PRInt32& aFrameIndex);
PRBool RemoveBidiContinuation(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIFrame* aNextFrame,
nsIContent* aContent,
PRInt32& aFrameIndex,
PRInt32& aOffset) const;
/**
* Helper method for Resolve()
* Delete one or more bidi continuation frames created in a previous reflow by
* EnsureBidiContinuation().
* @param aPresContext the pres context
* @param aFrame the frame whose continuations are to be removed
* @param aFirstIndex index of aFrame in mLogicalFrames
* @param aLastIndex index of the last frame to be removed
* @param aOffset [OUT] count of directional frames removed. Since
* directional frames have control characters
* corresponding to them in mBuffer, the pointers to
* mBuffer in Resolve() will need to be updated after
* deleting the frames.
*
* @see Resolve()
* @see EnsureBidiContinuation()
*/
void RemoveBidiContinuation(nsIPresContext* aPresContext,
nsIFrame* aFrame,
PRInt32 aFirstIndex,
PRInt32 aLastIndex,
PRInt32& aOffset) const;
void CalculateCharType(PRInt32& aOffset,
PRInt32 aCharTypeLimit,
PRInt32& aRunLimit,
@ -179,6 +224,7 @@ private:
nsAutoString mBuffer;
nsVoidArray mLogicalFrames;
nsVoidArray mVisualFrames;
nsDataHashtable<nsISupportsHashKey, PRInt32> mContentToFrameIndex;
PRInt32 mArraySize;
PRInt32* mIndexMap;
PRUint8* mLevels;

Просмотреть файл

@ -78,7 +78,7 @@ nsBidiPresUtils::nsBidiPresUtils() : mArraySize(8),
mBidiEngine(nsnull)
{
mBidiEngine = new nsBidi();
if (mBidiEngine) {
if (mBidiEngine && mContentToFrameIndex.Init()) {
mSuccess = NS_OK;
}
}
@ -141,7 +141,37 @@ CreateBidiContinuation(nsIPresContext* aPresContext,
return NS_OK;
}
/*
* Overview of the implementation of Resolve():
*
* Walk through the descendants of aBlockFrame and build:
* * mLogicalArray: an nsVoidArray of nsIFrame* pointers in logical order
* * mBuffer: an nsAutoString containing a representation of
* the content of the frames.
* In the case of text frames, this is the actual text context of the
* frames, but some other elements are represented in a symbolic form which
* will make the Unicode Bidi Algorithm give the correct results.
* Bidi embeddings and overrides set by CSS or <bdo> elements are
* represented by the corresponding Unicode control characters.
* <br> elements are represented by U+2028 LINE SEPARATOR
* Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
* CHARACTER
*
* Then pass mBuffer to the Bidi engine for resolving of embedding levels
* by nsBidi::SetPara() and division into directional runs by
* nsBidi::CountRuns().
*
* Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
* correlate them with the frames indexed in mLogicalArray, setting the
* baseLevel, embeddingLevel, and charType properties according to the results
* returned by the Bidi engine and CalculateCharType().
*
* The rendering layer requires each text frame to contain text in only one
* direction and of only one character type, so we may need to call
* EnsureBidiContinuation() to split frames. We may also need to call
* RemoveBidiContinuation() to delete frames created by
* EnsureBidiContinuation() in previous reflows.
*/
nsresult
nsBidiPresUtils::Resolve(nsIPresContext* aPresContext,
nsIFrame* aBlockFrame,
@ -151,6 +181,7 @@ nsBidiPresUtils::Resolve(nsIPresContext* aPresContext,
{
aForceReflow = PR_FALSE;
mLogicalFrames.Clear();
mContentToFrameIndex.Clear();
// handle bidi-override being set on the block itself before calling
// InitLogicalArray.
@ -307,13 +338,15 @@ nsBidiPresUtils::Resolve(nsIPresContext* aPresContext,
} // if (runLength < fragmentLength)
else {
frame->AdjustOffsetsForBidi(contentOffset, contentOffset + fragmentLength);
frame->GetBidiProperty(aPresContext, nsLayoutAtoms::nextBidi,
(void**) &nextBidi,sizeof(nextBidi));
if (RemoveBidiContinuation(aPresContext, frame, nextBidi,
content, frameIndex, temp) ) {
PRInt32 newIndex = 0;
mContentToFrameIndex.Get(content, &newIndex);
if (newIndex > frameIndex) {
RemoveBidiContinuation(aPresContext, frame,
frameIndex, newIndex, temp);
aForceReflow = PR_TRUE;
runLength -= temp;
lineOffset += temp;
frameIndex = newIndex;
}
}
} // isTextFrame
@ -385,7 +418,16 @@ nsBidiPresUtils::InitLogicalArray(nsIPresContext* aPresContext,
frame->FirstChild(aPresContext, nsnull, &kid);
res = InitLogicalArray(aPresContext, kid, aNextInFlow, aAddMarkers);
}
else { // bidi leaf
else {
/* Bidi leaf frame: add the frame to the mLogicalFrames array,
* and add its index to the mContentToFrameIndex hashtable. This
* will be used in RemoveBidiContinuation() to identify the last
* frame in the array with a given content.
*/
nsIContent* content = frame->GetContent();
if (content) {
mContentToFrameIndex.Put(content, mLogicalFrames.Count());
}
mLogicalFrames.AppendElement(frame);
}
@ -742,42 +784,24 @@ nsBidiPresUtils::EnsureBidiContinuation(nsIPresContext* aPresContext,
return PR_TRUE;
}
PRBool
void
nsBidiPresUtils::RemoveBidiContinuation(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIFrame* aNextFrame,
nsIContent* aContent,
PRInt32& aFrameIndex,
PRInt32 aFirstIndex,
PRInt32 aLastIndex,
PRInt32& aOffset) const
{
if (!aFrame) {
return PR_FALSE;
}
nsCOMPtr<nsIAtom> frameType;
nsIFrame* frame;
PRInt32 index;
PRInt32 newIndex = -1;
PRInt32 frameCount = mLogicalFrames.Count();
for (index = aFrameIndex + 1; index < frameCount; index++) {
// frames with the same content may not be adjacent. So check all frames.
frame = (nsIFrame*) mLogicalFrames[index];
if (frame->GetContent() == aContent) {
newIndex = index;
}
}
if (-1 == newIndex) {
return PR_FALSE;
}
nsIFrame* nextBidi;
nsIFrame* parent = aFrame->GetParent();
nsCOMPtr<nsIAtom> frameType;
nsIFrame* frame;
PRInt32 index;
nsIFrame* parent = aFrame->GetParent();
nsCOMPtr<nsIPresShell> presShell;
aPresContext->GetShell(getter_AddRefs(presShell) );
aOffset = 0;
for (index = newIndex; index > aFrameIndex; index--) {
for (index = aLastIndex; index > aFirstIndex; index--) {
frame = (nsIFrame*) mLogicalFrames[index];
frame->GetFrameType(getter_AddRefs(frameType) );
if (nsLayoutAtoms::directionalFrame == frameType.get() ) {
@ -797,10 +821,14 @@ nsBidiPresUtils::RemoveBidiContinuation(nsIPresContext* aPresContext,
}
}
}
if (aNextFrame) {
nsCOMPtr<nsIFrameManager> frameManager;
presShell->GetFrameManager(getter_AddRefs(frameManager) );
if (frameManager) {
nsIFrame* thisFramesNextBidiFrame;
nsIFrame* previousFramesNextBidiFrame;
nsIFrameManager* frameManager = presShell->GetFrameManager();
if (frameManager) {
frameManager->GetFrameProperty(aFrame, nsLayoutAtoms::nextBidi,
0, (void**) &thisFramesNextBidiFrame);
if (thisFramesNextBidiFrame) {
// Remove nextBidi property, associated with the current frame
// and with all of its prev-in-flow.
frame = aFrame;
@ -811,14 +839,10 @@ nsBidiPresUtils::RemoveBidiContinuation(nsIPresContext* aPresContext,
break;
}
frameManager->GetFrameProperty(frame, nsLayoutAtoms::nextBidi,
0, (void**) &nextBidi);
} while (aNextFrame == nextBidi);
} // if (frameManager)
} // if (aNextFrame)
aFrameIndex = newIndex;
return PR_TRUE;
0, (void**) &previousFramesNextBidiFrame);
} while (thisFramesNextBidiFrame == previousFramesNextBidiFrame);
} // if (thisFramesNextBidiFrame)
} // if (frameManager)
}
nsresult