• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
14 namespace content {
15 namespace {
16 typedef std::vector<base::string16> FontNameVector;
17 typedef std::map<FontCacheDispatcher*, FontNameVector> DispatcherToFontNames;
18 
19 class FontCache {
20  public:
GetInstance()21   static FontCache* GetInstance() {
22     return Singleton<FontCache>::get();
23   }
24 
PreCacheFont(const LOGFONT & font,FontCacheDispatcher * dispatcher)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 
ReleaseCachedFonts(FontCacheDispatcher * dispatcher)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 {
CacheElementcontent::__anon1dc17d880111::FontCache::CacheElement102     CacheElement()
103         : font_(NULL), old_font_(NULL), dc_(NULL), ref_count_(0) {
104     }
105 
~CacheElementcontent::__anon1dc17d880111::FontCache::CacheElement106     ~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 
FontCache()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 
FontCacheDispatcher()137 FontCacheDispatcher::FontCacheDispatcher()
138     : channel_(NULL) {
139 }
140 
~FontCacheDispatcher()141 FontCacheDispatcher::~FontCacheDispatcher() {
142 }
143 
OnFilterAdded(IPC::Channel * channel)144 void FontCacheDispatcher::OnFilterAdded(IPC::Channel* channel) {
145   channel_ = channel;
146 }
147 
OnMessageReceived(const IPC::Message & message)148 bool 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 
OnChannelClosing()159 void FontCacheDispatcher::OnChannelClosing() {
160   channel_ = NULL;
161 }
162 
Send(IPC::Message * message)163 bool FontCacheDispatcher::Send(IPC::Message* message) {
164   if (channel_)
165     return channel_->Send(message);
166 
167   delete message;
168   return false;
169 }
170 
OnPreCacheFont(const LOGFONT & font)171 void 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 
OnReleaseCachedFonts()188 void 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