Android: Fix potential null-ptr deref
[chromium-blink-merge.git] / content / common / font_cache_dispatcher_win.cc
blob2181a08f98d92d73415941c3cdf92deb25a7acbd
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/strings/string16.h"
12 #include "content/common/child_process_messages.h"
14 namespace content {
15 namespace {
16 typedef std::vector<base::string16> FontNameVector;
17 typedef std::map<FontCacheDispatcher*, FontNameVector> DispatcherToFontNames;
19 class FontCache {
20 public:
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
32 // that font file.
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);
40 TEXTMETRIC tm;
41 BOOL ret = GetTextMetrics(hdc, &tm);
42 DCHECK(ret);
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;
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()) {
78 return;
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_);
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;
100 private:
101 struct CacheElement {
102 CacheElement()
103 : font_(NULL), old_font_(NULL), dc_(NULL), ref_count_(0) {
106 ~CacheElement() {
107 if (font_) {
108 if (dc_ && old_font_) {
109 SelectObject(dc_, old_font_);
111 DeleteObject(font_);
113 if (dc_) {
114 ReleaseDC(NULL, dc_);
118 HFONT font_;
119 HGDIOBJ old_font_;
120 HDC dc_;
121 int ref_count_;
123 friend struct DefaultSingletonTraits<FontCache>;
125 FontCache() {
128 std::map<base::string16, CacheElement> cache_;
129 DispatcherToFontNames dispatcher_font_map_;
130 base::Lock mutex_;
132 DISALLOW_COPY_AND_ASSIGN(FontCache);
137 FontCacheDispatcher::FontCacheDispatcher()
138 : sender_(NULL) {
141 FontCacheDispatcher::~FontCacheDispatcher() {
144 void FontCacheDispatcher::OnFilterAdded(IPC::Sender* sender) {
145 sender_ = sender;
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;
159 void FontCacheDispatcher::OnChannelClosing() {
160 sender_ = NULL;
163 bool FontCacheDispatcher::Send(IPC::Message* message) {
164 if (sender_)
165 return sender_->Send(message);
167 delete message;
168 return false;
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);
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