1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178

content / common / font_cache_dispatcher_win.cc [blame]

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/public/common/font_cache_dispatcher_win.h"

#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/containers/contains.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/numerics/checked_math.h"
#include "base/thread_annotations.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"

namespace content {
namespace {
typedef std::vector<std::wstring> FontNameVector;
typedef std::map<FontCacheDispatcher*, FontNameVector> DispatcherToFontNames;

class FontCache {
 public:
  static FontCache* GetInstance() { return base::Singleton<FontCache>::get(); }

  FontCache(const FontCache&) = delete;
  FontCache& operator=(const FontCache&) = delete;

  void PreCacheFont(const LOGFONT& font, FontCacheDispatcher* dispatcher) {
    base::AutoLock lock(mutex_);

    // Fetch the font into memory.
    // No matter the font is cached or not, we load it to avoid GDI swapping out
    // that font file.
    HDC hdc = GetDC(NULL);
    HFONT font_handle = CreateFontIndirect(&font);
    DCHECK(NULL != font_handle);

    HGDIOBJ old_font = SelectObject(hdc, font_handle);
    DCHECK(NULL != old_font);

    TEXTMETRIC tm;
    BOOL ret = GetTextMetrics(hdc, &tm);
    DCHECK(ret);

    std::wstring font_name = font.lfFaceName;
    bool inc_ref_count = true;
    if (!base::Contains(dispatcher_font_map_[dispatcher], font_name)) {
      // Requested font is new to cache.
      dispatcher_font_map_[dispatcher].push_back(font_name);
    } else {
      inc_ref_count = false;
    }

    if (cache_[font_name].ref_count_.ValueOrDie() == 0) {
      // Requested font is new to cache.
      cache_[font_name].ref_count_ = 1;
    } else {  // Requested font is already in cache, release old handles.
      SelectObject(cache_[font_name].dc_, cache_[font_name].old_font_);
      DeleteObject(cache_[font_name].font_);
      ReleaseDC(NULL, cache_[font_name].dc_);
    }
    cache_[font_name].font_ = font_handle;
    cache_[font_name].dc_ = hdc;
    cache_[font_name].old_font_ = old_font;
    if (inc_ref_count) {
      cache_[font_name].ref_count_++;
    }
  }

  void ReleaseCachedFonts(FontCacheDispatcher* dispatcher) {
    typedef std::map<std::wstring, FontCache::CacheElement> FontNameToElement;

    base::AutoLock lock(mutex_);

    DispatcherToFontNames::iterator it;
    it = dispatcher_font_map_.find(dispatcher);
    if (it == dispatcher_font_map_.end()) {
      return;
    }

    for (FontNameVector::iterator i = it->second.begin(), e = it->second.end();
                                  i != e; ++i) {
      FontNameToElement::iterator element;
      element = cache_.find(*i);
      if (element != cache_.end()) {
        --((*element).second.ref_count_);
      }
    }

    dispatcher_font_map_.erase(it);
    for (FontNameToElement::iterator i = cache_.begin(); i != cache_.end(); ) {
      if (i->second.ref_count_.ValueOrDie() == 0) {
        cache_.erase(i++);
      } else {
        ++i;
      }
    }
  }

 private:
  struct CacheElement {
    CacheElement()
        : font_(NULL), old_font_(NULL), dc_(NULL), ref_count_(0) {
    }

    ~CacheElement() {
      if (font_) {
        if (dc_ && old_font_) {
          SelectObject(dc_, old_font_);
        }
        DeleteObject(font_);
      }
      if (dc_) {
        ReleaseDC(NULL, dc_);
      }
    }

    HFONT font_;
    HGDIOBJ old_font_;
    HDC dc_;
    base::CheckedNumeric<size_t> ref_count_;
  };
  friend struct base::DefaultSingletonTraits<FontCache>;

  FontCache() {
  }

  std::map<std::wstring, CacheElement> cache_ GUARDED_BY(mutex_);
  DispatcherToFontNames dispatcher_font_map_ GUARDED_BY(mutex_);
  base::Lock mutex_;
};

}  // namespace

FontCacheDispatcher::FontCacheDispatcher() {}

FontCacheDispatcher::~FontCacheDispatcher() {
}

// static
void FontCacheDispatcher::Create(
    mojo::PendingReceiver<mojom::FontCacheWin> receiver) {
  mojo::MakeSelfOwnedReceiver(std::make_unique<FontCacheDispatcher>(),
                              std::move(receiver));
}

void FontCacheDispatcher::PreCacheFont(const LOGFONT& log_font,
                                       PreCacheFontCallback callback) {
  // If a child process is running in a sandbox, GetTextMetrics()
  // can sometimes fail. If a font has not been loaded
  // previously, GetTextMetrics() will try to load the font
  // from the font file. However, the sandboxed process does
  // not have permissions to access any font files and
  // the call fails. So we make the browser pre-load the
  // font for us by using a dummy call to GetTextMetrics of
  // the same font.
  // This means the browser process just loads the font into memory so that
  // when GDI attempt to query that font info in child process, it does not
  // need to load that file, hence no permission issues there.  Therefore,
  // when a font is asked to be cached, we always recreates the font object
  // to avoid the case that an in-cache font is swapped out by GDI.
  FontCache::GetInstance()->PreCacheFont(log_font, this);

  // Run |callback| to indicate this synchronous handler finished.
  std::move(callback).Run();
}

void FontCacheDispatcher::ReleaseCachedFonts() {
  // Release cached fonts that requested from a pid by decrementing the ref
  // count.  When ref count is zero, the handles are released.
  FontCache::GetInstance()->ReleaseCachedFonts(this);
}

}  // namespace content