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/profiler/scoped_tracker.h"
12 #include "base/strings/string16.h"
13 #include "content/common/child_process_messages.h"
17 typedef std::vector
<base::string16
> FontNameVector
;
18 typedef std::map
<FontCacheDispatcher
*, FontNameVector
> DispatcherToFontNames
;
22 static FontCache
* GetInstance() { return base::Singleton
<FontCache
>::get(); }
24 void PreCacheFont(const LOGFONT
& font
, FontCacheDispatcher
* dispatcher
) {
25 // TODO(ananta): Remove ScopedTracker below once crbug.com/90127 is fixed.
26 tracked_objects::ScopedTracker
tracking_profile(
27 FROM_HERE_WITH_EXPLICIT_FUNCTION("90127 FontCache::PreCacheFont"));
29 base::AutoLock
lock(mutex_
);
31 // Fetch the font into memory.
32 // No matter the font is cached or not, we load it to avoid GDI swapping out
34 HDC hdc
= GetDC(NULL
);
35 HFONT font_handle
= CreateFontIndirect(&font
);
36 DCHECK(NULL
!= font_handle
);
38 HGDIOBJ old_font
= SelectObject(hdc
, font_handle
);
39 DCHECK(NULL
!= old_font
);
42 BOOL ret
= GetTextMetrics(hdc
, &tm
);
45 base::string16 font_name
= font
.lfFaceName
;
46 int ref_count_inc
= 1;
47 FontNameVector::iterator it
=
48 std::find(dispatcher_font_map_
[dispatcher
].begin(),
49 dispatcher_font_map_
[dispatcher
].end(),
51 if (it
== dispatcher_font_map_
[dispatcher
].end()) {
52 // Requested font is new to cache.
53 dispatcher_font_map_
[dispatcher
].push_back(font_name
);
58 if (cache_
[font_name
].ref_count_
== 0) { // Requested font is new to cache.
59 cache_
[font_name
].ref_count_
= 1;
60 } else { // Requested font is already in cache, release old handles.
61 SelectObject(cache_
[font_name
].dc_
, cache_
[font_name
].old_font_
);
62 DeleteObject(cache_
[font_name
].font_
);
63 ReleaseDC(NULL
, cache_
[font_name
].dc_
);
65 cache_
[font_name
].font_
= font_handle
;
66 cache_
[font_name
].dc_
= hdc
;
67 cache_
[font_name
].old_font_
= old_font
;
68 cache_
[font_name
].ref_count_
+= ref_count_inc
;
71 void ReleaseCachedFonts(FontCacheDispatcher
* dispatcher
) {
72 typedef std::map
<base::string16
, FontCache::CacheElement
> FontNameToElement
;
74 base::AutoLock
lock(mutex_
);
76 DispatcherToFontNames::iterator it
;
77 it
= dispatcher_font_map_
.find(dispatcher
);
78 if (it
== dispatcher_font_map_
.end()) {
82 for (FontNameVector::iterator i
= it
->second
.begin(), e
= it
->second
.end();
84 FontNameToElement::iterator element
;
85 element
= cache_
.find(*i
);
86 if (element
!= cache_
.end()) {
87 --((*element
).second
.ref_count_
);
91 dispatcher_font_map_
.erase(it
);
92 for (FontNameToElement::iterator i
= cache_
.begin(); i
!= cache_
.end(); ) {
93 if (i
->second
.ref_count_
== 0) {
102 struct CacheElement
{
104 : font_(NULL
), old_font_(NULL
), dc_(NULL
), ref_count_(0) {
109 if (dc_
&& old_font_
) {
110 SelectObject(dc_
, old_font_
);
115 ReleaseDC(NULL
, dc_
);
124 friend struct base::DefaultSingletonTraits
<FontCache
>;
129 std::map
<base::string16
, CacheElement
> cache_
;
130 DispatcherToFontNames dispatcher_font_map_
;
133 DISALLOW_COPY_AND_ASSIGN(FontCache
);
138 FontCacheDispatcher::FontCacheDispatcher()
142 bool FontCacheDispatcher::Send(IPC::Message
* message
) {
144 return sender_
->Send(message
);
150 FontCacheDispatcher::~FontCacheDispatcher() {
153 void FontCacheDispatcher::OnFilterAdded(IPC::Sender
* sender
) {
157 bool FontCacheDispatcher::OnMessageReceived(const IPC::Message
& message
) {
159 IPC_BEGIN_MESSAGE_MAP(FontCacheDispatcher
, message
)
160 IPC_MESSAGE_HANDLER(ChildProcessHostMsg_PreCacheFont
, OnPreCacheFont
)
161 IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ReleaseCachedFonts
,
162 OnReleaseCachedFonts
)
163 IPC_MESSAGE_UNHANDLED(handled
= false)
164 IPC_END_MESSAGE_MAP()
168 void FontCacheDispatcher::OnChannelClosing() {
172 void FontCacheDispatcher::OnPreCacheFont(const LOGFONT
& font
) {
173 // If a child process is running in a sandbox, GetTextMetrics()
174 // can sometimes fail. If a font has not been loaded
175 // previously, GetTextMetrics() will try to load the font
176 // from the font file. However, the sandboxed process does
177 // not have permissions to access any font files and
178 // the call fails. So we make the browser pre-load the
179 // font for us by using a dummy call to GetTextMetrics of
181 // This means the browser process just loads the font into memory so that
182 // when GDI attempt to query that font info in child process, it does not
183 // need to load that file, hence no permission issues there. Therefore,
184 // when a font is asked to be cached, we always recreates the font object
185 // to avoid the case that an in-cache font is swapped out by GDI.
186 FontCache::GetInstance()->PreCacheFont(font
, this);
189 void FontCacheDispatcher::OnReleaseCachedFonts() {
190 // Release cached fonts that requested from a pid by decrementing the ref
191 // count. When ref count is zero, the handles are released.
192 FontCache::GetInstance()->ReleaseCachedFonts(this);
195 } // namespace content