diff options
Diffstat (limited to 'content/common/child_process_host.cc')
-rw-r--r-- | content/common/child_process_host.cc | 137 |
1 files changed, 113 insertions, 24 deletions
diff --git a/content/common/child_process_host.cc b/content/common/child_process_host.cc index b8a3b95..65a9b7a 100644 --- a/content/common/child_process_host.cc +++ b/content/common/child_process_host.cc @@ -66,6 +66,14 @@ FilePath TransformPathForFeature(const FilePath& path, } // namespace #endif // OS_MACOSX +#if defined (OS_WIN) +// Types used in PreCacheFont +namespace { +typedef std::vector<string16> FontNameVector; +typedef std::map<int, FontNameVector> PidToFontNames; +} +#endif // OS_WIN + ChildProcessHost::ChildProcessHost() : ALLOW_THIS_IN_INITIALIZER_LIST(listener_(this)), opening_channel_(false) { @@ -131,27 +139,39 @@ FilePath ChildProcessHost::GetChildPath(int flags) { } #if defined(OS_WIN) +ChildProcessHost::FontCache::CacheElement::CacheElement() + : font_(NULL), dc_(NULL), ref_count_(0) { +} + +ChildProcessHost::FontCache::CacheElement::~CacheElement() { + if (font_) { + DeleteObject(font_); + } + if (dc_) { + DeleteDC(dc_); + } +} + +ChildProcessHost::FontCache::FontCache() { +} + +ChildProcessHost::FontCache::~FontCache() { +} + // static -void ChildProcessHost::PreCacheFont(LOGFONT font) { - // If a child process is running in a sandbox, GetTextMetrics() - // can sometimes fail. If a font has not been loaded - // previously, GetTextMetrics() will try to load the font - // from the font file. However, the sandboxed process does - // not have permissions to access any font files and - // the call fails. So we make the browser pre-load the - // font for us by using a dummy call to GetTextMetrics of - // the same font. +ChildProcessHost::FontCache* ChildProcessHost::FontCache::GetInstance() { + return Singleton<ChildProcessHost::FontCache>::get(); +} - // Maintain a circular queue for the fonts and DCs to be cached. - // font_index maintains next available location in the queue. - static const int kFontCacheSize = 32; - static HFONT fonts[kFontCacheSize] = {0}; - static HDC hdcs[kFontCacheSize] = {0}; - static size_t font_index = 0; +void ChildProcessHost::FontCache::PreCacheFont(LOGFONT font, int process_id) { + typedef std::map<string16, ChildProcessHost::FontCache::CacheElement> + FontNameToElement; - UMA_HISTOGRAM_COUNTS_100("Memory.CachedFontAndDC", - fonts[kFontCacheSize-1] ? kFontCacheSize : static_cast<int>(font_index)); + base::AutoLock lock(mutex_); + // Fetch the font into memory. + // No matter the font is cached or not, we load it to avoid GDI swapping out + // that font file. HDC hdc = GetDC(NULL); HFONT font_handle = CreateFontIndirect(&font); DCHECK(NULL != font_handle); @@ -163,15 +183,84 @@ void ChildProcessHost::PreCacheFont(LOGFONT font) { BOOL ret = GetTextMetrics(hdc, &tm); DCHECK(ret); - if (fonts[font_index] || hdcs[font_index]) { - // We already have too many fonts, we will delete one and take it's place. - DeleteObject(fonts[font_index]); - ReleaseDC(NULL, hdcs[font_index]); + string16 font_name = font.lfFaceName; + int ref_count_inc = 1; + FontNameVector::iterator it = + std::find(process_id_font_map_[process_id].begin(), + process_id_font_map_[process_id].end(), + font_name); + if (it == process_id_font_map_[process_id].end()) { + // Requested font is new to cache. + process_id_font_map_[process_id].push_back(font_name); + } else { + ref_count_inc = 0; + } + + if (cache_[font_name].ref_count_ == 0) { // Requested font is new to cache. + cache_[font_name].ref_count_ = 1; + } else { // Requested font is already in cache, release old handles. + DeleteObject(cache_[font_name].font_); + DeleteDC(cache_[font_name].dc_); } + cache_[font_name].font_ = font_handle; + cache_[font_name].dc_ = hdc; + cache_[font_name].ref_count_ += ref_count_inc; +} + +void ChildProcessHost::FontCache::ReleaseCachedFonts(int process_id) { + typedef std::map<string16, ChildProcessHost::FontCache::CacheElement> + FontNameToElement; + + base::AutoLock lock(mutex_); + + PidToFontNames::iterator it; + it = process_id_font_map_.find(process_id); + if (it == process_id_font_map_.end()) { + return; + } + + for (FontNameVector::iterator i = it->second.begin(), e = it->second.end(); + i != e; ++i) { + FontNameToElement::iterator element; + element = cache_.find(*i); + if (element != cache_.end()) { + --((*element).second.ref_count_); + } + } + + process_id_font_map_.erase(it); + for (FontNameToElement::iterator i = cache_.begin(); i != cache_.end(); ) { + if (i->second.ref_count_ == 0) { + cache_.erase(i++); + } else { + ++i; + } + } +} - fonts[font_index] = font_handle; - hdcs[font_index] = hdc; - font_index = (font_index + 1) % kFontCacheSize; +// static +void ChildProcessHost::PreCacheFont(LOGFONT font, int pid) { + // If a child process is running in a sandbox, GetTextMetrics() + // can sometimes fail. If a font has not been loaded + // previously, GetTextMetrics() will try to load the font + // from the font file. However, the sandboxed process does + // not have permissions to access any font files and + // the call fails. So we make the browser pre-load the + // font for us by using a dummy call to GetTextMetrics of + // the same font. + // This means the browser process just loads the font into memory so that + // when GDI attempt to query that font info in child process, it does not + // need to load that file, hence no permission issues there. Therefore, + // when a font is asked to be cached, we always recreates the font object + // to avoid the case that an in-cache font is swapped out by GDI. + ChildProcessHost::FontCache::GetInstance()->PreCacheFont(font, pid); +} + +// static +void ChildProcessHost::ReleaseCachedFonts(int pid) { + // Release cached fonts that requested from a pid by decrementing the ref + // count. When ref count is zero, the handles are released. + ChildProcessHost::FontCache::GetInstance()->ReleaseCachedFonts(pid); } #endif // OS_WIN |