// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/common/font_cache_dispatcher_win.h" #include #include #include "base/logging.h" #include "base/macros.h" #include "base/profiler/scoped_tracker.h" #include "base/strings/string16.h" #include "content/common/child_process_messages.h" namespace content { namespace { typedef std::vector FontNameVector; typedef std::map DispatcherToFontNames; class FontCache { public: static FontCache* GetInstance() { return base::Singleton::get(); } void PreCacheFont(const LOGFONT& font, FontCacheDispatcher* dispatcher) { // TODO(ananta): Remove ScopedTracker below once crbug.com/90127 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION("90127 FontCache::PreCacheFont")); 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); HGDIOBJ old_font = SelectObject(hdc, font_handle); DCHECK(NULL != old_font); TEXTMETRIC tm; BOOL ret = GetTextMetrics(hdc, &tm); DCHECK(ret); base::string16 font_name = font.lfFaceName; int ref_count_inc = 1; FontNameVector::iterator it = std::find(dispatcher_font_map_[dispatcher].begin(), dispatcher_font_map_[dispatcher].end(), font_name); if (it == dispatcher_font_map_[dispatcher].end()) { // Requested font is new to cache. dispatcher_font_map_[dispatcher].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. SelectObject(cache_[font_name].dc_, cache_[font_name].old_font_); DeleteObject(cache_[font_name].font_); ReleaseDC(NULL, cache_[font_name].dc_); } cache_[font_name].font_ = font_handle; cache_[font_name].dc_ = hdc; cache_[font_name].old_font_ = old_font; cache_[font_name].ref_count_ += ref_count_inc; } void ReleaseCachedFonts(FontCacheDispatcher* dispatcher) { typedef std::map FontNameToElement; base::AutoLock lock(mutex_); DispatcherToFontNames::iterator it; it = dispatcher_font_map_.find(dispatcher); if (it == dispatcher_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_); } } dispatcher_font_map_.erase(it); for (FontNameToElement::iterator i = cache_.begin(); i != cache_.end(); ) { if (i->second.ref_count_ == 0) { cache_.erase(i++); } else { ++i; } } } private: struct CacheElement { CacheElement() : font_(NULL), old_font_(NULL), dc_(NULL), ref_count_(0) { } ~CacheElement() { if (font_) { if (dc_ && old_font_) { SelectObject(dc_, old_font_); } DeleteObject(font_); } if (dc_) { ReleaseDC(NULL, dc_); } } HFONT font_; HGDIOBJ old_font_; HDC dc_; int ref_count_; }; friend struct base::DefaultSingletonTraits; FontCache() { } std::map cache_; DispatcherToFontNames dispatcher_font_map_; base::Lock mutex_; DISALLOW_COPY_AND_ASSIGN(FontCache); }; } FontCacheDispatcher::FontCacheDispatcher() : sender_(NULL) { } bool FontCacheDispatcher::Send(IPC::Message* message) { if (sender_) return sender_->Send(message); delete message; return false; } FontCacheDispatcher::~FontCacheDispatcher() { } void FontCacheDispatcher::OnFilterAdded(IPC::Sender* sender) { sender_ = sender; } bool FontCacheDispatcher::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(FontCacheDispatcher, message) IPC_MESSAGE_HANDLER(ChildProcessHostMsg_PreCacheFont, OnPreCacheFont) IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ReleaseCachedFonts, OnReleaseCachedFonts) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void FontCacheDispatcher::OnChannelClosing() { sender_ = NULL; } void FontCacheDispatcher::OnPreCacheFont(const 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. // 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. FontCache::GetInstance()->PreCacheFont(font, this); } void FontCacheDispatcher::OnReleaseCachedFonts() { // Release cached fonts that requested from a pid by decrementing the ref // count. When ref count is zero, the handles are released. FontCache::GetInstance()->ReleaseCachedFonts(this); } } // namespace content