--- a/gfx/src/thebes/nsThebesFontMetrics.h
+++ b/gfx/src/thebes/nsThebesFontMetrics.h
@@ -154,36 +154,36 @@ public:
protected:
const gfxFont::Metrics& GetMetrics() const;
class AutoTextRun {
public:
AutoTextRun(nsThebesFontMetrics* aMetrics, nsIRenderingContext* aRC,
const char* aString, PRInt32 aLength, PRBool aEnableSpacing) {
- mTextRun = gfxGlobalTextRunCache::GetTextRun(
+ mTextRun = gfxTextRunCache::MakeTextRun(
NS_REINTERPRET_CAST(const PRUint8*, aString), aLength,
aMetrics->mFontGroup,
NS_STATIC_CAST(gfxContext*, aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)),
aMetrics->mP2A,
ComputeFlags(aMetrics, aEnableSpacing));
}
AutoTextRun(nsThebesFontMetrics* aMetrics, nsIRenderingContext* aRC,
const PRUnichar* aString, PRInt32 aLength, PRBool aEnableSpacing) {
- mTextRun = gfxGlobalTextRunCache::GetTextRun(
+ mTextRun = gfxTextRunCache::MakeTextRun(
aString, aLength, aMetrics->mFontGroup,
NS_STATIC_CAST(gfxContext*, aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)),
aMetrics->mP2A,
ComputeFlags(aMetrics, aEnableSpacing));
}
- gfxTextRun* operator->() { return mTextRun; }
- gfxTextRun* get() { return mTextRun; }
+ gfxTextRun* operator->() { return mTextRun.get(); }
+ gfxTextRun* get() { return mTextRun.get(); }
private:
- gfxTextRun* mTextRun;
+ gfxTextRunCache::AutoTextRun mTextRun;
static PRUint32 ComputeFlags(nsThebesFontMetrics* aMetrics,
PRBool aEnableSpacing) {
PRUint32 flags = 0;
if (aMetrics->GetRightToLeftTextRunMode()) {
flags |= gfxTextRunFactory::TEXT_IS_RTL;
}
if (aEnableSpacing) {
--- a/gfx/thebes/public/gfxFont.h
+++ b/gfx/thebes/public/gfxFont.h
@@ -424,19 +424,21 @@ protected:
class THEBES_API gfxTextRunFactory {
THEBES_INLINE_DECL_REFCOUNTING(gfxTextRunFactory)
public:
// Flags in the mask 0xFFFF0000 are reserved for textrun clients
// Flags in the mask 0x0000F000 are reserved for per-platform fonts
// Flags in the mask 0x00000FFF are set by the textrun creator.
enum {
- USER_TEXT_FLAGS = 0xFFFF0000,
+ CACHE_TEXT_FLAGS = 0xF0000000,
+ USER_TEXT_FLAGS = 0x0FFF0000,
PLATFORM_TEXT_FLAGS = 0x0000F000,
TEXTRUN_TEXT_FLAGS = 0x00000FFF,
+ SETTABLE_FLAGS = CACHE_TEXT_FLAGS | USER_TEXT_FLAGS,
/**
* When set, the text string pointer used to create the text run
* is guaranteed to be available during the lifetime of the text run.
*/
TEXT_IS_PERSISTENT = 0x0001,
/**
* When set, the text is known to be all-ASCII (< 128).
@@ -512,17 +514,17 @@ public:
};
/**
* gfxTextRun is an abstraction for drawing and measuring substrings of a run
* of text. It stores runs of positioned glyph data, each run having a single
* gfxFont. The glyphs are associated with a string of source text, and the
* gfxTextRun APIs take parameters that are offsets into that source text.
*
- * \r, \t and \n characters (for which gfxFontGroup::IsInvisibleChar returns
+ * \r, \t and \n characters (for which gfxFontGroup::IsInvalidChar returns
* PR_TRUE) should be set to a CompressedGlyph with SetMissing() to make them
* invisible and zero-width.
*
* gfxTextRuns are not refcounted. They should be deleted when no longer required.
*
* gfxTextRuns are mostly immutable. The only things that can change are
* inter-cluster spacing and line break placement. Spacing is always obtained
* lazily by methods that need it, it is not cached. Line breaks are stored
@@ -790,22 +792,22 @@ public:
// Utility getters
PRBool IsRightToLeft() const { return (mFlags & gfxTextRunFactory::TEXT_IS_RTL) != 0; }
gfxFloat GetDirection() const { return (mFlags & gfxTextRunFactory::TEXT_IS_RTL) ? -1.0 : 1.0; }
void *GetUserData() const { return mUserData; }
void SetUserData(void *aUserData) { mUserData = aUserData; }
PRUint32 GetFlags() const { return mFlags; }
void SetFlagBits(PRUint32 aFlags) {
- NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::USER_TEXT_FLAGS),
+ NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS),
"Only user flags should be mutable");
mFlags |= aFlags;
}
void ClearFlagBits(PRUint32 aFlags) {
- NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::USER_TEXT_FLAGS),
+ NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS),
"Only user flags should be mutable");
mFlags &= ~aFlags;
}
const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
PRUint32 GetAppUnitsPerDevUnit() const { return mAppUnitsPerDevUnit; }
gfxFontGroup *GetFontGroup() const { return mFontGroup; }
const PRUint8 *GetText8Bit() const
{ return (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) ? mText.mSingle : nsnull; }
@@ -1163,20 +1165,19 @@ public:
mStyle.Equals(other.mStyle);
}
const gfxFontStyle *GetStyle() const { return &mStyle; }
virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle) = 0;
/**
- * Tabs, CRs and LFs should be zero-width and invisible. They should
- * break up shaping.
+ * Tabs, CRs and LFs should not be passed in to MakeTextRun.
*/
- static PRBool IsInvisibleChar(PRUnichar ch) {
+ static PRBool IsInvalidChar(PRUnichar ch) {
return ch == '\t' || ch == '\r' || ch == '\n';
}
/**
* Make a textrun for an empty string. This is fast; if you call it,
* don't bother caching the result.
*/
gfxTextRun *MakeEmptyTextRun(const Parameters *aParams, PRUint32 aFlags);
--- a/gfx/thebes/public/gfxTextRunCache.h
+++ b/gfx/thebes/public/gfxTextRunCache.h
@@ -34,163 +34,90 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef GFX_TEXT_RUN_CACHE_H
#define GFX_TEXT_RUN_CACHE_H
#include "gfxFont.h"
-#include "nsCheapSets.h"
/**
- * A textrun cache object. A general textrun caching solution. If you use
- * this class to create a textrun cache, you are responsible for managing
- * textrun lifetimes. The full power of textrun creation is exposed; you can
- * set all textrun creation flags and parameters.
- */
-class THEBES_API gfxTextRunCache {
-public:
- gfxTextRunCache() {
- mCache.Init(100);
- }
- ~gfxTextRunCache() {
- NS_ASSERTION(mCache.Count() == 0, "Textrun cache not empty!");
- }
-
- /**
- * Get a textrun from the cache, create one if necessary.
- * @param aFlags the flags TEXT_IS_ASCII, TEXT_IS_8BIT and TEXT_HAS_SURROGATES
- * are ignored; the cache sets them based on the string.
- * @param aCallerOwns if this is null, the cache always creates a new
- * textrun owned by the caller. If non-null, the cache may return a textrun
- * that was previously created and is owned by some previous caller
- * to GetOrMakeTextRun on this cache. If so, *aCallerOwns will be set
- * to false.
- */
- gfxTextRun *GetOrMakeTextRun(const PRUnichar *aText, PRUint32 aLength,
- gfxFontGroup *aFontGroup,
- const gfxFontGroup::Parameters *aParams,
- PRUint32 aFlags, PRBool *aCallerOwns = nsnull);
- /**
- * Get a textrun from the cache, create one if necessary.
- * @param aFlags the flags TEXT_IS_ASCII, TEXT_IS_8BIT and TEXT_HAS_SURROGATES
- * are ignored; the cache sets them based on the string.
- * @param aCallerOwns if this is null, the cache always creates a new
- * textrun owned by the caller. If non-null, the cache may return a textrun
- * that was previously created and is owned by some previous caller
- * to GetOrMakeTextRun on this cache. If so, *aCallerOwns will be set
- * to false.
- */
- gfxTextRun *GetOrMakeTextRun(const PRUint8 *aText, PRUint32 aLength,
- gfxFontGroup *aFontGroup,
- const gfxFontGroup::Parameters *aParams,
- PRUint32 aFlags, PRBool *aCallerOwns = nsnull);
-
- /**
- * Notify that a text run was hit in the cache, a new one created, and
- * that the new one has replaced the old one in the cache.
- */
- virtual void NotifyRemovedFromCache(gfxTextRun *aTextRun) {}
-
- /**
- * Remove a textrun from the cache. This must be called before aTextRun
- * is deleted!
- */
- void RemoveTextRun(gfxTextRun *aTextRun);
-
- /** The following flags are part of the cache key: */
- enum { FLAG_MASK =
- gfxTextRunFactory::TEXT_IS_RTL |
- gfxTextRunFactory::TEXT_ENABLE_SPACING |
- gfxTextRunFactory::TEXT_ABSOLUTE_SPACING |
- gfxTextRunFactory::TEXT_ENABLE_NEGATIVE_SPACING |
- gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS |
- gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX
- };
-
-protected:
- struct THEBES_API CacheHashKey {
- void *mFontOrGroup;
- const void *mString;
- PRUint32 mLength;
- PRUint32 mAppUnitsPerDevUnit;
- PRUint32 mFlags;
- PRUint32 mStringHash;
-
- CacheHashKey(void *aFontOrGroup, const void *aString, PRUint32 aLength,
- PRUint32 aAppUnitsPerDevUnit, PRUint32 aFlags, PRUint32 aStringHash)
- : mFontOrGroup(aFontOrGroup), mString(aString), mLength(aLength),
- mAppUnitsPerDevUnit(aAppUnitsPerDevUnit), mFlags(aFlags),
- mStringHash(aStringHash) {}
- };
-
- class THEBES_API CacheHashEntry : public PLDHashEntryHdr {
- public:
- typedef const CacheHashKey &KeyType;
- typedef const CacheHashKey *KeyTypePointer;
-
- // When constructing a new entry in the hashtable, mTextRuns will be
- // blank. The caller of Put() will fill it in.
- CacheHashEntry(KeyTypePointer aKey) { }
- CacheHashEntry(const CacheHashEntry& toCopy) { NS_ERROR("Should not be called"); }
- ~CacheHashEntry() { }
-
- PRBool KeyEquals(const KeyTypePointer aKey) const;
- static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
- static PLDHashNumber HashKey(const KeyTypePointer aKey);
- enum { ALLOW_MEMMOVE = PR_TRUE };
-
- gfxTextRun *mTextRun;
- };
-
- CacheHashKey GetKeyForTextRun(gfxTextRun *aTextRun);
-
- nsTHashtable<CacheHashEntry> mCache;
-};
-
-/**
- * A simple global textrun cache for textruns that do not carry state
+ * A simple textrun cache for textruns that do not carry state
* (e.g., actual or potential linebreaks) and do not need complex initialization.
* The lifetimes of these textruns are managed by the cache (they are auto-expired
* after a certain period of time).
*/
-class THEBES_API gfxGlobalTextRunCache {
+class THEBES_API gfxTextRunCache {
public:
/**
- * Get a textrun for the given text, using a global cache. The returned
- * textrun is valid until the next event loop. We own it, the caller
- * must not free it.
+ * Get a textrun for the given text, using a global cache. The textrun
+ * must be released via ReleaseTextRun, not deleted.
* Do not set any state in the textrun (e.g. actual or potential linebreaks).
* Flags IS_8BIT, IS_ASCII and HAS_SURROGATES are automatically set
* appropriately.
* Flag IS_PERSISTENT must NOT be set unless aText is guaranteed to live
* forever.
+ * The string can contain any characters, invalid ones will be stripped
+ * properly.
*/
- static gfxTextRun *GetTextRun(const PRUnichar *aText, PRUint32 aLength,
- gfxFontGroup *aFontGroup,
- gfxContext *aRefContext,
- PRUint32 aAppUnitsPerDevUnit,
- PRUint32 aFlags);
+ static gfxTextRun *MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ gfxContext *aRefContext,
+ PRUint32 aAppUnitsPerDevUnit,
+ PRUint32 aFlags);
/**
- * Get a textrun for the given text, using a global cache. The returned
- * textrun is valid until the next event loop. We own it, the caller
- * must not free it.
+ * As above, but allows a full Parameters object to be passed in.
+ */
+ static gfxTextRun *MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ const gfxTextRunFactory::Parameters* aParams,
+ PRUint32 aFlags);
+
+ /**
+ * Get a textrun for the given text, using a global cache. The textrun
+ * must be released via ReleaseTextRun, not deleted.
* Do not set any state in the textrun (e.g. actual or potential linebreaks).
* Flags IS_8BIT, IS_ASCII and HAS_SURROGATES are automatically set
* appropriately.
* Flag IS_PERSISTENT must NOT be set unless aText is guaranteed to live
* forever.
+ * The string can contain any characters, invalid ones will be stripped
+ * properly.
*/
- static gfxTextRun *GetTextRun(const PRUint8 *aText, PRUint32 aLength,
- gfxFontGroup *aFontGroup,
- gfxContext *aRefContext,
- PRUint32 aAppUnitsPerDevUnit,
- PRUint32 aFlags);
+ static gfxTextRun *MakeTextRun(const PRUint8 *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ gfxContext *aRefContext,
+ PRUint32 aAppUnitsPerDevUnit,
+ PRUint32 aFlags);
+
+ /**
+ * Release a previously acquired textrun. Consider using AutoTextRun
+ * instead of calling this.
+ */
+ static void ReleaseTextRun(gfxTextRun *aTextRun);
+
+ class AutoTextRun {
+ public:
+ AutoTextRun(gfxTextRun *aTextRun) : mTextRun(aTextRun) {}
+ AutoTextRun() : mTextRun(nsnull) {}
+ AutoTextRun& operator=(gfxTextRun *aTextRun) {
+ gfxTextRunCache::ReleaseTextRun(mTextRun);
+ mTextRun = aTextRun;
+ return *this;
+ }
+ ~AutoTextRun() {
+ gfxTextRunCache::ReleaseTextRun(mTextRun);
+ }
+ gfxTextRun *get() { return mTextRun; }
+ gfxTextRun *operator->() { return mTextRun; }
+ private:
+ gfxTextRun *mTextRun;
+ };
protected:
friend class gfxPlatform;
static nsresult Init();
static void Shutdown();
};
--- a/gfx/thebes/public/gfxTextRunWordCache.h
+++ b/gfx/thebes/public/gfxTextRunWordCache.h
@@ -41,106 +41,51 @@
#include "gfxFont.h"
/**
* Cache individual "words" (strings delimited by white-space or white-space-like
* characters that don't involve kerning or ligatures) in textruns.
*/
class THEBES_API gfxTextRunWordCache {
public:
- gfxTextRunWordCache() {
- mCache.Init(100);
- }
- ~gfxTextRunWordCache() {
- NS_ASSERTION(mCache.Count() == 0, "Textrun cache not empty!");
- }
+ enum { TEXT_IN_CACHE = 0x10000000 };
/**
* Create a textrun using cached words.
+ * Invalid characters (see gfxFontGroup::IsInvalidChar) will be automatically
+ * treated as invisible missing.
* @param aFlags the flags TEXT_IS_ASCII and TEXT_HAS_SURROGATES must be set
- * by the caller, if applicable
- * @param aIsInCache if true is returned, then RemoveTextRun must be called
- * before the textrun changes or dies.
+ * by the caller, if applicable; TEXT_IN_CACHE is added if we
+ * have a reference to the textrun in the cache and RemoveTextRun must
+ * be called when the textrun dies.
*/
- gfxTextRun *MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
- gfxFontGroup *aFontGroup,
- const gfxFontGroup::Parameters *aParams,
- PRUint32 aFlags, PRBool *aIsInCache);
+ static gfxTextRun *MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ const gfxFontGroup::Parameters *aParams,
+ PRUint32 aFlags);
/**
- * Create a textrun using cached words
- * @param aFlags the flag TEXT_IS_ASCII must be set by the caller,
- * if applicable
- * @param aIsInCache if true is returned, then RemoveTextRun must be called
- * before the textrun changes or dies.
+ * Create a textrun using cached words.
+ * Invalid characters (see gfxFontGroup::IsInvalidChar) will be automatically
+ * treated as invisible missing.
+ * @param aFlags the flags TEXT_IS_ASCII and TEXT_HAS_SURROGATES must be set
+ * by the caller, if applicable; TEXT_IN_CACHE is added if we
+ * have a reference to the textrun in the cache and RemoveTextRun must
+ * be called when the textrun dies.
*/
- gfxTextRun *MakeTextRun(const PRUint8 *aText, PRUint32 aLength,
- gfxFontGroup *aFontGroup,
- const gfxFontGroup::Parameters *aParams,
- PRUint32 aFlags, PRBool *aIsInCache);
+ static gfxTextRun *MakeTextRun(const PRUint8 *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ const gfxFontGroup::Parameters *aParams,
+ PRUint32 aFlags);
/**
* Remove a textrun from the cache. This must be called before aTextRun
* is deleted! The text in the textrun must still be valid.
*/
- void RemoveTextRun(gfxTextRun *aTextRun);
+ static void RemoveTextRun(gfxTextRun *aTextRun);
protected:
- struct THEBES_API CacheHashKey {
- void *mFontOrGroup;
- const void *mString;
- PRUint32 mLength;
- PRUint32 mAppUnitsPerDevUnit;
- PRUint32 mStringHash;
- PRPackedBool mIsDoubleByteText;
- };
-
- class THEBES_API CacheHashEntry : public PLDHashEntryHdr {
- public:
- typedef const CacheHashKey &KeyType;
- typedef const CacheHashKey *KeyTypePointer;
-
- // When constructing a new entry in the hashtable, the caller of Put()
- // will fill us in.
- CacheHashEntry(KeyTypePointer aKey) : mTextRun(nsnull), mWordOffset(0),
- mHashedByFont(PR_FALSE) { }
- CacheHashEntry(const CacheHashEntry& toCopy) { NS_ERROR("Should not be called"); }
- ~CacheHashEntry() { }
-
- PRBool KeyEquals(const KeyTypePointer aKey) const;
- static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
- static PLDHashNumber HashKey(const KeyTypePointer aKey);
- enum { ALLOW_MEMMOVE = PR_TRUE };
+ friend class gfxPlatform;
- gfxTextRun *mTextRun;
- // The offset of the start of the word in the textrun. The length of
- // the word is not stored here because we can figure it out by
- // looking at the textrun's text.
- PRUint32 mWordOffset:31;
- // This is set to true when the cache entry was hashed by the first
- // font in mTextRun's fontgroup; it's false when the cache entry
- // was hashed by the fontgroup itself.
- PRUint32 mHashedByFont:1;
- };
-
- // Used to track words that should be copied from one textrun to
- // another during the textrun construction process
- struct DeferredWord {
- gfxTextRun *mSourceTextRun;
- PRUint32 mSourceOffset;
- PRUint32 mDestOffset;
- PRUint32 mLength;
- PRUint32 mHash;
- };
-
- PRBool LookupWord(gfxTextRun *aTextRun, gfxFont *aFirstFont,
- PRUint32 aStart, PRUint32 aEnd, PRUint32 aHash,
- nsTArray<DeferredWord>* aDeferredWords);
- void FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun,
- gfxContext *aContext,
- const nsTArray<DeferredWord>& aDeferredWords,
- PRBool aSuccessful);
- void RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart,
- PRUint32 aEnd, PRUint32 aHash);
-
- nsTHashtable<CacheHashEntry> mCache;
+ static nsresult Init();
+ static void Shutdown();
};
#endif /* GFX_TEXT_RUN_WORD_CACHE_H */
--- a/gfx/thebes/src/gfxAtsuiFonts.cpp
+++ b/gfx/thebes/src/gfxAtsuiFonts.cpp
@@ -682,32 +682,28 @@ SetGlyphsForCharacterGroup(ATSLayoutReco
++regularGlyphCount;
displayGlyph = glyph;
}
if (i > 0 && aRun->IsRightToLeft() != (offset < aGlyphs[i - 1].originalOffset)) {
inOrder = PR_FALSE;
}
}
- gfxTextRun::CompressedGlyph g;
- if (gfxFontGroup::IsInvisibleChar(aString[firstOffset/2])) {
- NS_ASSERTION(firstOffset == lastOffset,
- "Invisible character grouped with other characters?");
- aRun->SetCharacterGlyph(aSegmentStart + firstOffset/2, g.SetMissing());
- return;
- }
+ NS_ASSERTION(!gfxFontGroup::IsInvalidChar(aString[firstOffset/2]),
+ "Invalid char passed in");
if (!allMatched) {
for (i = firstOffset; i <= lastOffset; ++i) {
PRUint32 index = i/2;
aRun->SetMissingGlyph(aSegmentStart + index, aString[index]);
}
return;
}
+ gfxTextRun::CompressedGlyph g;
PRUint32 offset;
for (offset = firstOffset + 2; offset <= lastOffset; offset += 2) {
PRUint32 index = offset/2;
if (!inOrder) {
// Because the characters in this group were not in the textrun's
// required order, we must make the entire group an indivisible cluster
aRun->SetCharacterGlyph(aSegmentStart + index, g.SetClusterContinuation());
} else if (!aRun->GetCharacterGlyphs()[index].IsClusterContinuation()) {
--- a/gfx/thebes/src/gfxPangoFonts.cpp
+++ b/gfx/thebes/src/gfxPangoFonts.cpp
@@ -1057,21 +1057,19 @@ SetGlyphsForCharacterGroup(const PangoGl
// Skip the CompressedGlyph that we have added, but check if the
// character was supposed to be ignored. If it's supposed to be ignored,
// overwrite the textrun entry with an invisible missing-glyph.
gunichar ch = g_utf8_get_char(p);
NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
if (ch >= 0x10000) {
// Skip surrogate
++utf16Offset;
- } else {
- if (gfxFontGroup::IsInvisibleChar(PRUnichar(ch))) {
- aTextRun->SetCharacterGlyph(utf16Offset, g.SetMissing());
- }
}
+ NS_ASSERTION(!gfxFontGroup::IsInvalidChar(PRUnichar(ch)),
+ "Invalid character detected");
++utf16Offset;
// We produced this UTF8 so we don't need to worry about malformed stuff
p = g_utf8_next_char(p);
if (p >= end)
break;
if (utf16Offset >= textRunLength) {
@@ -1251,19 +1249,18 @@ gfxPangoFontGroup::CreateGlyphRunsXft(gf
// g_unichar_validate(ch) may be mildly useful.
gunichar ch = g_utf8_get_char(p);
p = g_utf8_next_char(p);
if (ch == 0) {
// treat this null byte as a missing glyph. Pango
// doesn't create glyphs for these, not even missing-glyphs.
aTextRun->SetMissingGlyph(utf16Offset, 0);
- } else if (ch < 0x10000 && IsInvisibleChar(PRUnichar(ch))) {
- aTextRun->SetCharacterGlyph(utf16Offset, g.SetMissing());
} else {
+ NS_ASSERTION(!IsInvalidChar(ch), "Invalid char detected");
FT_UInt glyph = XftCharIndex(dpy, xfont, ch);
XGlyphInfo info;
XftGlyphExtents(dpy, xfont, &glyph, 1, &info);
if (info.yOff > 0) {
NS_WARNING("vertical offsets not supported");
}
PRInt32 advance = info.xOff*appUnitsPerDevUnit;
--- a/gfx/thebes/src/gfxPlatform.cpp
+++ b/gfx/thebes/src/gfxPlatform.cpp
@@ -48,16 +48,17 @@
#include "gfxBeOSPlatform.h"
#elif defined(XP_OS2)
#include "gfxOS2Platform.h"
#endif
#include "gfxContext.h"
#include "gfxImageSurface.h"
#include "gfxTextRunCache.h"
+#include "gfxTextRunWordCache.h"
#include "nsIPref.h"
#include "nsServiceManagerUtils.h"
#ifdef MOZ_ENABLE_GLITZ
#include <stdlib.h>
#endif
@@ -103,32 +104,40 @@ gfxPlatform::Init()
rv = gfxFontCache::Init();
if (NS_FAILED(rv)) {
NS_ERROR("Could not initialize gfxFontCache");
Shutdown();
return rv;
}
- rv = gfxGlobalTextRunCache::Init();
+ rv = gfxTextRunWordCache::Init();
if (NS_FAILED(rv)) {
- NS_ERROR("Could not initialize gfxGlobalTextRunCache");
+ NS_ERROR("Could not initialize gfxTextRunWordCache");
+ Shutdown();
+ return rv;
+ }
+
+ rv = gfxTextRunCache::Init();
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Could not initialize gfxTextRunCache");
Shutdown();
return rv;
}
return NS_OK;
}
void
gfxPlatform::Shutdown()
{
// These may be called before the corresponding subsystems have actually
// started up. That's OK, they can handle it.
- gfxGlobalTextRunCache::Shutdown();
+ gfxTextRunCache::Shutdown();
+ gfxTextRunWordCache::Shutdown();
gfxFontCache::Shutdown();
#if defined(XP_MACOSX)
gfxQuartzFontCache::Shutdown();
#endif
delete gPlatform;
gPlatform = nsnull;
}
--- a/gfx/thebes/src/gfxTextRunCache.cpp
+++ b/gfx/thebes/src/gfxTextRunCache.cpp
@@ -31,351 +31,106 @@
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "gfxTextRunCache.h"
+#include "gfxTextRunWordCache.h"
#include "nsExpirationTracker.h"
-static inline PRUint32
-HashMix(PRUint32 aHash, PRUnichar aCh)
-{
- return (aHash >> 28) ^ (aHash << 4) ^ aCh;
-}
-
-static PRUint32
-HashString(const PRUnichar *aText, PRUint32 aLength, PRUint32 *aFlags)
-{
- *aFlags &= ~(gfxFontGroup::TEXT_HAS_SURROGATES | gfxFontGroup::TEXT_IS_ASCII);
- PRUint32 i;
- PRUint32 hashCode = 0;
- PRUnichar allBits = 0;
- for (i = 0; i < aLength; ++i) {
- PRUnichar ch = aText[i];
- hashCode = HashMix(hashCode, ch);
- allBits |= ch;
- if (IS_SURROGATE(ch)) {
- *aFlags |= gfxFontGroup::TEXT_HAS_SURROGATES;
- }
- }
- if (!(allBits & ~0x7F)) {
- *aFlags |= gfxFontGroup::TEXT_IS_ASCII;
- }
- return hashCode;
-}
-
-static PRUint32
-HashString(const PRUint8 *aText, PRUint32 aLength, PRUint32 *aFlags)
-{
- *aFlags &= ~(gfxFontGroup::TEXT_HAS_SURROGATES | gfxFontGroup::TEXT_IS_ASCII);
- *aFlags |= gfxFontGroup::TEXT_IS_8BIT;
- PRUint32 i;
- PRUint32 hashCode = 0;
- PRUint8 allBits = 0;
- for (i = 0; i < aLength; ++i) {
- PRUint8 ch = aText[i];
- hashCode = HashMix(hashCode, ch);
- allBits |= ch;
- }
- if (!(allBits & ~0x7F)) {
- *aFlags |= gfxFontGroup::TEXT_IS_ASCII;
- }
- return hashCode;
-}
-
-static void *GetCacheKeyFontOrGroup(gfxTextRun *aTextRun)
-{
- PRUint32 glyphRunCount;
- const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&glyphRunCount);
- gfxFontGroup *fontGroup = aTextRun->GetFontGroup();
- gfxFont *firstFont = fontGroup->GetFontAt(0);
- return glyphRunCount == 1 && glyphRuns[0].mFont == firstFont
- ? NS_STATIC_CAST(void *, firstFont)
- : NS_STATIC_CAST(void *, fontGroup);
-}
-
-gfxTextRun *
-gfxTextRunCache::GetOrMakeTextRun(const PRUnichar *aText, PRUint32 aLength,
- gfxFontGroup *aFontGroup,
- const gfxFontGroup::Parameters *aParams,
- PRUint32 aFlags, PRBool *aCallerOwns)
-{
- if (aCallerOwns) {
- *aCallerOwns = PR_TRUE;
- }
- if (aLength == 0) {
- aFlags |= gfxFontGroup::TEXT_IS_PERSISTENT;
- } else if (aLength == 1 && aText[0] == ' ') {
- aFlags |= gfxFontGroup::TEXT_IS_PERSISTENT;
- static const PRUnichar space = ' ';
- aText = &space;
- }
-
- PRUint32 hashCode = HashString(aText, aLength, &aFlags);
- gfxFont *font = aFontGroup->GetFontAt(0);
- CacheHashKey key(font, aText, aLength, aParams->mAppUnitsPerDevUnit, aFlags, hashCode);
- CacheHashEntry *entry = nsnull;
- if (font) {
- entry = mCache.GetEntry(key);
- }
- if (!entry) {
- key.mFontOrGroup = aFontGroup;
- entry = mCache.GetEntry(key);
- }
- if (entry) {
- gfxTextRun *textRun = entry->mTextRun;
- if (aCallerOwns) {
- *aCallerOwns = PR_FALSE;
- return textRun;
- }
- gfxTextRun *newRun =
- textRun->Clone(aParams, aText, aLength, aFontGroup, aFlags);
- if (newRun) {
- newRun->SetHashCode(hashCode);
- entry->mTextRun = newRun;
- NotifyRemovedFromCache(textRun);
- return newRun;
- }
- }
-
- gfxTextRun *newRun =
- aFontGroup->MakeTextRun(aText, aLength, aParams, aFlags);
- if (newRun) {
- newRun->SetHashCode(hashCode);
- key.mFontOrGroup = GetCacheKeyFontOrGroup(newRun);
- entry = mCache.PutEntry(key);
- if (entry) {
- entry->mTextRun = newRun;
- }
- NS_ASSERTION(!entry || entry == mCache.GetEntry(GetKeyForTextRun(newRun)),
- "Inconsistent hashing");
- }
- return newRun;
-}
-
-gfxTextRun *
-gfxTextRunCache::GetOrMakeTextRun(const PRUint8 *aText, PRUint32 aLength,
- gfxFontGroup *aFontGroup,
- const gfxFontGroup::Parameters *aParams,
- PRUint32 aFlags, PRBool *aCallerOwns)
-{
- if (aCallerOwns) {
- *aCallerOwns = PR_TRUE;
- }
- if (aLength == 0) {
- aFlags |= gfxFontGroup::TEXT_IS_PERSISTENT;
- } else if (aLength == 1 && aText[0] == ' ') {
- aFlags |= gfxFontGroup::TEXT_IS_PERSISTENT;
- static const PRUint8 space = ' ';
- aText = &space;
- }
-
- PRUint32 hashCode = HashString(aText, aLength, &aFlags);
- gfxFont *font = aFontGroup->GetFontAt(0);
- CacheHashKey key(font, aText, aLength, aParams->mAppUnitsPerDevUnit, aFlags, hashCode);
- CacheHashEntry *entry = nsnull;
- if (font) {
- entry = mCache.GetEntry(key);
- }
- if (!entry) {
- key.mFontOrGroup = aFontGroup;
- entry = mCache.GetEntry(key);
- }
- if (entry) {
- gfxTextRun *textRun = entry->mTextRun;
- if (aCallerOwns) {
- *aCallerOwns = PR_FALSE;
- return textRun;
- }
-
- gfxTextRun *newRun =
- textRun->Clone(aParams, aText, aLength,
- aFontGroup, aFlags);
- if (newRun) {
- newRun->SetHashCode(hashCode);
- entry->mTextRun = newRun;
- NotifyRemovedFromCache(textRun);
- return newRun;
- }
- }
-
- gfxTextRun *newRun =
- aFontGroup->MakeTextRun(aText, aLength, aParams, aFlags);
- if (newRun) {
- newRun->SetHashCode(hashCode);
- key.mFontOrGroup = GetCacheKeyFontOrGroup(newRun);
- entry = mCache.PutEntry(key);
- if (entry) {
- entry->mTextRun = newRun;
- }
- NS_ASSERTION(!entry || entry == mCache.GetEntry(GetKeyForTextRun(newRun)),
- "Inconsistent hashing");
- }
- return newRun;
-}
-
-gfxTextRunCache::CacheHashKey
-gfxTextRunCache::GetKeyForTextRun(gfxTextRun *aTextRun)
-{
- const void *text;
- PRUint32 length = aTextRun->GetLength();
- if (aTextRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) {
- text = aTextRun->GetText8Bit();
- } else {
- text = aTextRun->GetTextUnicode();
- }
- void *fontOrGroup = GetCacheKeyFontOrGroup(aTextRun);
- return CacheHashKey(fontOrGroup, text, length, aTextRun->GetAppUnitsPerDevUnit(),
- aTextRun->GetFlags(), aTextRun->GetHashCode());
-}
-
-void
-gfxTextRunCache::RemoveTextRun(gfxTextRun *aTextRun)
-{
- CacheHashKey key = GetKeyForTextRun(aTextRun);
-#ifdef DEBUG
- CacheHashEntry *entry = mCache.GetEntry(key);
- NS_ASSERTION(entry && entry->mTextRun == aTextRun,
- "Failed to find textrun in cache");
-#endif
- mCache.RemoveEntry(key);
-}
-
-static PRBool
-CompareDifferentWidthStrings(const PRUint8 *aStr1, const PRUnichar *aStr2,
- PRUint32 aLength)
-{
- PRUint32 i;
- for (i = 0; i < aLength; ++i) {
- if (aStr1[i] != aStr2[i])
- return PR_FALSE;
- }
- return PR_TRUE;
-}
-
-PRBool
-gfxTextRunCache::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
-{
- gfxTextRun *textRun = mTextRun;
- if (!textRun)
- return PR_FALSE;
- PRUint32 length = textRun->GetLength();
- if (aKey->mFontOrGroup != GetCacheKeyFontOrGroup(textRun) ||
- aKey->mLength != length ||
- aKey->mAppUnitsPerDevUnit != textRun->GetAppUnitsPerDevUnit() ||
- ((aKey->mFlags ^ textRun->GetFlags()) & FLAG_MASK))
- return PR_FALSE;
-
- if (textRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) {
- if (aKey->mFlags & gfxFontGroup::TEXT_IS_8BIT)
- return memcmp(textRun->GetText8Bit(), aKey->mString, length) == 0;
- return CompareDifferentWidthStrings(textRun->GetText8Bit(),
- NS_STATIC_CAST(const PRUnichar *, aKey->mString), length);
- } else {
- if (!(aKey->mFlags & gfxFontGroup::TEXT_IS_8BIT))
- return memcmp(textRun->GetTextUnicode(), aKey->mString, length*sizeof(PRUnichar)) == 0;
- return CompareDifferentWidthStrings(NS_STATIC_CAST(const PRUint8 *, aKey->mString),
- textRun->GetTextUnicode(), length);
- }
-}
-
-PLDHashNumber
-gfxTextRunCache::CacheHashEntry::HashKey(const KeyTypePointer aKey)
-{
- return aKey->mStringHash + (long)aKey->mFontOrGroup + aKey->mAppUnitsPerDevUnit +
- (aKey->mFlags & FLAG_MASK);
-}
-
/*
* Cache textruns and expire them after 3*10 seconds of no use
*/
-class TextRunCache : public nsExpirationTracker<gfxTextRun,3> {
+class TextRunExpiringCache : public nsExpirationTracker<gfxTextRun,3> {
public:
- enum { TIMEOUT_SECONDS = 10 };
- TextRunCache()
- : nsExpirationTracker<gfxTextRun,3>(TIMEOUT_SECONDS*1000) {}
- ~TextRunCache() {
+ TextRunExpiringCache()
+ : nsExpirationTracker<gfxTextRun,3>(10*1000) {}
+ ~TextRunExpiringCache() {
AgeAllGenerations();
}
// This gets called when the timeout has expired on a gfxTextRun
virtual void NotifyExpired(gfxTextRun *aTextRun) {
RemoveObject(aTextRun);
- mCache.RemoveTextRun(aTextRun);
+ gfxTextRunWordCache::RemoveTextRun(aTextRun);
delete aTextRun;
}
-
- gfxTextRunCache mCache;
};
-static TextRunCache *gTextRunCache = nsnull;
+static TextRunExpiringCache *gTextRunCache = nsnull;
-static nsresult
-UpdateOwnership(gfxTextRun *aTextRun, PRBool aOwned)
+gfxTextRun *
+gfxTextRunCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ const gfxTextRunFactory::Parameters* aParams,
+ PRUint32 aFlags)
{
- if (!aTextRun)
+ if (!gTextRunCache)
return nsnull;
- if (aOwned)
- return gTextRunCache->AddObject(aTextRun);
- if (!aTextRun->GetExpirationState()->IsTracked())
- return NS_OK;
- return gTextRunCache->MarkUsed(aTextRun);
+ return gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup,
+ aParams, aFlags);
}
gfxTextRun *
-gfxGlobalTextRunCache::GetTextRun(const PRUnichar *aText, PRUint32 aLength,
- gfxFontGroup *aFontGroup,
- gfxContext *aRefContext,
- PRUint32 aAppUnitsPerDevUnit,
- PRUint32 aFlags)
+gfxTextRunCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ gfxContext *aRefContext,
+ PRUint32 aAppUnitsPerDevUnit,
+ PRUint32 aFlags)
{
if (!gTextRunCache)
return nsnull;
- PRBool owned;
gfxTextRunFactory::Parameters params = {
aRefContext, nsnull, nsnull, nsnull, 0, aAppUnitsPerDevUnit
};
- nsAutoPtr<gfxTextRun> textRun;
- textRun = gTextRunCache->mCache.GetOrMakeTextRun(aText, aLength, aFontGroup, ¶ms, aFlags, &owned);
- nsresult rv = UpdateOwnership(textRun, owned);
- if (NS_FAILED(rv))
- return nsnull;
- return textRun.forget();
+ return gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup,
+ ¶ms, aFlags);
}
gfxTextRun *
-gfxGlobalTextRunCache::GetTextRun(const PRUint8 *aText, PRUint32 aLength,
- gfxFontGroup *aFontGroup,
- gfxContext *aRefContext,
- PRUint32 aAppUnitsPerDevUnit,
- PRUint32 aFlags)
+gfxTextRunCache::MakeTextRun(const PRUint8 *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ gfxContext *aRefContext,
+ PRUint32 aAppUnitsPerDevUnit,
+ PRUint32 aFlags)
{
if (!gTextRunCache)
return nsnull;
- PRBool owned;
gfxTextRunFactory::Parameters params = {
aRefContext, nsnull, nsnull, nsnull, 0, aAppUnitsPerDevUnit
- };
- nsAutoPtr<gfxTextRun> textRun;
- textRun = gTextRunCache->mCache.GetOrMakeTextRun(aText, aLength, aFontGroup, ¶ms, aFlags, &owned);
- nsresult rv = UpdateOwnership(textRun, owned);
- if (NS_FAILED(rv))
- return nsnull;
- return textRun.forget();
+ };
+ return gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup,
+ ¶ms, aFlags);
+}
+
+void
+gfxTextRunCache::ReleaseTextRun(gfxTextRun *aTextRun)
+{
+ if (!aTextRun)
+ return;
+ if (!(aTextRun->GetFlags() & gfxTextRunWordCache::TEXT_IN_CACHE)) {
+ delete aTextRun;
+ return;
+ }
+ nsresult rv = gTextRunCache->AddObject(aTextRun);
+ if (NS_FAILED(rv)) {
+ delete aTextRun;
+ return;
+ }
}
nsresult
-gfxGlobalTextRunCache::Init()
+gfxTextRunCache::Init()
{
- gTextRunCache = new TextRunCache();
+ gTextRunCache = new TextRunExpiringCache();
return gTextRunCache ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
void
-gfxGlobalTextRunCache::Shutdown()
+gfxTextRunCache::Shutdown()
{
delete gTextRunCache;
gTextRunCache = nsnull;
}
--- a/gfx/thebes/src/gfxTextRunWordCache.cpp
+++ b/gfx/thebes/src/gfxTextRunWordCache.cpp
@@ -32,16 +32,124 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "gfxTextRunWordCache.h"
+
+/**
+ * Cache individual "words" (strings delimited by white-space or white-space-like
+ * characters that don't involve kerning or ligatures) in textruns.
+ */
+class TextRunWordCache {
+public:
+ TextRunWordCache() {
+ mCache.Init(100);
+ }
+ ~TextRunWordCache() {
+ NS_ASSERTION(mCache.Count() == 0, "Textrun cache not empty!");
+ }
+
+ /**
+ * Create a textrun using cached words.
+ * Invalid characters (see gfxFontGroup::IsInvalidChar) will be automatically
+ * treated as invisible missing.
+ * @param aFlags the flags TEXT_IS_ASCII and TEXT_HAS_SURROGATES must be set
+ * by the caller, if applicable
+ * @param aIsInCache if true is returned, then RemoveTextRun must be called
+ * before the textrun changes or dies.
+ */
+ gfxTextRun *MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ const gfxFontGroup::Parameters *aParams,
+ PRUint32 aFlags);
+ /**
+ * Create a textrun using cached words.
+ * Invalid characters (see gfxFontGroup::IsInvalidChar) will be automatically
+ * treated as invisible missing.
+ * @param aFlags the flag TEXT_IS_ASCII must be set by the caller,
+ * if applicable
+ * @param aIsInCache if true is returned, then RemoveTextRun must be called
+ * before the textrun changes or dies.
+ */
+ gfxTextRun *MakeTextRun(const PRUint8 *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ const gfxFontGroup::Parameters *aParams,
+ PRUint32 aFlags);
+
+ /**
+ * Remove a textrun from the cache. This must be called before aTextRun
+ * is deleted! The text in the textrun must still be valid.
+ */
+ void RemoveTextRun(gfxTextRun *aTextRun);
+
+protected:
+ struct CacheHashKey {
+ void *mFontOrGroup;
+ const void *mString;
+ PRUint32 mLength;
+ PRUint32 mAppUnitsPerDevUnit;
+ PRUint32 mStringHash;
+ PRPackedBool mIsDoubleByteText;
+ };
+
+ class CacheHashEntry : public PLDHashEntryHdr {
+ public:
+ typedef const CacheHashKey &KeyType;
+ typedef const CacheHashKey *KeyTypePointer;
+
+ // When constructing a new entry in the hashtable, the caller of Put()
+ // will fill us in.
+ CacheHashEntry(KeyTypePointer aKey) : mTextRun(nsnull), mWordOffset(0),
+ mHashedByFont(PR_FALSE) { }
+ CacheHashEntry(const CacheHashEntry& toCopy) { NS_ERROR("Should not be called"); }
+ ~CacheHashEntry() { }
+
+ PRBool KeyEquals(const KeyTypePointer aKey) const;
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(const KeyTypePointer aKey);
+ enum { ALLOW_MEMMOVE = PR_TRUE };
+
+ gfxTextRun *mTextRun;
+ // The offset of the start of the word in the textrun. The length of
+ // the word is not stored here because we can figure it out by
+ // looking at the textrun's text.
+ PRUint32 mWordOffset:31;
+ // This is set to true when the cache entry was hashed by the first
+ // font in mTextRun's fontgroup; it's false when the cache entry
+ // was hashed by the fontgroup itself.
+ PRUint32 mHashedByFont:1;
+ };
+
+ // Used to track words that should be copied from one textrun to
+ // another during the textrun construction process
+ struct DeferredWord {
+ gfxTextRun *mSourceTextRun;
+ PRUint32 mSourceOffset;
+ PRUint32 mDestOffset;
+ PRUint32 mLength;
+ PRUint32 mHash;
+ };
+
+ PRBool LookupWord(gfxTextRun *aTextRun, gfxFont *aFirstFont,
+ PRUint32 aStart, PRUint32 aEnd, PRUint32 aHash,
+ nsTArray<DeferredWord>* aDeferredWords);
+ void FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun,
+ gfxContext *aContext,
+ const nsTArray<DeferredWord>& aDeferredWords,
+ PRBool aSuccessful);
+ void RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart,
+ PRUint32 aEnd, PRUint32 aHash);
+
+ nsTHashtable<CacheHashEntry> mCache;
+};
+
static PRLogModuleInfo *gWordCacheLog = PR_NewLogModule("wordCache");
static inline PRUint32
HashMix(PRUint32 aHash, PRUnichar aCh)
{
return (aHash >> 28) ^ (aHash << 4) ^ aCh;
}
@@ -74,17 +182,17 @@ static PRBool
IsBoundarySpace(PRUnichar aChar)
{
return aChar == ' ' || aChar == UNICODE_NBSP;
}
static PRBool
IsWordBoundary(PRUnichar aChar)
{
- return IsBoundarySpace(aChar) || gfxFontGroup::IsInvisibleChar(aChar);
+ return IsBoundarySpace(aChar) || gfxFontGroup::IsInvalidChar(aChar);
}
/**
* Looks up a word in the cache. If the word is found in the cache
* (which could be from an existing textrun or an earlier word in the same
* textrun), we copy the glyphs from the word into the textrun, unless
* aDeferredWords is non-null (meaning that all words from now on must be
* copied later instead of now), in which case we add the word to be copied
@@ -99,19 +207,19 @@ IsWordBoundary(PRUnichar aChar)
* 2) we need to record words that appear in the string in some kind
* of hash table so we can detect and use them if they appear later in the
* (in general the string might be huge and contain many repeated words).
* We might as well use the master hash table for this.
*
* @return true if the word was found in the cache, false otherwise.
*/
PRBool
-gfxTextRunWordCache::LookupWord(gfxTextRun *aTextRun, gfxFont *aFirstFont,
- PRUint32 aStart, PRUint32 aEnd, PRUint32 aHash,
- nsTArray<DeferredWord>* aDeferredWords)
+TextRunWordCache::LookupWord(gfxTextRun *aTextRun, gfxFont *aFirstFont,
+ PRUint32 aStart, PRUint32 aEnd, PRUint32 aHash,
+ nsTArray<DeferredWord>* aDeferredWords)
{
if (aEnd <= aStart)
return PR_TRUE;
PRUint32 length = aEnd - aStart;
CacheHashKey key =
{ aFirstFont, aTextRun->GetTextAt(aStart),
length, aTextRun->GetAppUnitsPerDevUnit(), aHash,
@@ -167,21 +275,23 @@ gfxTextRunWordCache::LookupWord(gfxTextR
* with that font in the key, and add a new entry keyed with the fontgroup
* instead.
*
* @param aSuccessful if false, then we don't do any word copies and we don't
* add anything to the cache, but we still remove all the optimistic cache
* entries.
*/
void
-gfxTextRunWordCache::FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun,
- gfxContext *aContext,
- const nsTArray<DeferredWord>& aDeferredWords,
- PRBool aSuccessful)
+TextRunWordCache::FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun,
+ gfxContext *aContext,
+ const nsTArray<DeferredWord>& aDeferredWords,
+ PRBool aSuccessful)
{
+ aTextRun->SetFlagBits(gfxTextRunWordCache::TEXT_IN_CACHE);
+
PRUint32 i;
gfxFontGroup *fontGroup = aTextRun->GetFontGroup();
gfxFont *font = fontGroup->GetFontAt(0);
// copy deferred words from various sources into destination textrun
for (i = 0; i < aDeferredWords.Length(); ++i) {
const DeferredWord *word = &aDeferredWords[i];
gfxTextRun *source = word->mSourceTextRun;
if (!source) {
@@ -236,20 +346,20 @@ gfxTextRunWordCache::FinishTextRun(gfxTe
aTextRun->SetSpaceGlyph(font, aContext, charIndex);
}
}
}
}
}
gfxTextRun *
-gfxTextRunWordCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
- gfxFontGroup *aFontGroup,
- const gfxFontGroup::Parameters *aParams,
- PRUint32 aFlags, PRBool *aIsInCache)
+TextRunWordCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ const gfxFontGroup::Parameters *aParams,
+ PRUint32 aFlags)
{
nsAutoPtr<gfxTextRun> textRun;
textRun = new gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags);
if (!textRun || !textRun->GetCharacterGlyphs())
return nsnull;
gfxFont *font = aFontGroup->GetFontAt(0);
nsresult rv = textRun->AddGlyphRun(font, 0);
@@ -293,37 +403,35 @@ gfxTextRunWordCache::MakeTextRun(const P
hash = HashMix(hash, ch);
}
}
if (deferredWords.Length() == 0) {
// We got everything from the cache, so we're done. No point in calling
// FinishTextRun.
// This textrun is not referenced by the cache.
- *aIsInCache = PR_FALSE;
return textRun.forget();
}
- *aIsInCache = PR_TRUE;
// create textrun for unknown words
gfxTextRunFactory::Parameters params =
{ aParams->mContext, nsnull, nsnull, nsnull, 0, aParams->mAppUnitsPerDevUnit };
nsAutoPtr<gfxTextRun> newRun;
newRun = aFontGroup->MakeTextRun(tempString.Elements(), tempString.Length(),
¶ms, aFlags | gfxTextRunFactory::TEXT_IS_PERSISTENT);
FinishTextRun(textRun, newRun, aParams->mContext, deferredWords, newRun != nsnull);
return textRun.forget();
}
gfxTextRun *
-gfxTextRunWordCache::MakeTextRun(const PRUint8 *aText, PRUint32 aLength,
- gfxFontGroup *aFontGroup,
- const gfxFontGroup::Parameters *aParams,
- PRUint32 aFlags, PRBool *aIsInCache)
+TextRunWordCache::MakeTextRun(const PRUint8 *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ const gfxFontGroup::Parameters *aParams,
+ PRUint32 aFlags)
{
aFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
nsAutoPtr<gfxTextRun> textRun;
textRun = new gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags);
if (!textRun || !textRun->GetCharacterGlyphs())
return nsnull;
gfxFont *font = aFontGroup->GetFontAt(0);
@@ -368,35 +476,33 @@ gfxTextRunWordCache::MakeTextRun(const P
hash = HashMix(hash, ch);
}
}
if (deferredWords.Length() == 0) {
// We got everything from the cache, so we're done. No point in calling
// FinishTextRun.
// This textrun is not referenced by the cache.
- *aIsInCache = PR_FALSE;
return textRun.forget();
}
- *aIsInCache = PR_TRUE;
// create textrun for unknown words
gfxTextRunFactory::Parameters params =
{ aParams->mContext, nsnull, nsnull, nsnull, 0, aParams->mAppUnitsPerDevUnit };
nsAutoPtr<gfxTextRun> newRun;
newRun = aFontGroup->MakeTextRun(tempString.Elements(), tempString.Length(),
¶ms, aFlags | gfxTextRunFactory::TEXT_IS_PERSISTENT);
FinishTextRun(textRun, newRun, aParams->mContext, deferredWords, newRun != nsnull);
return textRun.forget();
}
void
-gfxTextRunWordCache::RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart,
- PRUint32 aEnd, PRUint32 aHash)
+TextRunWordCache::RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart,
+ PRUint32 aEnd, PRUint32 aHash)
{
if (aEnd <= aStart)
return;
PRUint32 length = aEnd - aStart;
CacheHashKey key =
{ GetWordFontOrGroup(aTextRun, aStart, length), aTextRun->GetTextAt(aStart),
length, aTextRun->GetAppUnitsPerDevUnit(), aHash,
@@ -409,17 +515,17 @@ gfxTextRunWordCache::RemoveWord(gfxTextR
PR_LOG(gWordCacheLog, PR_LOG_DEBUG, ("%p(%d-%d,%d): removed using %s",
aTextRun, aStart, aEnd - aStart, aHash,
key.mFontOrGroup == aTextRun->GetFontGroup() ? "fontgroup" : "font"));
}
}
// Remove a textrun from the cache by looking up each word and removing it
void
-gfxTextRunWordCache::RemoveTextRun(gfxTextRun *aTextRun)
+TextRunWordCache::RemoveTextRun(gfxTextRun *aTextRun)
{
PRUint32 i;
PRUint32 wordStart = 0;
PRUint32 hash = 0;
for (i = 0; i < aTextRun->GetLength(); ++i) {
PRUnichar ch = aTextRun->GetChar(i);
if (IsWordBoundary(ch)) {
RemoveWord(aTextRun, wordStart, i, hash);
@@ -459,17 +565,17 @@ static void *
GetFontOrGroup(gfxFontGroup *aFontGroup, PRBool aUseFont)
{
return aUseFont
? NS_STATIC_CAST(void *, aFontGroup->GetFontAt(0))
: NS_STATIC_CAST(void *, aFontGroup);
}
PRBool
-gfxTextRunWordCache::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
+TextRunWordCache::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
{
if (!mTextRun)
return PR_FALSE;
PRUint32 length = aKey->mLength;
gfxFontGroup *fontGroup = mTextRun->GetFontGroup();
if (!IsWordEnd(mTextRun, mWordOffset + length) ||
GetFontOrGroup(fontGroup, mHashedByFont) != aKey->mFontOrGroup ||
@@ -487,13 +593,59 @@ gfxTextRunWordCache::CacheHashEntry::Key
if (aKey->mIsDoubleByteText)
return memcmp(text, aKey->mString, length*sizeof(PRUnichar)) == 0;
return CompareDifferentWidthStrings(NS_STATIC_CAST(const PRUint8 *, aKey->mString),
text, length);
}
}
PLDHashNumber
-gfxTextRunWordCache::CacheHashEntry::HashKey(const KeyTypePointer aKey)
+TextRunWordCache::CacheHashEntry::HashKey(const KeyTypePointer aKey)
{
return aKey->mStringHash + (long)aKey->mFontOrGroup + aKey->mAppUnitsPerDevUnit +
aKey->mIsDoubleByteText;
}
+
+static TextRunWordCache *gTextRunWordCache = nsnull;
+
+nsresult
+gfxTextRunWordCache::Init()
+{
+ gTextRunWordCache = new TextRunWordCache();
+ return gTextRunWordCache ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+void
+gfxTextRunWordCache::Shutdown()
+{
+ delete gTextRunWordCache;
+ gTextRunWordCache = nsnull;
+}
+
+gfxTextRun *
+gfxTextRunWordCache::MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ const gfxFontGroup::Parameters *aParams,
+ PRUint32 aFlags)
+{
+ if (!gTextRunWordCache)
+ return nsnull;
+ return gTextRunWordCache->MakeTextRun(aText, aLength, aFontGroup, aParams, aFlags);
+}
+
+gfxTextRun *
+gfxTextRunWordCache::MakeTextRun(const PRUint8 *aText, PRUint32 aLength,
+ gfxFontGroup *aFontGroup,
+ const gfxFontGroup::Parameters *aParams,
+ PRUint32 aFlags)
+{
+ if (!gTextRunWordCache)
+ return nsnull;
+ return gTextRunWordCache->MakeTextRun(aText, aLength, aFontGroup, aParams, aFlags);
+}
+
+void
+gfxTextRunWordCache::RemoveTextRun(gfxTextRun *aTextRun)
+{
+ if (!gTextRunWordCache)
+ return;
+ gTextRunWordCache->RemoveTextRun(aTextRun);
+}
--- a/gfx/thebes/src/gfxWindowsFonts.cpp
+++ b/gfx/thebes/src/gfxWindowsFonts.cpp
@@ -640,19 +640,19 @@ SetupTextRunFromGlyphs(gfxTextRun *aRun,
PRUint32 i;
PRInt32 lastWidth = 0;
PRUint32 appUnitsPerDevPixel = aRun->GetAppUnitsPerDevUnit();
for (i = 0; i < length; ++i) {
PRInt32 advancePixels = partialWidthArray[i] - lastWidth;
lastWidth = partialWidthArray[i];
PRInt32 advanceAppUnits = advancePixels*appUnitsPerDevPixel;
WCHAR glyph = aGlyphs[i];
- if (gfxFontGroup::IsInvisibleChar(aRun->GetChar(i))) {
- aRun->SetCharacterGlyph(i, g.SetMissing());
- } else if (advanceAppUnits >= 0 &&
+ NS_ASSERTION(!gfxFontGroup::IsInvalidChar(aRun->GetChar(i)),
+ "Invalid character detected!");
+ if (advanceAppUnits >= 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
aRun->SetCharacterGlyph(i, g.SetSimpleGlyph(advanceAppUnits, glyph));
} else {
gfxTextRun::DetailedGlyph details;
details.mIsLastGlyph = PR_TRUE;
details.mGlyphID = glyph;
details.mAdvance = advanceAppUnits;
--- a/gfx/thebes/test/gfxFontSelectionTest.cpp
+++ b/gfx/thebes/test/gfxFontSelectionTest.cpp
@@ -294,26 +294,25 @@ RunTest (TestEntry *test, gfxContext *ct
gfxTextRunFactory::Parameters params = {
ctx, nsnull, nsnull, nsnull, 0, 60
};
PRUint32 flags = gfxTextRunFactory::TEXT_IS_PERSISTENT;
if (test->isRTL) {
flags |= gfxTextRunFactory::TEXT_IS_RTL;
}
PRUint32 length;
- PRBool isInCache;
if (test->stringType == S_ASCII) {
flags |= gfxTextRunFactory::TEXT_IS_ASCII | gfxTextRunFactory::TEXT_IS_8BIT;
length = strlen(test->string);
- textRun = gTextRunCache->MakeTextRun(NS_REINTERPRET_CAST(PRUint8*, test->string), length, fontGroup, ¶ms, flags, &isInCache);
+ textRun = gfxTextRunWordCache::MakeTextRun(NS_REINTERPRET_CAST(PRUint8*, test->string), length, fontGroup, ¶ms, flags);
} else {
flags |= gfxTextRunFactory::TEXT_HAS_SURROGATES; // just in case
NS_ConvertUTF8toUTF16 str(nsDependentCString(test->string));
length = str.Length();
- textRun = gTextRunCache->MakeTextRun(str.get(), length, fontGroup, ¶ms, flags, &isInCache);
+ textRun = gfxTextRunWordCache::MakeTextRun(str.get(), length, fontGroup, ¶ms, flags);
}
gfxFontTestStore::NewStore();
textRun->Draw(ctx, gfxPoint(0,0), 0, length, nsnull, nsnull, nsnull);
gfxFontTestStore *s = gfxFontTestStore::CurrentStore();
gTextRunCache->RemoveTextRun(textRun);
--- a/gfx/thebes/test/gfxWordCacheTest.cpp
+++ b/gfx/thebes/test/gfxWordCacheTest.cpp
@@ -77,44 +77,39 @@ public:
~FrameTextRunCache() {
AgeAllGenerations();
}
void RemoveFromCache(gfxTextRun* aTextRun) {
if (aTextRun->GetExpirationState()->IsTracked()) {
RemoveObject(aTextRun);
}
- mCache.RemoveTextRun(aTextRun);
+ gfxTextRunWordCache::RemoveTextRun(aTextRun);
}
// This gets called when the timeout has expired on a gfxTextRun
virtual void NotifyExpired(gfxTextRun* aTextRun) {
RemoveFromCache(aTextRun);
delete aTextRun;
}
-
- gfxTextRunWordCache mCache;
};
static gfxTextRun *
MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
gfxFontGroup *aFontGroup, const gfxFontGroup::Parameters* aParams,
PRUint32 aFlags)
{
nsAutoPtr<gfxTextRun> textRun;
if (aLength == 0) {
textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags);
} else if (aLength == 1 && aText[0] == ' ') {
textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags);
} else {
- PRBool isInCache;
- textRun = gTextRuns->mCache.MakeTextRun(aText, aLength, aFontGroup,
- aParams, aFlags, &isInCache);
- if (!isInCache && textRun) {
- }
+ textRun = gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup,
+ aParams, aFlags);
}
if (!textRun)
return nsnull;
nsresult rv = gTextRuns->AddObject(textRun);
if (NS_FAILED(rv)) {
gTextRuns->RemoveFromCache(textRun);
return nsnull;
}
@@ -176,23 +171,23 @@ main (int argc, char **argv) {
PRUint32 flags = gfxTextRunFactory::TEXT_IS_PERSISTENT;
// First load an Arabic word into the cache
const char cString[] = "\xd8\xaa\xd9\x85";
nsDependentCString cStr(cString);
NS_ConvertUTF8toUTF16 str(cStr);
gfxTextRun *tr = MakeTextRun(str.get(), str.Length(), fontGroup, ¶ms, flags);
+ tr->GetAdvanceWidth(0, str.Length(), nsnull);
// Now try to trigger an assertion with a word cache bug. The first
// word is in the cache so it gets added to the new textrun directly.
// The second word is not in the cache
const char cString2[] = "\xd8\xaa\xd9\x85\n\xd8\xaa\xd8\x85 ";
nsDependentCString cStr2(cString2);
NS_ConvertUTF8toUTF16 str2(cStr2);
gfxTextRun *tr2 = MakeTextRun(str2.get(), str2.Length(), fontGroup, ¶ms, flags);
-
tr2->GetAdvanceWidth(0, str2.Length(), nsnull);
}
fflush (stderr);
fflush (stdout);
}
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -133,26 +133,20 @@
// This bit is set on frames that are logically adjacent to the end of the
// line (i.e. no following on line with actual displayed in-flow content).
#define TEXT_END_OF_LINE 0x00400000
// This bit is set on frames that end with a hyphenated break.
#define TEXT_HYPHEN_BREAK 0x00800000
// This bit is set on frames that trimmed trailing whitespace characters when
// calculating their width during reflow.
#define TEXT_TRIMMED_TRAILING_WHITESPACE 0x01000000
-// Set when the frame's text may have a different style from its in-flow
-// brethren (e.g. the frame is in first-letter or first-line), so text runs
-// may need to be reconstructed when the frame's content offset/length changes.
-// We set this on the frame that has in first-letter or first-line, but not
-// in-flow siblings outside first-letter or first-line.
-#define TEXT_RUN_LAYOUT_DEPENDENT 0x02000000
#define TEXT_REFLOW_FLAGS \
(TEXT_FIRST_LETTER|TEXT_START_OF_LINE|TEXT_END_OF_LINE|TEXT_HYPHEN_BREAK| \
- TEXT_TRIMMED_TRAILING_WHITESPACE|TEXT_RUN_LAYOUT_DEPENDENT)
+ TEXT_TRIMMED_TRAILING_WHITESPACE)
// Cache bits for IsEmpty().
// Set this bit if the textframe is known to be only collapsible whitespace.
#define TEXT_IS_ONLY_WHITESPACE 0x08000000
// Set this bit if the textframe is known to be not only collapsible whitespace.
#define TEXT_ISNOT_ONLY_WHITESPACE 0x10000000
#define TEXT_WHITESPACE_FLAGS 0x18000000
@@ -499,16 +493,25 @@ public:
#ifdef DEBUG
void ToCString(nsString& aBuf, PRInt32* aTotalContentLength) const;
#endif
PRInt32 GetContentOffset() const { return mContentOffset; }
PRInt32 GetContentLength() const { return mContentLength; }
PRInt32 GetContentEnd() const { return mContentOffset + mContentLength; }
+ /**
+ * Fix up the content offsets for all next-in-flows so that they do not
+ * overlap this frame's content.
+ * @param aClearTextRuns if true, then any next-in-flows whose content
+ * offsets changed have their textruns cleared (as would be necessary
+ * if this frame could have a different style to those frames)
+ */
+ void AdjustNextInFlowContentOffsetsForGrowth(PRBool aClearTextRuns);
+
// Compute the length of the content mapped by this frame
// and all its in-flow siblings. Basically this means starting at mContentOffset
// and going to the end of the text node or the next bidi continuation
// boundary.
PRInt32 GetInFlowContentLength();
// Clears out mTextRun from this frame and all other frames that hold a reference
// to it, then deletes the textrun.
@@ -554,18 +557,16 @@ protected:
SelectionDetails* GetSelectionDetails();
void AdjustSelectionPointsForBidi(SelectionDetails *sdptr,
PRInt32 textLength,
PRBool isRTLChars,
PRBool isOddLevel,
PRBool isBidiSystem);
-
- void SetOffsets(PRInt32 start, PRInt32 end);
};
static void
DestroyUserData(void* aUserData)
{
TextRunUserData* userData = NS_STATIC_CAST(TextRunUserData*, aUserData);
if (userData) {
nsMemory::Free(userData);
@@ -627,48 +628,42 @@ public:
~FrameTextRunCache() {
AgeAllGenerations();
}
void RemoveFromCache(gfxTextRun* aTextRun) {
if (aTextRun->GetExpirationState()->IsTracked()) {
RemoveObject(aTextRun);
}
- if (!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_UNCACHED)) {
- mCache.RemoveTextRun(aTextRun);
+ if (aTextRun->GetFlags() & gfxTextRunWordCache::TEXT_IN_CACHE) {
+ gfxTextRunWordCache::RemoveTextRun(aTextRun);
}
}
// This gets called when the timeout has expired on a gfxTextRun
virtual void NotifyExpired(gfxTextRun* aTextRun) {
UnhookTextRunFromFrames(aTextRun);
RemoveFromCache(aTextRun);
delete aTextRun;
}
-
- gfxTextRunWordCache mCache;
};
static gfxTextRun *
MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
gfxFontGroup *aFontGroup, const gfxFontGroup::Parameters* aParams,
PRUint32 aFlags)
{
nsAutoPtr<gfxTextRun> textRun;
if (aLength == 0) {
- textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags | nsTextFrameUtils::TEXT_IS_UNCACHED);
+ textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags);
} else if (aLength == 1 && aText[0] == ' ') {
- textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags | nsTextFrameUtils::TEXT_IS_UNCACHED);
+ textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags);
} else {
- PRBool isInCache;
- textRun = gTextRuns->mCache.MakeTextRun(aText, aLength, aFontGroup,
- aParams, aFlags, &isInCache);
- if (!isInCache && textRun) {
- textRun->SetFlagBits(nsTextFrameUtils::TEXT_IS_UNCACHED);
- }
+ textRun = gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup,
+ aParams, aFlags);
}
if (!textRun)
return nsnull;
nsresult rv = gTextRuns->AddObject(textRun);
if (NS_FAILED(rv)) {
gTextRuns->RemoveFromCache(textRun);
return nsnull;
}
@@ -677,26 +672,22 @@ MakeTextRun(const PRUnichar *aText, PRUi
static gfxTextRun *
MakeTextRun(const PRUint8 *aText, PRUint32 aLength,
gfxFontGroup *aFontGroup, const gfxFontGroup::Parameters* aParams,
PRUint32 aFlags)
{
nsAutoPtr<gfxTextRun> textRun;
if (aLength == 0) {
- textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags | nsTextFrameUtils::TEXT_IS_UNCACHED);
+ textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags);
} else if (aLength == 1 && aText[0] == ' ') {
- textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags | nsTextFrameUtils::TEXT_IS_UNCACHED);
+ textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags);
} else {
- PRBool isInCache;
- textRun = gTextRuns->mCache.MakeTextRun(aText, aLength, aFontGroup,
- aParams, aFlags, &isInCache);
- if (!isInCache && textRun) {
- textRun->SetFlagBits(nsTextFrameUtils::TEXT_IS_UNCACHED);
- }
+ textRun = gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup,
+ aParams, aFlags);
}
if (!textRun)
return nsnull;
nsresult rv = gTextRuns->AddObject(textRun);
if (NS_FAILED(rv)) {
gTextRuns->RemoveFromCache(textRun);
return nsnull;
}
@@ -1345,23 +1336,19 @@ void BuildTextRunsScanner::ScanFrame(nsI
// Don't do this optimization if mLastFrame has a terminal newline...
// it's quite likely preformatted and we might want to end the textrun here.
// This is almost always true:
if (mLastFrame->GetStyleContext() == aFrame->GetStyleContext() &&
!HasTerminalNewline(mLastFrame)) {
nsTextFrame* frame = NS_STATIC_CAST(nsTextFrame*, aFrame);
mappedFlow->mEndFrame = NS_STATIC_CAST(nsTextFrame*, frame->GetNextInFlow());
- // Frames in the same flow can overlap at least temporarily
- // (e.g. when first-line builds its textrun, we need to have it suck
- // up all the in-flow content because we don't know how long the line
- // is going to be).
- mappedFlow->mContentEndOffset =
- PR_MAX(mappedFlow->mContentEndOffset,
- frame->GetContentOffset() + frame->GetContentLength());
+ NS_ASSERTION(mappedFlow->mContentEndOffset <= frame->GetContentOffset(),
+ "frame offsets overlap!");
+ mappedFlow->mContentEndOffset = frame->GetContentEnd();
AccumulateRunInfo(frame);
return;
}
}
}
// Now see if we can add a new set of frames to the current textrun
if (aFrame->GetType() == nsGkAtoms::textFrame) {
@@ -1374,18 +1361,18 @@ void BuildTextRunsScanner::ScanFrame(nsI
MappedFlow* mappedFlow = mMappedFlows.AppendElement();
if (!mappedFlow)
return;
mappedFlow->mStartFrame = frame;
mappedFlow->mEndFrame = NS_STATIC_CAST(nsTextFrame*, frame->GetNextInFlow());
mappedFlow->mAncestorControllingInitialBreak = mCommonAncestorWithLastFrame;
mappedFlow->mContentOffset = frame->GetContentOffset();
- mappedFlow->mContentEndOffset =
- frame->GetContentOffset() + frame->GetContentLength();
+ mappedFlow->mContentEndOffset = frame->GetContentEnd();
+ // This is temporary: it's overwritten in BuildTextRunForFrames
mappedFlow->mTransformedTextOffset = 0;
mLastFrame = frame;
AccumulateRunInfo(frame);
if (mMappedFlows.Length() == 1) {
mCurrentFramesAllSameTextRun = frame->GetTextRun();
mCurrentRunTrimLeadingWhitespace = mTrimNextRunLeadingWhitespace;
} else {
@@ -1471,25 +1458,25 @@ GetHyphenTextRun(gfxTextRun* aTextRun, n
}
gfxContext* ctx = NS_STATIC_CAST(gfxContext*,
aRefContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
PRUint32 flags = gfxFontGroup::TEXT_IS_PERSISTENT;
static const PRUnichar unicodeHyphen = 0x2010;
gfxTextRun* textRun =
- gfxGlobalTextRunCache::GetTextRun(&unicodeHyphen, 1, fontGroup, ctx,
- aTextRun->GetAppUnitsPerDevUnit(), flags);
+ gfxTextRunCache::MakeTextRun(&unicodeHyphen, 1, fontGroup, ctx,
+ aTextRun->GetAppUnitsPerDevUnit(), flags);
if (textRun && textRun->CountMissingGlyphs() == 0)
return textRun;
static const PRUint8 dash = '-';
- return gfxGlobalTextRunCache::GetTextRun(&dash, 1, fontGroup, ctx,
- aTextRun->GetAppUnitsPerDevUnit(),
- flags);
+ return gfxTextRunCache::MakeTextRun(&dash, 1, fontGroup, ctx,
+ aTextRun->GetAppUnitsPerDevUnit(),
+ flags);
}
static gfxFont::Metrics
GetFontMetrics(gfxFontGroup* aFontGroup)
{
if (!aFontGroup)
return gfxFont::Metrics();
gfxFont* font = aFontGroup->GetFontAt(0);
@@ -1569,22 +1556,18 @@ BuildTextRunsScanner::BuildTextRunForFra
// Figure out what content is included in this flow.
nsIContent* content = f->GetContent();
const nsTextFragment* frag = content->GetText();
PRInt32 contentStart = mappedFlow->mContentOffset;
PRInt32 contentEnd = mappedFlow->mContentEndOffset;
PRInt32 contentLength = contentEnd - contentStart;
if (content == lastContent) {
- NS_ASSERTION(endOfLastContent >= contentStart,
- "Gap in textframes mapping content?!");
- // Text frames can overlap (see comment in ScanFrame below)
- contentStart = PR_MAX(contentStart, endOfLastContent);
- if (contentStart >= contentEnd)
- continue;
+ NS_ASSERTION(endOfLastContent == contentStart,
+ "Gap in textframes mapping content, or overlap?!");
userData->mMappedFlows[finalMappedFlowCount - 1].mContentLength += contentLength;
} else {
TextRunMappedFlow* newFlow = &userData->mMappedFlows[finalMappedFlowCount];
newFlow->mStartFrame = mappedFlow->mStartFrame;
newFlow->mDOMOffsetToBeforeTransformOffset = builder.GetCharCount() - mappedFlow->mContentOffset;
newFlow->mContentLength = contentLength;
++finalMappedFlowCount;
@@ -1706,17 +1689,16 @@ BuildTextRunsScanner::BuildTextRunForFra
PRUint32 end = i == mMappedFlows.Length() - 1 ? transformedLength :
mMappedFlows[i + 1].mTransformedTextOffset;
nsStyleContext* sc = mappedFlow->mStartFrame->GetStyleContext();
PRUint32 j;
for (j = mappedFlow->mTransformedTextOffset; j < end; ++j) {
styles.AppendElement(sc);
}
}
- textFlags |= nsTextFrameUtils::TEXT_IS_UNCACHED;
}
if (textFlags & nsTextFrameUtils::TEXT_HAS_TAB) {
textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
}
if (textFlags & nsTextFrameUtils::TEXT_HAS_SHY) {
textFlags |= gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS;
}
@@ -3278,43 +3260,45 @@ NS_IMETHODIMP
nsContinuingTextFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow)
{
NS_ASSERTION(aPrevInFlow, "Must be a continuation!");
// NOTE: bypassing nsTextFrame::Init!!!
nsresult rv = nsFrame::Init(aContent, aParent, aPrevInFlow);
- nsIFrame* nextContinuation = aPrevInFlow->GetNextContinuation();
// Hook the frame into the flow
- SetPrevInFlow(aPrevInFlow);
- aPrevInFlow->SetNextInFlow(this);
nsTextFrame* prev = NS_STATIC_CAST(nsTextFrame*, aPrevInFlow);
+ nsTextFrame* nextContinuation = NS_STATIC_CAST(nsTextFrame*,
+ prev->GetNextContinuation());
+ SetPrevInFlow(prev);
+ prev->SetNextInFlow(this);
+
mTextRun = prev->GetTextRun();
- mContentOffset = prev->GetContentOffset() + prev->GetContentLength();
+ mContentOffset = prev->GetContentEnd();
+ mContentLength = mContent->TextLength() - mContentOffset;
#ifdef IBMBIDI
- if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) {
- PRInt32 start, end;
- aPrevInFlow->GetOffsets(start, mContentOffset);
-
+ if (prev->GetStateBits() & NS_FRAME_IS_BIDI) {
nsPropertyTable *propTable = PresContext()->PropertyTable();
propTable->SetProperty(this, nsGkAtoms::embeddingLevel,
propTable->GetProperty(aPrevInFlow, nsGkAtoms::embeddingLevel),
nsnull, nsnull);
propTable->SetProperty(this, nsGkAtoms::baseLevel,
propTable->GetProperty(aPrevInFlow, nsGkAtoms::baseLevel),
nsnull, nsnull);
propTable->SetProperty(this, nsGkAtoms::charType,
propTable->GetProperty(aPrevInFlow, nsGkAtoms::charType),
nsnull, nsnull);
+
if (nextContinuation) {
SetNextContinuation(nextContinuation);
nextContinuation->SetPrevContinuation(this);
- nextContinuation->GetOffsets(start, end);
- mContentLength = PR_MAX(1, start - mContentOffset);
+ NS_ASSERTION(mContentOffset <= nextContinuation->GetContentOffset(),
+ "Overlapping text ranges!");
+ mContentLength = nextContinuation->GetContentOffset() - mContentOffset;
}
mState |= NS_FRAME_IS_BIDI;
} // prev frame is bidi
#endif // IBMBIDI
return rv;
}
@@ -3511,17 +3495,17 @@ nsTextFrame::ClearTextRun()
// // so we'd better kill this textrun now.
// if (textRun->GetExpirationState()->IsTracked()) {
// gTextRuns->RemoveFromCache(textRun);
// }
// delete textRun;
// return;
// }
- if (textRun->GetFlags() & nsTextFrameUtils::TEXT_IS_UNCACHED) {
+ if (!(textRun->GetFlags() & gfxTextRunWordCache::TEXT_IN_CACHE)) {
// Remove it now because it's not doing anything useful
gTextRuns->RemoveFromCache(textRun);
delete textRun;
}
}
static void
ClearTextRunsInFlowChain(nsTextFrame* aFrame)
@@ -3669,18 +3653,18 @@ nsTextFrame::GetSelectionDetails()
return nsnull;
SelectionDetails* details =
GetFrameSelection()->LookUpSelection(owner->GetContent(),
isBefore ? 0 : owner->GetContent()->GetChildCount(), 0, PR_FALSE);
SelectionDetails* sd;
for (sd = details; sd; sd = sd->mNext) {
// The entire text is selected!
- sd->mStart = mContentOffset;
- sd->mEnd = mContentOffset + mContentLength;
+ sd->mStart = GetContentOffset();
+ sd->mEnd = GetContentEnd();
}
return details;
}
static void
FillClippedRect(gfxContext* aCtx, nsPresContext* aPresContext,
nscolor aColor, const gfxRect& aDirtyRect, const gfxRect& aRect)
{
@@ -4276,18 +4260,18 @@ nsTextFrame::IsVisibleInSelection(nsISel
return PR_FALSE;
SelectionDetails* details = GetSelectionDetails();
PRBool found = PR_FALSE;
// where are the selection points "really"
SelectionDetails *sdptr = details;
while (sdptr) {
- if (sdptr->mEnd > mContentOffset &&
- sdptr->mStart < mContentOffset + mContentLength &&
+ if (sdptr->mEnd > GetContentOffset() &&
+ sdptr->mStart < GetContentEnd() &&
sdptr->mType == nsISelectionController::SELECTION_NORMAL) {
found = PR_TRUE;
break;
}
sdptr = sdptr->mNext;
}
DestroySelectionDetails(details);
@@ -4404,17 +4388,17 @@ nsTextFrame::SetSelected(nsPresContext*
aRange->GetEndContainer(getter_AddRefs(endNode));
aRange->GetEndOffset(&endOffset);
aRange->GetStartContainer(getter_AddRefs(startNode));
aRange->GetStartOffset(&startOffset);
nsCOMPtr<nsIDOMNode> thisNode = do_QueryInterface(GetContent());
if (thisNode == startNode)
{
- if ((mContentOffset + mContentLength) >= startOffset)
+ if (GetContentEnd() >= startOffset)
{
found = PR_TRUE;
if (thisNode == endNode)
{ //special case
if (endOffset == startOffset) //no need to redraw since drawing takes place with cursor
found = PR_FALSE;
if (mContentOffset > endOffset)
@@ -4496,22 +4480,22 @@ nsTextFrame::GetPointFromOffset(nsPresCo
if (!mTextRun)
return NS_ERROR_FAILURE;
PropertyProvider properties(this, iter);
// Don't trim trailing whitespace, we want the caret to appear in the right
// place if it's positioned there
properties.InitializeForDisplay(PR_FALSE);
- if (inOffset < mContentOffset){
+ if (inOffset < GetContentOffset()){
NS_WARNING("offset before this frame's content");
- inOffset = mContentOffset;
- } else if (inOffset > mContentOffset + mContentLength) {
+ inOffset = GetContentOffset();
+ } else if (inOffset > GetContentEnd()) {
NS_WARNING("offset after this frame's content");
- inOffset = mContentOffset + mContentLength;
+ inOffset = GetContentEnd();
}
PRInt32 trimmedOffset = properties.GetStart().GetOriginalOffset();
PRInt32 trimmedEnd = trimmedOffset + properties.GetOriginalLength();
inOffset = PR_MAX(inOffset, trimmedOffset);
inOffset = PR_MIN(inOffset, trimmedEnd);
iter.SetOriginalOffset(inOffset);
@@ -4854,27 +4838,34 @@ nsTextFrame::CheckVisibility(nsPresConte
*aRetval = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsTextFrame::GetOffsets(PRInt32 &start, PRInt32 &end) const
{
- start = mContentOffset;
- end = mContentOffset+mContentLength;
+ start = GetContentOffset();
+ end = GetContentEnd();
return NS_OK;
}
-// returns PR_TRUE if this text frame completes the first-letter, PR_FALSE
-// if it does not contain a true "letter"
-// if returns PR_TRUE, then it also updates aLength to cover just the first-letter
-// text
-// XXX :first-letter should be handled during frame construction
-// (and it has a good bit in common with nextBidi)
+/**
+ * Returns PR_TRUE if this text frame completes the first-letter, PR_FALSE
+ * if it does not contain a true "letter".
+ * If returns PR_TRUE, then it also updates aLength to cover just the first-letter
+ * text.
+ *
+ * XXX :first-letter should be handled during frame construction
+ * (and it has a good bit in common with nextBidi)
+ *
+ * @param aLength an in/out parameter: on entry contains the maximum length to
+ * return, on exit returns length of the first-letter fragment (which may
+ * include leading punctuation, for example)
+ */
static PRBool
FindFirstLetterRange(const nsTextFragment* aFrag,
gfxTextRun* aTextRun,
PRInt32 aOffset, PRInt32* aLength)
{
// Find first non-whitespace, non-punctuation cluster, and stop after it
PRInt32 i;
PRInt32 length = *aLength;
@@ -5165,16 +5156,36 @@ HasSoftHyphenBefore(const nsTextFragment
if (!iter.IsOriginalCharSkipped())
break;
if (aFrag->CharAt(iter.GetOriginalOffset()) == CH_SHY)
return PR_TRUE;
}
return PR_FALSE;
}
+void
+nsTextFrame::AdjustNextInFlowContentOffsetsForGrowth(PRBool aClearTextRuns)
+{
+ PRInt32 end = GetContentEnd();
+ nsTextFrame* f = this;
+ NS_ASSERTION(!GetNextInFlow() ||
+ NS_STATIC_CAST(nsTextFrame*, GetNextInFlow())->GetContentOffset() <= GetContentEnd(),
+ "We shrunk, this should not be called");
+ while (PR_TRUE) {
+ f = NS_STATIC_CAST(nsTextFrame*, f->GetNextInFlow());
+ if (!f || f->GetContentOffset() >= end)
+ break;
+ f->mContentLength = PR_MAX(end, f->GetContentEnd()) - end;
+ f->mContentOffset = end;
+ if (aClearTextRuns) {
+ f->ClearTextRun();
+ }
+ }
+}
+
NS_IMETHODIMP
nsTextFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aMetrics,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
DO_GLOBAL_REFLOW_COUNT("nsTextFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
@@ -5188,35 +5199,26 @@ nsTextFrame::Reflow(nsPresContext*
// Set up flags and clear out state
/////////////////////////////////////////////////////////////////////
// Clear out the reflow state flags in mState (without destroying
// the TEXT_BLINK_ON bit). We also clear the whitespace flags because this
// can change whether the frame maps whitespace-only text or not.
RemoveStateBits(TEXT_REFLOW_FLAGS | TEXT_WHITESPACE_FLAGS);
- nsTextFrame* prevInFlow = NS_STATIC_CAST(nsTextFrame*, GetPrevInFlow());
- if (prevInFlow) {
- // Our mContentOffset may be out of date due to our prev-in-flow growing or
- // shrinking. Update it.
- mContentOffset = prevInFlow->GetContentOffset() + prevInFlow->GetContentLength();
- }
-
- // Temporarily map all possible content while we construct our new textrun.
- // so that when doing reflow our styles prevail over any part of the
- // textrun we look at. Note that next-in-flows may be mapping the same
- // content; gfxTextRun construction logic will ensure that we take priority.
+ NS_ASSERTION(!GetPrevInFlow() ||
+ NS_STATIC_CAST(nsTextFrame*, GetPrevInFlow())->GetContentEnd() == mContentOffset,
+ "Discontinuous content offsets!");
PRInt32 maxContentLength = GetInFlowContentLength();
- mContentLength = maxContentLength;
// XXX If there's no line layout, we shouldn't even have created this
// frame. This may happen if, for example, this is text inside a table
// but not inside a cell. For now, just don't reflow. We also don't need to
// reflow if there is no content.
- if (!aReflowState.mLineLayout || !mContentLength) {
+ if (!aReflowState.mLineLayout || !maxContentLength) {
ClearMetrics(aMetrics);
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
nsLineLayout& lineLayout = *aReflowState.mLineLayout;
if (aPresContext->BidiEnabled()) {
@@ -5245,21 +5247,25 @@ nsTextFrame::Reflow(nsPresContext*
AddStateBits(TEXT_START_OF_LINE);
}
// Layout dependent styles are a problem because we need to reconstruct
// the gfxTextRun based on our layout.
PRBool layoutDependentTextRun =
lineLayout.GetFirstLetterStyleOK() || lineLayout.GetInFirstLine();
if (layoutDependentTextRun) {
- AddStateBits(TEXT_RUN_LAYOUT_DEPENDENT);
- }
- if (layoutDependentTextRun ||
- (prevInFlow && (prevInFlow->GetStateBits() & TEXT_RUN_LAYOUT_DEPENDENT))) {
ClearTextRun();
+ // Temporarily map all possible content while we construct our new textrun.
+ // so that when doing reflow our styles prevail over any part of the
+ // textrun we look at.
+ mContentLength = maxContentLength;
+ // The following loop is going to traverse all in-flow frames, which could
+ // be kinda slow, but we're going to have to rebuild all their text runs
+ // anyway so this shouldn't be any worse
+ AdjustNextInFlowContentOffsetsForGrowth(PR_TRUE);
}
PRUint32 flowEndInTextRun;
nsIFrame* lineContainer = lineLayout.GetLineContainerFrame();
gfxSkipCharsIterator iter =
EnsureTextRun(aReflowState.rendContext, lineContainer,
lineLayout.GetLine(), &flowEndInTextRun);
@@ -5268,17 +5274,17 @@ nsTextFrame::Reflow(nsPresContext*
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
const nsTextFragment* frag = mContent->GetText();
// DOM offsets of the text range we need to measure, after trimming
// whitespace, restricting to first-letter, and restricting preformatted text
// to nearest newline
- PRInt32 length = mContentLength;
+ PRInt32 length = maxContentLength;
PRInt32 offset = mContentOffset;
// Restrict preformatted text to the nearest newline
PRInt32 newLineOffset = -1;
if (textStyle->WhiteSpaceIsSignificant()) {
newLineOffset = FindChar(frag, offset, length, '\n');
if (newLineOffset >= 0) {
length = newLineOffset + 1 - offset;
@@ -5423,16 +5429,37 @@ nsTextFrame::Reflow(nsPresContext*
} else {
// We're definitely going to break and our whitespace will definitely
// be trimmed.
// Record that whitespace has already been trimmed.
AddStateBits(TEXT_TRIMMED_TRAILING_WHITESPACE);
}
mContentLength = offset + charsFit - mContentOffset;
+ // Now fix up content offsets/lengths for in-flows
+ nsTextFrame* f = NS_STATIC_CAST(nsTextFrame*, GetNextInFlow());
+ if (f) {
+ if (f->GetContentOffset() > GetContentEnd()) {
+ // We must have shrunk. Add the leftover text to the start of f.
+ f->mContentLength = f->GetContentEnd() - GetContentEnd();
+ f->mContentOffset = GetContentEnd();
+ if (layoutDependentTextRun) {
+ // f's textrun may need to change since the text style may be
+ // different.
+ f->ClearTextRun();
+ }
+ } else if (f->GetContentOffset() < GetContentEnd()) {
+ // We must have grown. Remove the text from f and possibly its
+ // continuations.
+ NS_ASSERTION(!layoutDependentTextRun,
+ "We should have grown up above and be shrinking here!");
+ AdjustNextInFlowContentOffsetsForGrowth(layoutDependentTextRun);
+ }
+ }
+
/////////////////////////////////////////////////////////////////////
// Compute output metrics
/////////////////////////////////////////////////////////////////////
// first-letter frames should use the tight bounding box metrics for ascent/descent
// for good drop-cap effects
if (GetStateBits() & TEXT_FIRST_LETTER) {
textMetrics.mAscent = PR_MAX(0, -textMetrics.mBoundingBox.Y());
@@ -5590,19 +5617,19 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPr
TrimmedOffsets trimmed = GetTrimmedOffsets(frag, PR_TRUE);
gfxSkipCharsIterator iter = start;
PRUint32 trimmedEnd = iter.ConvertOriginalToSkipped(trimmed.mStart + trimmed.mLength);
const nsStyleText* textStyle = GetStyleText();
gfxFloat delta = 0;
if (GetStateBits() & TEXT_TRIMMED_TRAILING_WHITESPACE) {
aLastCharIsJustifiable = PR_TRUE;
- } else if (trimmed.mStart + trimmed.mLength < mContentOffset + mContentLength) {
+ } else if (trimmed.mStart + trimmed.mLength < GetContentEnd()) {
gfxSkipCharsIterator end = iter;
- PRUint32 endOffset = end.ConvertOriginalToSkipped(mContentOffset + mContentLength);
+ PRUint32 endOffset = end.ConvertOriginalToSkipped(GetContentEnd());
if (trimmedEnd < endOffset) {
// We can't be dealing with tabs here ... they wouldn't be trimmed. So it's
// OK to pass null for the line container.
PropertyProvider provider(mTextRun, textStyle, frag, this, start, mContentLength,
nsnull, 0);
delta = mTextRun->GetAdvanceWidth(trimmedEnd, endOffset - trimmedEnd, &provider);
// non-compressed whitespace being skipped at end of line -> justifiable
// XXX should we actually *count* justifiable characters that should be
@@ -5758,17 +5785,17 @@ nsTextFrame::List(FILE* out, PRInt32 aIn
fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, GetView()));
}
PRInt32 totalContentLength;
nsAutoString tmp;
ToCString(tmp, &totalContentLength);
// Output the first/last content offset and prev/next in flow info
- PRBool isComplete = (mContentOffset + mContentLength) == totalContentLength;
+ PRBool isComplete = GetContentEnd() == totalContentLength;
fprintf(out, "[%d,%d,%c] ",
mContentOffset, mContentLength,
isComplete ? 'T':'F');
if (nsnull != mNextSibling) {
fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling));
}
nsIFrame* prevContinuation = GetPrevContinuation();
@@ -5867,30 +5894,34 @@ void nsTextFrame::AdjustSelectionPointsF
return;
}
void
nsTextFrame::AdjustOffsetsForBidi(PRInt32 aStart, PRInt32 aEnd)
{
AddStateBits(NS_FRAME_IS_BIDI);
- SetOffsets(aStart, aEnd);
- /*
- * After Bidi resolution we may need to reassign text runs.
- * This is called during bidi resolution from the block container, so we
- * shouldn't be holding a local reference to a textrun anywhere.
- */
- ClearTextRunsInFlowChain(this);
-}
-
-void
-nsTextFrame::SetOffsets(PRInt32 aStart, PRInt32 aEnd)
-{
mContentOffset = aStart;
mContentLength = aEnd - aStart;
+ nsTextFrame* f = this;
+ // We were just assigned all the content for this in-flow run, so unmap all
+ // next-in-flows
+ while (PR_TRUE) {
+ /*
+ * After Bidi resolution we may need to reassign text runs.
+ * This is called during bidi resolution from the block container, so we
+ * shouldn't be holding a local reference to a textrun anywhere.
+ */
+ f->ClearTextRun();
+ f = NS_STATIC_CAST(nsTextFrame*, f->GetNextInFlow());
+ if (!f)
+ break;
+ f->mContentOffset = aEnd;
+ f->mContentLength = 0;
+ }
}
/**
* @return PR_TRUE if this text frame ends with a newline character. It should return
* PR_FALSE if it is not a text frame.
*/
PRBool
nsTextFrame::HasTerminalNewline() const
--- a/layout/generic/nsTextFrameUtils.h
+++ b/layout/generic/nsTextFrameUtils.h
@@ -67,18 +67,17 @@ public:
TEXT_WAS_TRANSFORMED = 0x040000,
// The following flags are set by nsTextFrame
TEXT_IS_SIMPLE_FLOW = 0x100000,
TEXT_INCOMING_WHITESPACE = 0x200000,
TEXT_TRAILING_WHITESPACE = 0x400000,
TEXT_COMPRESSED_LEADING_WHITESPACE = 0x800000,
- TEXT_IS_UNCACHED = 0x1000000,
- TEXT_NO_BREAKS = 0x2000000
+ TEXT_NO_BREAKS = 0x1000000
};
static PRBool
IsPunctuationMark(PRUnichar aChar);
/**
* Returns PR_TRUE if aChars/aLength are something that make a space
* character not be whitespace when they follow the space character.
--- a/layout/generic/nsTextRunTransformations.cpp
+++ b/layout/generic/nsTextRunTransformations.cpp
@@ -184,51 +184,16 @@ static PRUint32
CountGlyphs(const gfxTextRun::DetailedGlyph* aDetails) {
PRUint32 glyphCount;
for (glyphCount = 0; !aDetails[glyphCount].mIsLastGlyph; ++glyphCount) {
}
return glyphCount + 1;
}
/**
- * Concatenate textruns together just by copying their glyphrun data
- */
-static void
-AppendTextRun(gfxTextRun* aDest, gfxTextRun* aSrc, PRUint32 aOffset)
-{
- PRUint32 numGlyphRuns;
- const gfxTextRun::GlyphRun* glyphRuns = aSrc->GetGlyphRuns(&numGlyphRuns);
- PRUint32 j;
- PRUint32 offset = aOffset;
- for (j = 0; j < numGlyphRuns; ++j) {
- PRUint32 runOffset = glyphRuns[j].mCharacterOffset;
- PRUint32 len =
- (j + 1 < numGlyphRuns ? glyphRuns[j + 1].mCharacterOffset : aSrc->GetLength()) -
- runOffset;
- nsresult rv = aDest->AddGlyphRun(glyphRuns[j].mFont, offset);
- if (NS_FAILED(rv))
- return;
-
- PRUint32 k;
- for (k = 0; k < len; ++k) {
- gfxTextRun::CompressedGlyph g = aSrc->GetCharacterGlyphs()[runOffset + k];
- if (g.IsComplexCluster()) {
- const gfxTextRun::DetailedGlyph* details = aSrc->GetDetailedGlyphs(runOffset + k);
- aDest->SetDetailedGlyphs(offset, details, CountGlyphs(details));
- } else {
- aDest->SetCharacterGlyph(offset, g);
- }
- ++offset;
- }
- }
- NS_ASSERTION(offset - aOffset == aSrc->GetLength(),
- "Something went wrong in our length calculations...");
-}
-
-/**
* Copy a given textrun, but merge certain characters into a single logical
* character. Glyphs for a character are added to the glyph list for the previous
* character and then the merged character is eliminated. Visually the results
* are identical.
*
* This is used for text-transform:uppercase when we encounter a SZLIG,
* whose uppercase form is "SS".
*
@@ -312,17 +277,17 @@ MergeCharactersInTextRun(gfxTextRun* aDe
}
static gfxTextRunFactory::Parameters
GetParametersForInner(nsTransformedTextRun* aTextRun, PRUint32* aFlags,
gfxContext* aRefContext)
{
gfxTextRunFactory::Parameters params =
{ aRefContext, nsnull, nsnull,
- nsnull, nsnull, PRUint32(aTextRun->GetAppUnitsPerDevUnit())
+ nsnull, nsnull, aTextRun->GetAppUnitsPerDevUnit()
};
*aFlags = aTextRun->GetFlags() & ~gfxFontGroup::TEXT_IS_PERSISTENT;
return params;
}
void
nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
gfxContext* aRefContext)
@@ -336,27 +301,24 @@ nsFontVariantTextRunFactory::RebuildText
fontStyle.size *= 0.8;
nsRefPtr<gfxFontGroup> smallFont = fontGroup->Copy(&fontStyle);
if (!smallFont)
return;
PRUint32 flags;
gfxTextRunFactory::Parameters innerParams =
GetParametersForInner(aTextRun, &flags, aRefContext);
- // The text outlives the child and inner textruns
- flags |= gfxFontGroup::TEXT_IS_PERSISTENT;
PRUint32 length = aTextRun->GetLength();
const PRUnichar* str = aTextRun->GetTextUnicode();
nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements();
// Create a textrun so we can check cluster-start properties
- nsAutoPtr<gfxTextRun> inner;
- // This text is going to outlive the inner text run
- inner = fontGroup->MakeTextRun(str, length, &innerParams, flags);
- if (!inner)
+ gfxTextRunCache::AutoTextRun inner(
+ gfxTextRunCache::MakeTextRun(str, length, fontGroup, &innerParams, flags));
+ if (!inner.get())
return;
nsCaseTransformTextRunFactory uppercaseFactory(nsnull, PR_TRUE);
aTextRun->ResetGlyphRuns();
PRUint32 runStart = 0;
PRBool runIsLowercase = PR_FALSE;
@@ -387,36 +349,41 @@ nsFontVariantTextRunFactory::RebuildText
isLowercase = ch != ch2 || ch == SZLIG;
} else {
// Don't transform the character! I.e., pretend that it's not lowercase
}
}
}
if ((i == length || runIsLowercase != isLowercase) && runStart < i) {
- nsAutoPtr<gfxTextRun> child;
+ nsAutoPtr<gfxTextRun> transformedChild;
+ gfxTextRunCache::AutoTextRun cachedChild;
+ gfxTextRun* child;
// Setup actual line break data for child (which may affect shaping)
innerParams.mInitialBreaks = lineBreakBeforeArray.Elements();
innerParams.mInitialBreakCount = lineBreakBeforeArray.Length();
if (runIsLowercase) {
- child = uppercaseFactory.MakeTextRun(str + runStart, i - runStart,
+ transformedChild = uppercaseFactory.MakeTextRun(str + runStart, i - runStart,
&innerParams, smallFont, flags, styleArray.Elements(), PR_FALSE);
+ child = transformedChild;
} else {
- child = fontGroup->
- MakeTextRun(str + runStart, i - runStart, &innerParams, flags);
+ cachedChild =
+ gfxTextRunCache::MakeTextRun(str + runStart, i - runStart, fontGroup,
+ &innerParams, flags);
+ child = cachedChild.get();
}
if (!child)
return;
// Copy potential linebreaks into child so they're preserved
// (and also child will be shaped appropriately)
NS_ASSERTION(canBreakBeforeArray.Length() == i - runStart,
"lost some break-before values?");
child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
canBreakBeforeArray.Elements(), aRefContext);
- AppendTextRun(aTextRun, child, runStart);
+ aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), runStart, PR_FALSE);
runStart = i;
styleArray.Clear();
canBreakBeforeArray.Clear();
lineBreakBeforeArray.Clear();
if (nextLineBreak > 0 && aTextRun->mLineBreaks[nextLineBreak - 1] == i) {
lineBreakBeforeArray.AppendElement(0);
}
@@ -513,30 +480,32 @@ nsCaseTransformTextRunFactory::RebuildTe
NS_ASSERTION(nextLineBreak == aTextRun->mLineBreaks.Length(),
"lost track of line breaks somehow");
PRUint32 flags;
gfxTextRunFactory::Parameters innerParams =
GetParametersForInner(aTextRun, &flags, aRefContext);
gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
- nsAutoPtr<gfxTextRun> child;
+ nsAutoPtr<gfxTextRun> transformedChild;
+ gfxTextRunCache::AutoTextRun cachedChild;
+ gfxTextRun* child;
// Setup actual line break data for child (which may affect shaping)
innerParams.mInitialBreaks = lineBreakBeforeArray.Elements();
innerParams.mInitialBreakCount = lineBreakBeforeArray.Length();
- // The text outlives 'child'
- flags |= gfxFontGroup::TEXT_IS_PERSISTENT;
if (mInnerTransformingTextRunFactory) {
- child = mInnerTransformingTextRunFactory->MakeTextRun(
+ transformedChild = mInnerTransformingTextRunFactory->MakeTextRun(
convertedString.BeginReading(), convertedString.Length(),
&innerParams, fontGroup, flags, styleArray.Elements(), PR_FALSE);
+ child = transformedChild.get();
} else {
- child = fontGroup->MakeTextRun(
- convertedString.BeginReading(), convertedString.Length(), &innerParams,
- flags);
+ cachedChild = gfxTextRunCache::MakeTextRun(
+ convertedString.BeginReading(), convertedString.Length(), fontGroup,
+ &innerParams, flags);
+ child = cachedChild.get();
}
if (!child)
return;
// Copy potential linebreaks into child so they're preserved
// (and also child will be shaped appropriately)
NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(),
"Dropped characters or break-before values somewhere!");
child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -49,16 +49,17 @@
#include "nsSVGTextPathFrame.h"
#include "nsSVGPathElement.h"
#include "nsSVGPoint.h"
#include "nsSVGRect.h"
#include "nsDOMError.h"
#include "gfxContext.h"
#include "gfxMatrix.h"
#include "gfxPlatform.h"
+#include "gfxTextRunCache.h"
// XXX: This initial straightforward conversion from accessing cairo
// directly to Thebes doesn't handle clusters. Pretty much all code
// that measures or draws single characters (textPath code and some
// DOM accessors) will need to be reworked.
//----------------------------------------------------------------------
// Implementation
@@ -210,19 +211,19 @@ nsSVGGlyphFrame::GetType() const
//----------------------------------------------------------------------
// nsISVGChildFrame methods
void
nsSVGGlyphFrame::LoopCharacters(gfxContext *aCtx, const nsString &aText,
const nsSVGCharacterPosition *aCP,
FillOrStroke aFillOrStroke)
{
- nsAutoPtr<gfxTextRun> textRun(GetTextRun(aCtx, aText));
+ gfxTextRunCache::AutoTextRun textRun = GetTextRun(aCtx, aText);
- if (!textRun)
+ if (!textRun.get())
return;
if (!aCP) {
if (aFillOrStroke == STROKE) {
textRun->DrawToPath(aCtx, mPosition, 0, aText.Length(), nsnull, nsnull);
} else {
textRun->Draw(aCtx, mPosition, 0, aText.Length(),
nsnull, nsnull, nsnull);
@@ -601,18 +602,18 @@ nsSVGGlyphFrame::GetCharacterPosition(gf
/* textPath frame, but invalid target */
if (!data)
return NS_ERROR_FAILURE;
gfxFloat length = data->GetLength();
PRUint32 strLength = aText.Length();
- nsAutoPtr<gfxTextRun> textRun(GetTextRun(aContext, aText));
- if (!textRun)
+ gfxTextRunCache::AutoTextRun textRun = GetTextRun(aContext, aText);
+ if (!textRun.get())
return NS_ERROR_OUT_OF_MEMORY;
nsSVGCharacterPosition *cp = new nsSVGCharacterPosition[strLength];
for (PRUint32 k = 0; k < strLength; k++)
cp[k].draw = PR_FALSE;
gfxFloat x = mPosition.x;
@@ -1342,27 +1343,21 @@ nsSVGGlyphFrame::GetTextRun(gfxContext *
{
// XXX: should really pass in GetPresContext()->AppUnitsPerDevPixel()
// instead of "1" and do the appropriate unit conversions when sending
// coordinates into thebes and pulling metrics out.
//
// References:
// https://bugzilla.mozilla.org/show_bug.cgi?id=375141
// http://weblogs.mozillazine.org/roc/archives/2007/03/text_text_text.html
-
- gfxTextRunFactory::Parameters params =
- { aCtx, nsnull, nsnull,
- nsnull, nsnull,
- 1 // see note above
- };
-
if (!mFontGroup)
return nsnull;
- return mFontGroup->MakeTextRun(aText.get(), aText.Length(), ¶ms, 0);
+ return gfxTextRunCache::MakeTextRun(aText.get(), aText.Length(),
+ mFontGroup, aCtx, 1, 0);
}
//----------------------------------------------------------------------
// helper class
nsSVGGlyphFrame::nsSVGAutoGlyphHelperContext::nsSVGAutoGlyphHelperContext(
nsSVGGlyphFrame *aSource,
const nsString &aText,
--- a/layout/svg/base/src/nsSVGGlyphFrame.h
+++ b/layout/svg/base/src/nsSVGGlyphFrame.h
@@ -39,16 +39,17 @@
#ifndef __NS_SVGGLYPHFRAME_H__
#define __NS_SVGGLYPHFRAME_H__
#include "nsSVGGeometryFrame.h"
#include "nsISVGGlyphFragmentLeaf.h"
#include "nsISVGChildFrame.h"
#include "gfxContext.h"
#include "gfxFont.h"
+#include "gfxTextRunCache.h"
struct nsSVGCharacterPosition;
class nsSVGTextFrame;
class nsSVGGlyphFrame;
typedef nsSVGGeometryFrame nsSVGGlyphFrameBase;
class nsSVGGlyphFrame : public nsSVGGlyphFrameBase,
@@ -159,40 +160,41 @@ protected:
};
// VC6 does not allow the inner class to access protected members
// of the outer class
class nsSVGAutoGlyphHelperContext;
friend class nsSVGAutoGlyphHelperContext;
// A helper class to deal with gfxTextRuns and temporary thebes
- // contexts. It destroys them when it goes out of scope.
+ // contexts.
class nsSVGAutoGlyphHelperContext
{
public:
nsSVGAutoGlyphHelperContext(nsSVGGlyphFrame *aSource,
const nsString &aText)
{
Init(aSource, aText);
}
nsSVGAutoGlyphHelperContext(nsSVGGlyphFrame *aSource,
const nsString &aText,
nsSVGCharacterPosition **cp);
gfxContext *GetContext() { return mCT; }
- gfxTextRun *GetTextRun() { return mTextRun; }
+ gfxTextRun *GetTextRun() { return mTextRun.get(); }
private:
void Init(nsSVGGlyphFrame *aSource, const nsString &aText);
- nsRefPtr<gfxContext> mCT;
- nsAutoPtr<gfxTextRun> mTextRun;
+ nsRefPtr<gfxContext> mCT;
+ gfxTextRunCache::AutoTextRun mTextRun;
};
+ // The textrun must be released via gfxTextRunCache::AutoTextRun
gfxTextRun *GetTextRun(gfxContext *aCtx,
const nsString &aText);
PRBool GetCharacterData(nsAString & aCharacterData);
nsresult GetCharacterPosition(gfxContext *aContext,
const nsString &aText,
nsSVGCharacterPosition **aCharacterPosition);