Home | History | Annotate | Download | only in common
      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 
     14 namespace content {
     15 namespace {
     16 typedef std::vector<string16> FontNameVector;
     17 typedef std::map<FontCacheDispatcher*, FontNameVector> DispatcherToFontNames;
     18 
     19 class FontCache {
     20  public:
     21   static FontCache* GetInstance() {
     22     return Singleton<FontCache>::get();
     23   }
     24 
     25   void PreCacheFont(const LOGFONT& font, FontCacheDispatcher* dispatcher) {
     26     typedef std::map<string16, FontCache::CacheElement> FontNameToElement;
     27 
     28     base::AutoLock lock(mutex_);
     29 
     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);
     36 
     37     HGDIOBJ old_font = SelectObject(hdc, font_handle);
     38     DCHECK(NULL != old_font);
     39 
     40     TEXTMETRIC tm;
     41     BOOL ret = GetTextMetrics(hdc, &tm);
     42     DCHECK(ret);
     43 
     44     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;
     55     }
     56 
     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_);
     63     }
     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;
     68   }
     69 
     70   void ReleaseCachedFonts(FontCacheDispatcher* dispatcher) {
     71     typedef std::map<string16, FontCache::CacheElement> FontNameToElement;
     72 
     73     base::AutoLock lock(mutex_);
     74 
     75     DispatcherToFontNames::iterator it;
     76     it = dispatcher_font_map_.find(dispatcher);
     77     if (it == dispatcher_font_map_.end()) {
     78       return;
     79     }
     80 
     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_);
     87       }
     88     }
     89 
     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;
     96       }
     97     }
     98   }
     99 
    100  private:
    101   struct CacheElement {
    102     CacheElement()
    103         : font_(NULL), old_font_(NULL), dc_(NULL), ref_count_(0) {
    104     }
    105 
    106     ~CacheElement() {
    107       if (font_) {
    108         if (dc_ && old_font_) {
    109           SelectObject(dc_, old_font_);
    110         }
    111         DeleteObject(font_);
    112       }
    113       if (dc_) {
    114         ReleaseDC(NULL, dc_);
    115       }
    116     }
    117 
    118     HFONT font_;
    119     HGDIOBJ old_font_;
    120     HDC dc_;
    121     int ref_count_;
    122   };
    123   friend struct DefaultSingletonTraits<FontCache>;
    124 
    125   FontCache() {
    126   }
    127 
    128   std::map<string16, CacheElement> cache_;
    129   DispatcherToFontNames dispatcher_font_map_;
    130   base::Lock mutex_;
    131 
    132   DISALLOW_COPY_AND_ASSIGN(FontCache);
    133 };
    134 
    135 }
    136 
    137 FontCacheDispatcher::FontCacheDispatcher()
    138     : channel_(NULL) {
    139 }
    140 
    141 FontCacheDispatcher::~FontCacheDispatcher() {
    142 }
    143 
    144 void FontCacheDispatcher::OnFilterAdded(IPC::Channel* channel) {
    145   channel_ = channel;
    146 }
    147 
    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;
    157 }
    158 
    159 void FontCacheDispatcher::OnChannelClosing() {
    160   channel_ = NULL;
    161 }
    162 
    163 bool FontCacheDispatcher::Send(IPC::Message* message) {
    164   if (channel_)
    165     return channel_->Send(message);
    166 
    167   delete message;
    168   return false;
    169 }
    170 
    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);
    186 }
    187 
    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);
    192 }
    193 
    194 }  // namespace content
    195