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.
5 #include "content/common/font_cache_dispatcher_win.h"
10 #include "base/logging.h"
11 #include "base/strings/string16.h"
12 #include "content/common/child_process_messages.h"
16 typedef std::vector
<base::string16
> FontNameVector
;
17 typedef std::map
<FontCacheDispatcher
*, FontNameVector
> DispatcherToFontNames
;
21 static FontCache
* GetInstance() {
22 return Singleton
<FontCache
>::get();
25 void PreCacheFont(const LOGFONT
& font
, FontCacheDispatcher
* dispatcher
) {
26 typedef std::map
<base::string16
, FontCache::CacheElement
> FontNameToElement
;
28 base::AutoLock
lock(mutex_
);
30 // Fetch the font into memory.
31 // No matter the font is cached or not, we load it to avoid GDI swapping out
33 HDC hdc
= GetDC(NULL
);
34 HFONT font_handle
= CreateFontIndirect(&font
);
35 DCHECK(NULL
!= font_handle
);
37 HGDIOBJ old_font
= SelectObject(hdc
, font_handle
);
38 DCHECK(NULL
!= old_font
);
41 BOOL ret
= GetTextMetrics(hdc
, &tm
);
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(),
50 if (it
== dispatcher_font_map_
[dispatcher
].end()) {
51 // Requested font is new to cache.
52 dispatcher_font_map_
[dispatcher
].push_back(font_name
);
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_
);
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
;
70 void ReleaseCachedFonts(FontCacheDispatcher
* dispatcher
) {
71 typedef std::map
<base::string16
, FontCache::CacheElement
> FontNameToElement
;
73 base::AutoLock
lock(mutex_
);
75 DispatcherToFontNames::iterator it
;
76 it
= dispatcher_font_map_
.find(dispatcher
);
77 if (it
== dispatcher_font_map_
.end()) {
81 for (FontNameVector::iterator i
= it
->second
.begin(), e
= it
->second
.end();
83 FontNameToElement::iterator element
;
84 element
= cache_
.find(*i
);
85 if (element
!= cache_
.end()) {
86 --((*element
).second
.ref_count_
);
90 dispatcher_font_map_
.erase(it
);
91 for (FontNameToElement::iterator i
= cache_
.begin(); i
!= cache_
.end(); ) {
92 if (i
->second
.ref_count_
== 0) {
101 struct CacheElement
{
103 : font_(NULL
), old_font_(NULL
), dc_(NULL
), ref_count_(0) {
108 if (dc_
&& old_font_
) {
109 SelectObject(dc_
, old_font_
);
114 ReleaseDC(NULL
, dc_
);
123 friend struct DefaultSingletonTraits
<FontCache
>;
128 std::map
<base::string16
, CacheElement
> cache_
;
129 DispatcherToFontNames dispatcher_font_map_
;
132 DISALLOW_COPY_AND_ASSIGN(FontCache
);
137 FontCacheDispatcher::FontCacheDispatcher()
141 FontCacheDispatcher::~FontCacheDispatcher() {
144 void FontCacheDispatcher::OnFilterAdded(IPC::Sender
* sender
) {
148 bool FontCacheDispatcher::OnMessageReceived(const IPC::Message
& message
) {
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()
159 void FontCacheDispatcher::OnChannelClosing() {
163 bool FontCacheDispatcher::Send(IPC::Message
* message
) {
165 return sender_
->Send(message
);
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
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);
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);
194 } // namespace content