1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/common/font_cache_dispatcher_win.h" 6 7#include <map> 8#include <vector> 9 10#include "base/logging.h" 11#include "base/strings/string16.h" 12#include "content/common/child_process_messages.h" 13 14namespace content { 15namespace { 16typedef std::vector<base::string16> FontNameVector; 17typedef std::map<FontCacheDispatcher*, FontNameVector> DispatcherToFontNames; 18 19class FontCache { 20 public: 21 static FontCache* GetInstance() { 22 return Singleton<FontCache>::get(); 23 } 24 25 void PreCacheFont(const LOGFONT& font, FontCacheDispatcher* dispatcher) { 26 typedef std::map<base::string16, FontCache::CacheElement> FontNameToElement; 27 28 base::AutoLock lock(mutex_); 29 30 // Fetch the font into memory. 31 // No matter the font is cached or not, we load it to avoid GDI swapping out 32 // that font file. 33 HDC hdc = GetDC(NULL); 34 HFONT font_handle = CreateFontIndirect(&font); 35 DCHECK(NULL != font_handle); 36 37 HGDIOBJ old_font = SelectObject(hdc, font_handle); 38 DCHECK(NULL != old_font); 39 40 TEXTMETRIC tm; 41 BOOL ret = GetTextMetrics(hdc, &tm); 42 DCHECK(ret); 43 44 base::string16 font_name = font.lfFaceName; 45 int ref_count_inc = 1; 46 FontNameVector::iterator it = 47 std::find(dispatcher_font_map_[dispatcher].begin(), 48 dispatcher_font_map_[dispatcher].end(), 49 font_name); 50 if (it == dispatcher_font_map_[dispatcher].end()) { 51 // Requested font is new to cache. 52 dispatcher_font_map_[dispatcher].push_back(font_name); 53 } else { 54 ref_count_inc = 0; 55 } 56 57 if (cache_[font_name].ref_count_ == 0) { // Requested font is new to cache. 58 cache_[font_name].ref_count_ = 1; 59 } else { // Requested font is already in cache, release old handles. 60 SelectObject(cache_[font_name].dc_, cache_[font_name].old_font_); 61 DeleteObject(cache_[font_name].font_); 62 ReleaseDC(NULL, cache_[font_name].dc_); 63 } 64 cache_[font_name].font_ = font_handle; 65 cache_[font_name].dc_ = hdc; 66 cache_[font_name].old_font_ = old_font; 67 cache_[font_name].ref_count_ += ref_count_inc; 68 } 69 70 void ReleaseCachedFonts(FontCacheDispatcher* dispatcher) { 71 typedef std::map<base::string16, FontCache::CacheElement> FontNameToElement; 72 73 base::AutoLock lock(mutex_); 74 75 DispatcherToFontNames::iterator it; 76 it = dispatcher_font_map_.find(dispatcher); 77 if (it == dispatcher_font_map_.end()) { 78 return; 79 } 80 81 for (FontNameVector::iterator i = it->second.begin(), e = it->second.end(); 82 i != e; ++i) { 83 FontNameToElement::iterator element; 84 element = cache_.find(*i); 85 if (element != cache_.end()) { 86 --((*element).second.ref_count_); 87 } 88 } 89 90 dispatcher_font_map_.erase(it); 91 for (FontNameToElement::iterator i = cache_.begin(); i != cache_.end(); ) { 92 if (i->second.ref_count_ == 0) { 93 cache_.erase(i++); 94 } else { 95 ++i; 96 } 97 } 98 } 99 100 private: 101 struct CacheElement { 102 CacheElement() 103 : font_(NULL), old_font_(NULL), dc_(NULL), ref_count_(0) { 104 } 105 106 ~CacheElement() { 107 if (font_) { 108 if (dc_ && old_font_) { 109 SelectObject(dc_, old_font_); 110 } 111 DeleteObject(font_); 112 } 113 if (dc_) { 114 ReleaseDC(NULL, dc_); 115 } 116 } 117 118 HFONT font_; 119 HGDIOBJ old_font_; 120 HDC dc_; 121 int ref_count_; 122 }; 123 friend struct DefaultSingletonTraits<FontCache>; 124 125 FontCache() { 126 } 127 128 std::map<base::string16, CacheElement> cache_; 129 DispatcherToFontNames dispatcher_font_map_; 130 base::Lock mutex_; 131 132 DISALLOW_COPY_AND_ASSIGN(FontCache); 133}; 134 135} 136 137FontCacheDispatcher::FontCacheDispatcher() 138 : channel_(NULL) { 139} 140 141FontCacheDispatcher::~FontCacheDispatcher() { 142} 143 144void FontCacheDispatcher::OnFilterAdded(IPC::Channel* channel) { 145 channel_ = channel; 146} 147 148bool FontCacheDispatcher::OnMessageReceived(const IPC::Message& message) { 149 bool handled = true; 150 IPC_BEGIN_MESSAGE_MAP(FontCacheDispatcher, message) 151 IPC_MESSAGE_HANDLER(ChildProcessHostMsg_PreCacheFont, OnPreCacheFont) 152 IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ReleaseCachedFonts, 153 OnReleaseCachedFonts) 154 IPC_MESSAGE_UNHANDLED(handled = false) 155 IPC_END_MESSAGE_MAP() 156 return handled; 157} 158 159void FontCacheDispatcher::OnChannelClosing() { 160 channel_ = NULL; 161} 162 163bool FontCacheDispatcher::Send(IPC::Message* message) { 164 if (channel_) 165 return channel_->Send(message); 166 167 delete message; 168 return false; 169} 170 171void FontCacheDispatcher::OnPreCacheFont(const LOGFONT& font) { 172 // If a child process is running in a sandbox, GetTextMetrics() 173 // can sometimes fail. If a font has not been loaded 174 // previously, GetTextMetrics() will try to load the font 175 // from the font file. However, the sandboxed process does 176 // not have permissions to access any font files and 177 // the call fails. So we make the browser pre-load the 178 // font for us by using a dummy call to GetTextMetrics of 179 // the same font. 180 // This means the browser process just loads the font into memory so that 181 // when GDI attempt to query that font info in child process, it does not 182 // need to load that file, hence no permission issues there. Therefore, 183 // when a font is asked to be cached, we always recreates the font object 184 // to avoid the case that an in-cache font is swapped out by GDI. 185 FontCache::GetInstance()->PreCacheFont(font, this); 186} 187 188void FontCacheDispatcher::OnReleaseCachedFonts() { 189 // Release cached fonts that requested from a pid by decrementing the ref 190 // count. When ref count is zero, the handles are released. 191 FontCache::GetInstance()->ReleaseCachedFonts(this); 192} 193 194} // namespace content 195