Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / common / font_cache_dispatcher_win.cc
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 #include "ipc/ipc_channel.h"
14
15 namespace content {
16 namespace {
17 typedef std::vector<base::string16> FontNameVector;
18 typedef std::map<FontCacheDispatcher*, FontNameVector> DispatcherToFontNames;
19
20 class FontCache {
21  public:
22   static FontCache* GetInstance() {
23     return Singleton<FontCache>::get();
24   }
25
26   void PreCacheFont(const LOGFONT& font, FontCacheDispatcher* dispatcher) {
27     typedef std::map<base::string16, FontCache::CacheElement> FontNameToElement;
28
29     base::AutoLock lock(mutex_);
30
31     // Fetch the font into memory.
32     // No matter the font is cached or not, we load it to avoid GDI swapping out
33     // that font file.
34     HDC hdc = GetDC(NULL);
35     HFONT font_handle = CreateFontIndirect(&font);
36     DCHECK(NULL != font_handle);
37
38     HGDIOBJ old_font = SelectObject(hdc, font_handle);
39     DCHECK(NULL != old_font);
40
41     TEXTMETRIC tm;
42     BOOL ret = GetTextMetrics(hdc, &tm);
43     DCHECK(ret);
44
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(),
50                   font_name);
51     if (it == dispatcher_font_map_[dispatcher].end()) {
52       // Requested font is new to cache.
53       dispatcher_font_map_[dispatcher].push_back(font_name);
54     } else {
55       ref_count_inc = 0;
56     }
57
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_);
64     }
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;
69   }
70
71   void ReleaseCachedFonts(FontCacheDispatcher* dispatcher) {
72     typedef std::map<base::string16, FontCache::CacheElement> FontNameToElement;
73
74     base::AutoLock lock(mutex_);
75
76     DispatcherToFontNames::iterator it;
77     it = dispatcher_font_map_.find(dispatcher);
78     if (it == dispatcher_font_map_.end()) {
79       return;
80     }
81
82     for (FontNameVector::iterator i = it->second.begin(), e = it->second.end();
83                                   i != e; ++i) {
84       FontNameToElement::iterator element;
85       element = cache_.find(*i);
86       if (element != cache_.end()) {
87         --((*element).second.ref_count_);
88       }
89     }
90
91     dispatcher_font_map_.erase(it);
92     for (FontNameToElement::iterator i = cache_.begin(); i != cache_.end(); ) {
93       if (i->second.ref_count_ == 0) {
94         cache_.erase(i++);
95       } else {
96         ++i;
97       }
98     }
99   }
100
101  private:
102   struct CacheElement {
103     CacheElement()
104         : font_(NULL), old_font_(NULL), dc_(NULL), ref_count_(0) {
105     }
106
107     ~CacheElement() {
108       if (font_) {
109         if (dc_ && old_font_) {
110           SelectObject(dc_, old_font_);
111         }
112         DeleteObject(font_);
113       }
114       if (dc_) {
115         ReleaseDC(NULL, dc_);
116       }
117     }
118
119     HFONT font_;
120     HGDIOBJ old_font_;
121     HDC dc_;
122     int ref_count_;
123   };
124   friend struct DefaultSingletonTraits<FontCache>;
125
126   FontCache() {
127   }
128
129   std::map<base::string16, CacheElement> cache_;
130   DispatcherToFontNames dispatcher_font_map_;
131   base::Lock mutex_;
132
133   DISALLOW_COPY_AND_ASSIGN(FontCache);
134 };
135
136 }
137
138 FontCacheDispatcher::FontCacheDispatcher()
139     : channel_(NULL) {
140 }
141
142 FontCacheDispatcher::~FontCacheDispatcher() {
143 }
144
145 void FontCacheDispatcher::OnFilterAdded(IPC::Channel* channel) {
146   channel_ = channel;
147 }
148
149 bool FontCacheDispatcher::OnMessageReceived(const IPC::Message& message) {
150   bool handled = true;
151   IPC_BEGIN_MESSAGE_MAP(FontCacheDispatcher, message)
152     IPC_MESSAGE_HANDLER(ChildProcessHostMsg_PreCacheFont, OnPreCacheFont)
153     IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ReleaseCachedFonts,
154                         OnReleaseCachedFonts)
155     IPC_MESSAGE_UNHANDLED(handled = false)
156   IPC_END_MESSAGE_MAP()
157   return handled;
158 }
159
160 void FontCacheDispatcher::OnChannelClosing() {
161   channel_ = NULL;
162 }
163
164 bool FontCacheDispatcher::Send(IPC::Message* message) {
165   if (channel_)
166     return channel_->Send(message);
167
168   delete message;
169   return false;
170 }
171
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
180   // the same font.
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);
187 }
188
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);
193 }
194
195 }  // namespace content