diff options
author | arthurhsu@chromium.org <arthurhsu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-20 03:33:21 +0000 |
---|---|---|
committer | arthurhsu@chromium.org <arthurhsu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-20 03:33:21 +0000 |
commit | 79fff8227ab720a8bf309107ef96f57ae93592c1 (patch) | |
tree | cb9c7d286ece595404e5dde9ab381820017b40dc | |
parent | f354e25b2905341d67166e20709a71163004de73 (diff) | |
download | chromium_src-79fff8227ab720a8bf309107ef96f57ae93592c1.zip chromium_src-79fff8227ab720a8bf309107ef96f57ae93592c1.tar.gz chromium_src-79fff8227ab720a8bf309107ef96f57ae93592c1.tar.bz2 |
New implementation of font precache on Windows.
BUG=94421
TEST=none
Review URL: http://codereview.chromium.org/7866019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@101911 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/common/chrome_utility_messages.h | 7 | ||||
-rw-r--r-- | chrome/renderer/chrome_render_process_observer.cc | 1 | ||||
-rw-r--r-- | chrome/service/service_utility_process_host.cc | 12 | ||||
-rw-r--r-- | chrome/service/service_utility_process_host.h | 3 | ||||
-rw-r--r-- | chrome/utility/chrome_content_utility_client.cc | 6 | ||||
-rw-r--r-- | content/browser/renderer_host/render_message_filter.cc | 17 | ||||
-rw-r--r-- | content/browser/renderer_host/render_message_filter.h | 6 | ||||
-rw-r--r-- | content/common/child_process_host.cc | 137 | ||||
-rw-r--r-- | content/common/child_process_host.h | 35 | ||||
-rw-r--r-- | content/common/child_process_messages.h | 10 | ||||
-rw-r--r-- | content/common/view_messages.h | 7 | ||||
-rw-r--r-- | content/renderer/render_thread.cc | 10 | ||||
-rw-r--r-- | content/renderer/render_thread.h | 3 | ||||
-rw-r--r-- | content/renderer/renderer_webkitplatformsupport_impl.cc | 4 |
14 files changed, 212 insertions, 46 deletions
diff --git a/chrome/common/chrome_utility_messages.h b/chrome/common/chrome_utility_messages.h index c993653..d808f8d 100644 --- a/chrome/common/chrome_utility_messages.h +++ b/chrome/common/chrome_utility_messages.h @@ -143,13 +143,6 @@ IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Succeeded, // Reply when an error occured rendering the PDF. IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed) -#if defined(OS_WIN) -// Request that the given font be loaded by the host so it's cached by the -// OS. Please see ChildProcessHost::PreCacheFont for details. -IPC_SYNC_MESSAGE_CONTROL1_0(ChromeUtilityHostMsg_PreCacheFont, - LOGFONT /* font data */) -#endif // defined(OS_WIN) - // Reply when the utility process successfully parsed a JSON string. // // WARNING: The result can be of any Value subclass type, but we can't easily diff --git a/chrome/renderer/chrome_render_process_observer.cc b/chrome/renderer/chrome_render_process_observer.cc index 1ab4815..a1ec2b3 100644 --- a/chrome/renderer/chrome_render_process_observer.cc +++ b/chrome/renderer/chrome_render_process_observer.cc @@ -165,6 +165,7 @@ DWORD WINAPI GetFontDataPatch(HDC hdc, std::vector<char> font_data; if (RenderThread::PreCacheFont(logfont)) rv = GetFontData(hdc, table, offset, buffer, length); + RenderThread::ReleaseCachedFonts(); } } return rv; diff --git a/chrome/service/service_utility_process_host.cc b/chrome/service/service_utility_process_host.cc index 0c4e88c..760fc23 100644 --- a/chrome/service/service_utility_process_host.cc +++ b/chrome/service/service_utility_process_host.cc @@ -20,6 +20,7 @@ #if defined(OS_WIN) #include "base/memory/scoped_ptr.h" #include "base/win/scoped_handle.h" +#include "content/common/child_process_messages.h" #include "printing/emf_win.h" #endif @@ -29,6 +30,7 @@ ServiceUtilityProcessHost::ServiceUtilityProcessHost( client_(client), client_message_loop_proxy_(client_message_loop_proxy), waiting_for_reply_(false) { + process_id_ = ChildProcessInfo::GenerateChildProcessUniqueId(); } ServiceUtilityProcessHost::~ServiceUtilityProcessHost() { @@ -144,7 +146,9 @@ bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) { bool msg_is_ok = false; IPC_BEGIN_MESSAGE_MAP_EX(ServiceUtilityProcessHost, message, msg_is_ok) #if defined(OS_WIN) // This hack is Windows-specific. - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_PreCacheFont, OnPreCacheFont) + IPC_MESSAGE_HANDLER(ChildProcessHostMsg_PreCacheFont, OnPreCacheFont) + IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ReleaseCachedFonts, + OnReleaseCachedFonts) #endif IPC_MESSAGE_HANDLER( ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Succeeded, @@ -166,7 +170,11 @@ bool ServiceUtilityProcessHost::MessageForClient(const IPC::Message& message) { #if defined(OS_WIN) // This hack is Windows-specific. void ServiceUtilityProcessHost::OnPreCacheFont(const LOGFONT& font) { - PreCacheFont(font); + PreCacheFont(font, process_id_); +} + +void ServiceUtilityProcessHost::OnReleaseCachedFonts() { + ReleaseCachedFonts(process_id_); } #endif // OS_WIN diff --git a/chrome/service/service_utility_process_host.h b/chrome/service/service_utility_process_host.h index b3802f9..f24bd3a 100644 --- a/chrome/service/service_utility_process_host.h +++ b/chrome/service/service_utility_process_host.h @@ -131,6 +131,7 @@ class ServiceUtilityProcessHost : public ServiceChildProcessHost { #if defined(OS_WIN) // This hack is Windows-specific. void OnPreCacheFont(const LOGFONT& font); + void OnReleaseCachedFonts(); #endif // defined(OS_WIN) // A pointer to our client interface, who will be informed of progress. @@ -141,6 +142,8 @@ class ServiceUtilityProcessHost : public ServiceChildProcessHost { FilePath metafile_path_; // The temporary folder created for the metafile. scoped_ptr<ScopedTempDir> scratch_metafile_dir_; + // The unique id created for the process. + int process_id_; DISALLOW_COPY_AND_ASSIGN(ServiceUtilityProcessHost); }; diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc index 2f1b895..8beb181 100644 --- a/chrome/utility/chrome_content_utility_client.cc +++ b/chrome/utility/chrome_content_utility_client.cc @@ -27,6 +27,7 @@ #include "base/path_service.h" #include "base/win/iat_patch_function.h" #include "base/win/scoped_handle.h" +#include "content/common/child_process_messages.h" #include "content/common/content_switches.h" #include "content/common/sandbox_init_wrapper.h" #include "printing/emf_win.h" @@ -223,8 +224,11 @@ DWORD WINAPI UtilityProcess_GetFontDataPatch( if (GetObject(font, sizeof(LOGFONT), &logfont)) { std::vector<char> font_data; if (UtilityThread::current()->Send( - new ChromeUtilityHostMsg_PreCacheFont(logfont))) + new ChildProcessHostMsg_PreCacheFont(logfont))) { rv = GetFontData(hdc, table, offset, buffer, length); + UtilityThread::current()->Send( + new ChildProcessHostMsg_ReleaseCachedFonts()); + } } } return rv; diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc index 25eb0d2..d64b464 100644 --- a/content/browser/renderer_host/render_message_filter.cc +++ b/content/browser/renderer_host/render_message_filter.cc @@ -64,6 +64,7 @@ #endif #if defined(OS_WIN) #include "content/common/child_process_host.h" +#include "content/common/child_process_messages.h" #endif using net::CookieStore; @@ -285,6 +286,12 @@ void RenderMessageFilter::OnChannelClosing() { plugin_host_clients_.clear(); } +#if defined (OS_WIN) +void RenderMessageFilter::OnChannelError() { + ChildProcessHost::ReleaseCachedFonts(render_process_id_); +} +#endif + void RenderMessageFilter::OverrideThreadForMessage(const IPC::Message& message, BrowserThread::ID* thread) { switch (message.type()) { @@ -313,7 +320,9 @@ bool RenderMessageFilter::OnMessageReceived(const IPC::Message& message, IPC_MESSAGE_HANDLER(ViewHostMsg_GetRootWindowRect, OnGetRootWindowRect) // This hack is Windows-specific. - IPC_MESSAGE_HANDLER(ViewHostMsg_PreCacheFont, OnPreCacheFont) + IPC_MESSAGE_HANDLER(ChildProcessHostMsg_PreCacheFont, OnPreCacheFont) + IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ReleaseCachedFonts, + OnReleaseCachedFonts) #endif IPC_MESSAGE_HANDLER(ViewHostMsg_GenerateRoutingID, OnGenerateRoutingID) @@ -494,7 +503,11 @@ void RenderMessageFilter::OnLoadFont(const FontDescriptor& font, #if defined(OS_WIN) // This hack is Windows-specific. void RenderMessageFilter::OnPreCacheFont(const LOGFONT& font) { - ChildProcessHost::PreCacheFont(font); + ChildProcessHost::PreCacheFont(font, render_process_id_); +} + +void RenderMessageFilter::OnReleaseCachedFonts() { + ChildProcessHost::ReleaseCachedFonts(render_process_id_); } #endif // OS_WIN diff --git a/content/browser/renderer_host/render_message_filter.h b/content/browser/renderer_host/render_message_filter.h index 70a0d8f..4079134 100644 --- a/content/browser/renderer_host/render_message_filter.h +++ b/content/browser/renderer_host/render_message_filter.h @@ -78,6 +78,9 @@ class RenderMessageFilter : public BrowserMessageFilter { // IPC::ChannelProxy::MessageFilter methods: virtual void OnChannelClosing() OVERRIDE; +#if defined (OS_WIN) + virtual void OnChannelError() OVERRIDE; +#endif // BrowserMessageFilter methods: virtual void OverrideThreadForMessage(const IPC::Message& message, @@ -148,6 +151,9 @@ class RenderMessageFilter : public BrowserMessageFilter { // Cache fonts for the renderer. See RenderMessageFilter::OnPreCacheFont // implementation for more details. void OnPreCacheFont(const LOGFONT& font); + + // Release fonts cached for renderer. + void OnReleaseCachedFonts(); #endif void OnGetPlugins(bool refresh, diff --git a/content/common/child_process_host.cc b/content/common/child_process_host.cc index b8a3b95..65a9b7a 100644 --- a/content/common/child_process_host.cc +++ b/content/common/child_process_host.cc @@ -66,6 +66,14 @@ FilePath TransformPathForFeature(const FilePath& path, } // namespace #endif // OS_MACOSX +#if defined (OS_WIN) +// Types used in PreCacheFont +namespace { +typedef std::vector<string16> FontNameVector; +typedef std::map<int, FontNameVector> PidToFontNames; +} +#endif // OS_WIN + ChildProcessHost::ChildProcessHost() : ALLOW_THIS_IN_INITIALIZER_LIST(listener_(this)), opening_channel_(false) { @@ -131,27 +139,39 @@ FilePath ChildProcessHost::GetChildPath(int flags) { } #if defined(OS_WIN) +ChildProcessHost::FontCache::CacheElement::CacheElement() + : font_(NULL), dc_(NULL), ref_count_(0) { +} + +ChildProcessHost::FontCache::CacheElement::~CacheElement() { + if (font_) { + DeleteObject(font_); + } + if (dc_) { + DeleteDC(dc_); + } +} + +ChildProcessHost::FontCache::FontCache() { +} + +ChildProcessHost::FontCache::~FontCache() { +} + // static -void ChildProcessHost::PreCacheFont(LOGFONT font) { - // 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. +ChildProcessHost::FontCache* ChildProcessHost::FontCache::GetInstance() { + return Singleton<ChildProcessHost::FontCache>::get(); +} - // Maintain a circular queue for the fonts and DCs to be cached. - // font_index maintains next available location in the queue. - static const int kFontCacheSize = 32; - static HFONT fonts[kFontCacheSize] = {0}; - static HDC hdcs[kFontCacheSize] = {0}; - static size_t font_index = 0; +void ChildProcessHost::FontCache::PreCacheFont(LOGFONT font, int process_id) { + typedef std::map<string16, ChildProcessHost::FontCache::CacheElement> + FontNameToElement; - UMA_HISTOGRAM_COUNTS_100("Memory.CachedFontAndDC", - fonts[kFontCacheSize-1] ? kFontCacheSize : static_cast<int>(font_index)); + 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); @@ -163,15 +183,84 @@ void ChildProcessHost::PreCacheFont(LOGFONT font) { BOOL ret = GetTextMetrics(hdc, &tm); DCHECK(ret); - if (fonts[font_index] || hdcs[font_index]) { - // We already have too many fonts, we will delete one and take it's place. - DeleteObject(fonts[font_index]); - ReleaseDC(NULL, hdcs[font_index]); + string16 font_name = font.lfFaceName; + int ref_count_inc = 1; + FontNameVector::iterator it = + std::find(process_id_font_map_[process_id].begin(), + process_id_font_map_[process_id].end(), + font_name); + if (it == process_id_font_map_[process_id].end()) { + // Requested font is new to cache. + process_id_font_map_[process_id].push_back(font_name); + } else { + ref_count_inc = 0; + } + + if (cache_[font_name].ref_count_ == 0) { // Requested font is new to cache. + cache_[font_name].ref_count_ = 1; + } else { // Requested font is already in cache, release old handles. + DeleteObject(cache_[font_name].font_); + DeleteDC(cache_[font_name].dc_); } + cache_[font_name].font_ = font_handle; + cache_[font_name].dc_ = hdc; + cache_[font_name].ref_count_ += ref_count_inc; +} + +void ChildProcessHost::FontCache::ReleaseCachedFonts(int process_id) { + typedef std::map<string16, ChildProcessHost::FontCache::CacheElement> + FontNameToElement; + + base::AutoLock lock(mutex_); + + PidToFontNames::iterator it; + it = process_id_font_map_.find(process_id); + if (it == process_id_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_); + } + } + + process_id_font_map_.erase(it); + for (FontNameToElement::iterator i = cache_.begin(); i != cache_.end(); ) { + if (i->second.ref_count_ == 0) { + cache_.erase(i++); + } else { + ++i; + } + } +} - fonts[font_index] = font_handle; - hdcs[font_index] = hdc; - font_index = (font_index + 1) % kFontCacheSize; +// static +void ChildProcessHost::PreCacheFont(LOGFONT font, int pid) { + // 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. + ChildProcessHost::FontCache::GetInstance()->PreCacheFont(font, pid); +} + +// static +void ChildProcessHost::ReleaseCachedFonts(int pid) { + // Release cached fonts that requested from a pid by decrementing the ref + // count. When ref count is zero, the handles are released. + ChildProcessHost::FontCache::GetInstance()->ReleaseCachedFonts(pid); } #endif // OS_WIN diff --git a/content/common/child_process_host.h b/content/common/child_process_host.h index 5b965b9..a4a7c55 100644 --- a/content/common/child_process_host.h +++ b/content/common/child_process_host.h @@ -8,6 +8,7 @@ #include <string> #include <vector> +#include <map> #include "build/build_config.h" @@ -17,6 +18,8 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "base/string16.h" #include "content/common/content_export.h" #include "content/common/content_notification_types.h" #include "ipc/ipc_channel_proxy.h" @@ -87,7 +90,8 @@ class CONTENT_EXPORT ChildProcessHost : public IPC::Channel::Listener, #if defined(OS_WIN) // See comments in the cc file. This is a common hack needed for a process // hosting a sandboxed child process. Hence it lives in this file. - static void PreCacheFont(LOGFONT font); + static void PreCacheFont(LOGFONT font, int pid); + static void ReleaseCachedFonts(int pid); #endif // defined(OS_WIN) // IPC::Message::Sender implementation. @@ -156,6 +160,35 @@ class CONTENT_EXPORT ChildProcessHost : public IPC::Channel::Listener, ListenerHook listener_; +#if defined (OS_WIN) + class FontCache { + public: + static FontCache* GetInstance(); + void PreCacheFont(LOGFONT font, int process_id); + void ReleaseCachedFonts(int process_id); + + private: + struct CacheElement { + CacheElement(); + ~CacheElement(); + + HFONT font_; + HDC dc_; + int ref_count_; + }; + friend struct DefaultSingletonTraits<FontCache>; + + FontCache(); + ~FontCache(); + + std::map<string16, CacheElement> cache_; + std::map<int, std::vector<string16> > process_id_font_map_; + base::Lock mutex_; + + DISALLOW_COPY_AND_ASSIGN(FontCache); + }; +#endif + bool opening_channel_; // True while we're waiting the channel to be opened. scoped_ptr<IPC::Channel> channel_; std::string channel_id_; diff --git a/content/common/child_process_messages.h b/content/common/child_process_messages.h index a503423..32ff20b 100644 --- a/content/common/child_process_messages.h +++ b/content/common/child_process_messages.h @@ -59,3 +59,13 @@ IPC_MESSAGE_CONTROL1(ChildProcessHostMsg_TraceDataCollected, // Reply to ChildProcessMsg_GetTraceBufferPercentFull. IPC_MESSAGE_CONTROL1(ChildProcessHostMsg_TraceBufferPercentFullReply, float /*trace buffer percent full*/) + +#if defined(OS_WIN) +// Request that the given font be loaded by the host so it's cached by the +// OS. Please see ChildProcessHost::PreCacheFont for details. +IPC_SYNC_MESSAGE_CONTROL1_0(ChildProcessHostMsg_PreCacheFont, + LOGFONT /* font data */) + +// Release the cached font +IPC_MESSAGE_CONTROL0(ChildProcessHostMsg_ReleaseCachedFonts) +#endif // defined(OS_WIN) diff --git a/content/common/view_messages.h b/content/common/view_messages.h index 05e72c6..695ffc2 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -1784,13 +1784,6 @@ IPC_SYNC_MESSAGE_CONTROL1_3(ViewHostMsg_LoadFont, uint32 /* font id */) #endif -#if defined(OS_WIN) -// Request that the given font be loaded by the browser so it's cached by the -// OS. Please see ChildProcessHost::PreCacheFont for details. -IPC_SYNC_MESSAGE_CONTROL1_0(ViewHostMsg_PreCacheFont, - LOGFONT /* font data */) -#endif // defined(OS_WIN) - // Returns WebScreenInfo corresponding to the view. // TODO(shess): Provide a mapping from reply_msg->routing_id() to // HWND so that we can eliminate the NativeViewId parameter. diff --git a/content/renderer/render_thread.cc b/content/renderer/render_thread.cc index 3e11f98..d50ec17 100644 --- a/content/renderer/render_thread.cc +++ b/content/renderer/render_thread.cc @@ -71,6 +71,7 @@ // TODO(port) #if defined(OS_WIN) +#include "content/common/child_process_messages.h" #include "content/plugin/plugin_channel.h" #else #include "base/memory/scoped_handle.h" @@ -519,7 +520,14 @@ void RenderThread::RecordUserMetrics(const std::string& action) { #if defined(OS_WIN) // static bool RenderThread::PreCacheFont(const LOGFONT& log_font) { - return RenderThread::current()->Send(new ViewHostMsg_PreCacheFont(log_font)); + return RenderThread::current()->Send( + new ChildProcessHostMsg_PreCacheFont(log_font)); +} + +// static +bool RenderThread::ReleaseCachedFonts() { + return RenderThread::current()->Send( + new ChildProcessHostMsg_ReleaseCachedFonts()); } #endif // OS_WIN diff --git a/content/renderer/render_thread.h b/content/renderer/render_thread.h index 9c5a471..8fb9e03 100644 --- a/content/renderer/render_thread.h +++ b/content/renderer/render_thread.h @@ -227,6 +227,9 @@ class CONTENT_EXPORT RenderThread : public RenderThreadBase, // Request that the given font be loaded by the browser so it's cached by the // OS. Please see ChildProcessHost::PreCacheFont for details. static bool PreCacheFont(const LOGFONT& log_font); + + // Release cached font. + static bool ReleaseCachedFonts(); #endif // OS_WIN private: diff --git a/content/renderer/renderer_webkitplatformsupport_impl.cc b/content/renderer/renderer_webkitplatformsupport_impl.cc index 6d459e9..2ca0e7c 100644 --- a/content/renderer/renderer_webkitplatformsupport_impl.cc +++ b/content/renderer/renderer_webkitplatformsupport_impl.cc @@ -47,6 +47,7 @@ #include "webkit/gpu/webgraphicscontext3d_in_process_impl.h" #if defined(OS_WIN) +#include "content/common/child_process_messages.h" #include "third_party/WebKit/Source/WebKit/chromium/public/win/WebSandboxSupport.h" #endif @@ -444,7 +445,8 @@ bool RendererWebKitPlatformSupportImpl::SandboxSupport::ensureFontLoaded( HFONT font) { LOGFONT logfont; GetObject(font, sizeof(LOGFONT), &logfont); - return RenderThread::current()->Send(new ViewHostMsg_PreCacheFont(logfont)); + return RenderThread::current()->Send( + new ChildProcessHostMsg_PreCacheFont(logfont)); } #elif defined(OS_MACOSX) |