A few more instrumentation updates
[chromium-blink-merge.git] / content / common / font_cache_dispatcher_win.cc
blob69aa7ae73a6e8ffb4e41b21424420b94c46c8f1b
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"
7 #include <map>
8 #include <vector>
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"
15 namespace content {
16 namespace {
17 typedef std::vector<base::string16> FontNameVector;
18 typedef std::map<FontCacheDispatcher*, FontNameVector> DispatcherToFontNames;
20 class FontCache {
21 public:
22 static FontCache* GetInstance() {
23 return Singleton<FontCache>::get();
26 void PreCacheFont(const LOGFONT& font, FontCacheDispatcher* dispatcher) {
27 // TODO(ananta): Remove ScopedTracker below once crbug.com/90127 is fixed.
28 tracked_objects::ScopedTracker tracking_profile(
29 FROM_HERE_WITH_EXPLICIT_FUNCTION("90127 FontCache::PreCacheFont"));
31 typedef std::map<base::string16, FontCache::CacheElement> FontNameToElement;
33 base::AutoLock lock(mutex_);
35 // Fetch the font into memory.
36 // No matter the font is cached or not, we load it to avoid GDI swapping out
37 // that font file.
38 HDC hdc = GetDC(NULL);
39 HFONT font_handle = CreateFontIndirect(&font);
40 DCHECK(NULL != font_handle);
42 HGDIOBJ old_font = SelectObject(hdc, font_handle);
43 DCHECK(NULL != old_font);
45 TEXTMETRIC tm;
46 BOOL ret = GetTextMetrics(hdc, &tm);
47 DCHECK(ret);
49 base::string16 font_name = font.lfFaceName;
50 int ref_count_inc = 1;
51 FontNameVector::iterator it =
52 std::find(dispatcher_font_map_[dispatcher].begin(),
53 dispatcher_font_map_[dispatcher].end(),
54 font_name);
55 if (it == dispatcher_font_map_[dispatcher].end()) {
56 // Requested font is new to cache.
57 dispatcher_font_map_[dispatcher].push_back(font_name);
58 } else {
59 ref_count_inc = 0;
62 if (cache_[font_name].ref_count_ == 0) { // Requested font is new to cache.
63 cache_[font_name].ref_count_ = 1;
64 } else { // Requested font is already in cache, release old handles.
65 SelectObject(cache_[font_name].dc_, cache_[font_name].old_font_);
66 DeleteObject(cache_[font_name].font_);
67 ReleaseDC(NULL, cache_[font_name].dc_);
69 cache_[font_name].font_ = font_handle;
70 cache_[font_name].dc_ = hdc;
71 cache_[font_name].old_font_ = old_font;
72 cache_[font_name].ref_count_ += ref_count_inc;
75 void ReleaseCachedFonts(FontCacheDispatcher* dispatcher) {
76 typedef std::map<base::string16, FontCache::CacheElement> FontNameToElement;
78 base::AutoLock lock(mutex_);
80 DispatcherToFontNames::iterator it;
81 it = dispatcher_font_map_.find(dispatcher);
82 if (it == dispatcher_font_map_.end()) {
83 return;
86 for (FontNameVector::iterator i = it->second.begin(), e = it->second.end();
87 i != e; ++i) {
88 FontNameToElement::iterator element;
89 element = cache_.find(*i);
90 if (element != cache_.end()) {
91 --((*element).second.ref_count_);
95 dispatcher_font_map_.erase(it);
96 for (FontNameToElement::iterator i = cache_.begin(); i != cache_.end(); ) {
97 if (i->second.ref_count_ == 0) {
98 cache_.erase(i++);
99 } else {
100 ++i;
105 private:
106 struct CacheElement {
107 CacheElement()
108 : font_(NULL), old_font_(NULL), dc_(NULL), ref_count_(0) {
111 ~CacheElement() {
112 if (font_) {
113 if (dc_ && old_font_) {
114 SelectObject(dc_, old_font_);
116 DeleteObject(font_);
118 if (dc_) {
119 ReleaseDC(NULL, dc_);
123 HFONT font_;
124 HGDIOBJ old_font_;
125 HDC dc_;
126 int ref_count_;
128 friend struct DefaultSingletonTraits<FontCache>;
130 FontCache() {
133 std::map<base::string16, CacheElement> cache_;
134 DispatcherToFontNames dispatcher_font_map_;
135 base::Lock mutex_;
137 DISALLOW_COPY_AND_ASSIGN(FontCache);
142 FontCacheDispatcher::FontCacheDispatcher()
143 : sender_(NULL) {
146 FontCacheDispatcher::~FontCacheDispatcher() {
149 void FontCacheDispatcher::OnFilterAdded(IPC::Sender* sender) {
150 sender_ = sender;
153 bool FontCacheDispatcher::OnMessageReceived(const IPC::Message& message) {
154 bool handled = true;
155 IPC_BEGIN_MESSAGE_MAP(FontCacheDispatcher, message)
156 IPC_MESSAGE_HANDLER(ChildProcessHostMsg_PreCacheFont, OnPreCacheFont)
157 IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ReleaseCachedFonts,
158 OnReleaseCachedFonts)
159 IPC_MESSAGE_UNHANDLED(handled = false)
160 IPC_END_MESSAGE_MAP()
161 return handled;
164 void FontCacheDispatcher::OnChannelClosing() {
165 sender_ = NULL;
168 bool FontCacheDispatcher::Send(IPC::Message* message) {
169 if (sender_)
170 return sender_->Send(message);
172 delete message;
173 return false;
176 void FontCacheDispatcher::OnPreCacheFont(const LOGFONT& font) {
177 // If a child process is running in a sandbox, GetTextMetrics()
178 // can sometimes fail. If a font has not been loaded
179 // previously, GetTextMetrics() will try to load the font
180 // from the font file. However, the sandboxed process does
181 // not have permissions to access any font files and
182 // the call fails. So we make the browser pre-load the
183 // font for us by using a dummy call to GetTextMetrics of
184 // the same font.
185 // This means the browser process just loads the font into memory so that
186 // when GDI attempt to query that font info in child process, it does not
187 // need to load that file, hence no permission issues there. Therefore,
188 // when a font is asked to be cached, we always recreates the font object
189 // to avoid the case that an in-cache font is swapped out by GDI.
190 FontCache::GetInstance()->PreCacheFont(font, this);
193 void FontCacheDispatcher::OnReleaseCachedFonts() {
194 // Release cached fonts that requested from a pid by decrementing the ref
195 // count. When ref count is zero, the handles are released.
196 FontCache::GetInstance()->ReleaseCachedFonts(this);
199 } // namespace content