initial import
[vuplus_webkit] / Source / WebCore / platform / graphics / chromium / CrossProcessFontLoading.mm
1 /*
2  * This file is part of the internal font implementation.
3  *
4  * Copyright (c) 2010 Google Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22
23 // This file provides additional functionality to the Mac FontPlatformData class
24 // defined in WebCore/platform/cocoa/FontPlatformDataCocoa.mm .
25 // Because we want to support loading fonts between processes in the face of
26 // font loading being blocked by the sandbox, we need a mechnasim to both
27 // do the loading of in-memory fonts and keep track of them.
28
29 #import "config.h"
30 #import "CrossProcessFontLoading.h"
31
32 #import "../graphics/FontPlatformData.h"
33 #import "PlatformSupport.h"
34 #import <AppKit/NSFont.h>
35 #import <wtf/HashMap.h>
36
37 namespace WebCore {
38
39 namespace {
40
41 typedef HashMap<uint32, MemoryActivatedFont*> FontContainerRefMemoryFontHash;
42 typedef HashMap<WTF::String, MemoryActivatedFont*> FontNameMemoryFontHash;
43
44 // On 10.5, font loading is not blocked by the sandbox and thus there is no
45 // need for the cross-process font loading mechanim.
46 // On system versions >=10.6 cross-process font loading is required.
47 bool OutOfProcessFontLoadingEnabled()
48 {
49     static SInt32 systemVersion = 0;
50     if (!systemVersion) {
51         if (Gestalt(gestaltSystemVersion, &systemVersion) != noErr)
52             return false;
53     }
54
55     return systemVersion >= 0x1060;
56 }
57
58 // Caching:
59 //
60 // Requesting a font from the browser process is expensive and so is
61 // "activating" said font.  Caching of loaded fonts is complicated by the fact
62 // that it's impossible to get a unique identifier for the on-disk font file
63 // from inside the sandboxed renderer process.
64 // This means that when loading a font we need to round-trip through the browser
65 // process in order to get the unique font file identifer which we might already
66 // have activated and cached.
67 //
68 // In order to save as much work as we can, we maintain 2 levels of caching
69 // for the font data:
70 // 1. A dumb cache keyed by the font name/style (information we can determine
71 // from inside the sandbox).
72 // 2. A smarter cache keyed by the real "unique font id".
73 //
74 // In order to perform a lookup in #2 we need to consult with the browser to get
75 // us the lookup key.  While this doesn't save us the font load, it does save
76 // us font activation.
77 //
78 // It's important to remember that existing FontPlatformData objects are already
79 // cached, so a cache miss in the code in this file isn't necessarily so bad.
80
81 FontContainerRefMemoryFontHash& fontCacheByFontID()
82 {
83     DEFINE_STATIC_LOCAL(FontContainerRefMemoryFontHash, srcFontIDCache, ());
84     return srcFontIDCache;
85 }
86
87
88 FontNameMemoryFontHash& fontCacheByFontName()
89 {
90     DEFINE_STATIC_LOCAL(FontNameMemoryFontHash, srcFontNameCache, ());
91     return srcFontNameCache;
92 }
93
94 // Given a font specified by |srcFont|, use the information we can query in
95 // the sandbox to construct a key which we hope will be as unique as possible
96 // to the containing font file.
97 WTF::String hashKeyFromNSFont(NSFont* srcFont)
98 {
99     NSFontDescriptor* desc = [srcFont fontDescriptor];
100     NSFontSymbolicTraits traits = [desc symbolicTraits];
101     return WTF::String::format("%s %x", [[srcFont fontName] UTF8String], traits);
102 }
103
104 ATSFontContainerRef fontContainerRefFromNSFont(NSFont* srcFont)
105 {
106     ATSFontRef fontRef = CTFontGetPlatformFont(toCTFontRef(srcFont), 0);
107     if (!fontRef)
108         return kATSFontContainerRefUnspecified;
109     ATSFontContainerRef fontContainer = kATSFontContainerRefUnspecified;
110     if (ATSFontGetContainer(fontRef, 0, &fontContainer) != noErr)
111         return kATSFontContainerRefUnspecified;
112     return fontContainer;
113 }
114
115 // The only way we can tell that an in-process font has failed to load
116 // is if CTFontCopyGraphicsFont() returns the LastResort font.
117 bool isLastResortFont(CGFontRef cgFont)
118 {
119     NSString* fontName = (NSString*)CGFontCopyPostScriptName(cgFont);
120     return [fontName isEqualToString:@"LastResort"];
121 }
122
123 // Given an in-process font which has failed to load, return a
124 // MemoryActivatedFont* corresponding to an in-memory representation of the
125 // same font loaded from the browser process.
126 // On failure this function returns a PassRefPtr pointing to 0.
127 PassRefPtr<MemoryActivatedFont> loadFontFromBrowserProcess(NSFont* nsFont)
128 {
129     // First try to lookup in our cache with the limited information we have.
130     WTF::String hashKey = hashKeyFromNSFont(nsFont);
131     RefPtr<MemoryActivatedFont> font(fontCacheByFontName().get(hashKey));
132     if (font)
133         return font;
134
135     CGFontRef tmpCGFont;
136     uint32_t fontID;
137     // Send cross-process request to load font.
138     if (!PlatformSupport::loadFont(nsFont, &tmpCGFont, &fontID))
139         return 0;
140
141     RetainPtr<CGFontRef> cgFont(tmpCGFont);
142     // Now that we have the fontID from the browser process, we can consult
143     // the ID cache.
144     font = fontCacheByFontID().get(fontID);
145     if (font)
146         // FIXME: PlatformSupport::loadFont() should consult the id cache
147         // before activating the font.
148         return font;
149
150     return MemoryActivatedFont::create(fontID, nsFont, cgFont.get());
151 }
152
153 } // namespace
154
155 PassRefPtr<MemoryActivatedFont> MemoryActivatedFont::create(uint32_t fontID, NSFont* nsFont, CGFontRef cgFont)
156 {
157   return adoptRef(new MemoryActivatedFont(fontID, nsFont, cgFont));
158 }
159
160 MemoryActivatedFont::MemoryActivatedFont(uint32_t fontID, NSFont* nsFont, CGFontRef cgFont)
161     : m_cgFont(cgFont)
162     , m_fontID(fontID)
163     , m_inSandboxHashKey(hashKeyFromNSFont(nsFont))
164 {
165     // Add ourselves to caches.
166     fontCacheByFontID().add(fontID, this);
167     fontCacheByFontName().add(m_inSandboxHashKey, this);
168 }
169
170 // Destructor - Unload font container from memory and remove ourselves
171 // from cache.
172 MemoryActivatedFont::~MemoryActivatedFont()
173 {
174     // First remove ourselves from the caches.
175     ASSERT(fontCacheByFontID().contains(m_fontID));
176     ASSERT(fontCacheByFontName().contains(m_inSandboxHashKey));
177
178     fontCacheByFontID().remove(m_fontID);
179     fontCacheByFontName().remove(m_inSandboxHashKey);
180 }
181
182 // Given an NSFont, try to load a representation of that font into the cgFont
183 // parameter.  If loading is blocked by the sandbox, the font may be loaded
184 // cross-process.
185 // If sandbox loading also fails, a fallback font is loaded.
186 //
187 // Considerations:
188 // * cgFont must be CFRelease()ed by the caller when done.
189 // 
190 // Parameters:
191 // * nsFont - The font we wish to load.
192 // * fontSize - point size of the font we wish to load.
193 // * outNSFont - The font that was actually loaded, may be different from nsFont
194 //   if a fallback font was used.
195 // * cgFont - on output this contains the CGFontRef corresponding to the NSFont
196 //   that was picked in the end.  The caller is responsible for calling
197 //   CFRelease() on this parameter when done with it.
198 // * fontID - on output, the ID corresponding to nsFont.
199 void FontPlatformData::loadFont(NSFont* nsFont, float fontSize, NSFont*& outNSFont, CGFontRef& cgFont)
200 {
201     outNSFont = nsFont;
202     cgFont = CTFontCopyGraphicsFont(toCTFontRef(outNSFont), 0);
203     if (OutOfProcessFontLoadingEnabled() && outNSFont && cgFont && isLastResortFont(cgFont)) {
204         // Release old CGFontRef since it points at the LastResort font which we don't want.
205         CFRelease(cgFont);
206         cgFont = 0;
207         
208         // Font loading was blocked by the Sandbox.
209         m_inMemoryFont = loadFontFromBrowserProcess(outNSFont);
210         if (m_inMemoryFont) {
211             cgFont = m_inMemoryFont->cgFont();
212             
213             // Need to add an extra retain so output semantics of this function
214             // are consistent.
215             CFRetain(cgFont);
216         } else {
217             // If we still can't load the font, then return Times,
218             // rather than the LastResort font.
219             outNSFont = [NSFont fontWithName:@"Times" size:fontSize];
220             cgFont = CTFontCopyGraphicsFont(toCTFontRef(outNSFont), 0);
221         }
222     }
223 }
224
225 } // namespace WebCore