OLD | NEW |
1 /* | 1 /* |
2 * This file is part of the internal font implementation. | 2 * This file is part of the internal font implementation. |
3 * | 3 * |
4 * Copyright (c) 2010 Google Inc. All rights reserved. | 4 * Copyright (c) 2010 Google Inc. All rights reserved. |
5 * | 5 * |
6 * This library is free software; you can redistribute it and/or | 6 * This library is free software; you can redistribute it and/or |
7 * modify it under the terms of the GNU Library General Public | 7 * modify it under the terms of the GNU Library General Public |
8 * License as published by the Free Software Foundation; either | 8 * License as published by the Free Software Foundation; either |
9 * version 2 of the License, or (at your option) any later version. | 9 * version 2 of the License, or (at your option) any later version. |
10 * | 10 * |
(...skipping 20 matching lines...) Expand all Loading... |
31 | 31 |
32 #import "../graphics/FontPlatformData.h" | 32 #import "../graphics/FontPlatformData.h" |
33 #import "PlatformBridge.h" | 33 #import "PlatformBridge.h" |
34 #import <AppKit/NSFont.h> | 34 #import <AppKit/NSFont.h> |
35 #import <wtf/HashMap.h> | 35 #import <wtf/HashMap.h> |
36 | 36 |
37 namespace WebCore { | 37 namespace WebCore { |
38 | 38 |
39 namespace { | 39 namespace { |
40 | 40 |
41 typedef HashMap<ATSFontContainerRef, MemoryActivatedFont*> FontContainerRefMemor
yFontHash; | 41 typedef HashMap<uint32, MemoryActivatedFont*> FontContainerRefMemoryFontHash; |
| 42 typedef HashMap<WTF::String, MemoryActivatedFont*> FontNameMemoryFontHash; |
42 | 43 |
43 // On 10.5, font loading is not blocked by the sandbox and thus there is no | 44 // On 10.5, font loading is not blocked by the sandbox and thus there is no |
44 // need for the cross-process font loading mechanim. | 45 // need for the cross-process font loading mechanim. |
45 // On system versions >=10.6 cross-process font loading is required. | 46 // On system versions >=10.6 cross-process font loading is required. |
46 bool OutOfProcessFontLoadingEnabled() | 47 bool OutOfProcessFontLoadingEnabled() |
47 { | 48 { |
48 static SInt32 systemVersion = 0; | 49 static SInt32 systemVersion = 0; |
49 if (!systemVersion) { | 50 if (!systemVersion) { |
50 if (Gestalt(gestaltSystemVersion, &systemVersion) != noErr) | 51 if (Gestalt(gestaltSystemVersion, &systemVersion) != noErr) |
51 return false; | 52 return false; |
52 } | 53 } |
53 | 54 |
54 return systemVersion >= 0x1060; | 55 return systemVersion >= 0x1060; |
55 } | 56 } |
56 | 57 |
57 FontContainerRefMemoryFontHash& fontCacheBySrcFontContainerRef() | 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() |
58 { | 82 { |
59 DEFINE_STATIC_LOCAL(FontContainerRefMemoryFontHash, srcFontRefCache, ()); | 83 DEFINE_STATIC_LOCAL(FontContainerRefMemoryFontHash, srcFontIDCache, ()); |
60 return srcFontRefCache; | 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)
; |
61 } | 102 } |
62 | 103 |
63 ATSFontContainerRef fontContainerRefFromNSFont(NSFont* srcFont) | 104 ATSFontContainerRef fontContainerRefFromNSFont(NSFont* srcFont) |
64 { | 105 { |
65 ATSFontRef fontRef = CTFontGetPlatformFont(toCTFontRef(srcFont), 0); | 106 ATSFontRef fontRef = CTFontGetPlatformFont(toCTFontRef(srcFont), 0); |
66 if (!fontRef) | 107 if (!fontRef) |
67 return kATSFontContainerRefUnspecified; | 108 return kATSFontContainerRefUnspecified; |
68 ATSFontContainerRef fontContainer = kATSFontContainerRefUnspecified; | 109 ATSFontContainerRef fontContainer = kATSFontContainerRefUnspecified; |
69 if (ATSFontGetContainer(fontRef, 0, &fontContainer) != noErr) | 110 if (ATSFontGetContainer(fontRef, 0, &fontContainer) != noErr) |
70 return kATSFontContainerRefUnspecified; | 111 return kATSFontContainerRefUnspecified; |
71 return fontContainer; | 112 return fontContainer; |
72 } | 113 } |
73 | 114 |
74 // The only way we can tell that an in-process font has failed to load | 115 // The only way we can tell that an in-process font has failed to load |
75 // is if CTFontCopyGraphicsFont() returns the LastResort font. | 116 // is if CTFontCopyGraphicsFont() returns the LastResort font. |
76 bool isLastResortFont(CGFontRef cgFont) | 117 bool isLastResortFont(CGFontRef cgFont) |
77 { | 118 { |
78 NSString* fontName = (NSString*)CGFontCopyPostScriptName(cgFont); | 119 NSString* fontName = (NSString*)CGFontCopyPostScriptName(cgFont); |
79 return [fontName isEqualToString:@"LastResort"]; | 120 return [fontName isEqualToString:@"LastResort"]; |
80 } | 121 } |
81 | 122 |
82 // Given an in-process font which has failed to load, return a | 123 // Given an in-process font which has failed to load, return a |
83 // MemoryActivatedFont* corresponding to an in-memory representation of the | 124 // MemoryActivatedFont* corresponding to an in-memory representation of the |
84 // same font loaded from the browser process. | 125 // same font loaded from the browser process. |
85 // On failure this function returns a PassRefPtr pointing to 0. | 126 // On failure this function returns a PassRefPtr pointing to 0. |
86 PassRefPtr<MemoryActivatedFont> loadFontFromBrowserProcess(NSFont* nsFont) | 127 PassRefPtr<MemoryActivatedFont> loadFontFromBrowserProcess(NSFont* nsFont) |
87 { | 128 { |
88 ATSFontContainerRef container; | 129 // First try to lookup in our cache with the limited information we have. |
89 // Send cross-process request to load font. | 130 WTF::String hashKey = hashKeyFromNSFont(nsFont); |
90 if (!PlatformBridge::loadFont(nsFont, &container)) | 131 RefPtr<MemoryActivatedFont> font(fontCacheByFontName().get(hashKey)); |
91 return 0; | 132 if (font) |
92 | |
93 ATSFontContainerRef srcFontContainerRef = fontContainerRefFromNSFont(nsFont)
; | |
94 if (!srcFontContainerRef) { | |
95 ATSFontDeactivate(container, 0, kATSOptionFlagsDefault); | |
96 return 0; | |
97 } | |
98 | |
99 PassRefPtr<MemoryActivatedFont> font = adoptRef(fontCacheBySrcFontContainerR
ef().get(srcFontContainerRef)); | |
100 if (font.get()) | |
101 return font; | 133 return font; |
102 | 134 |
103 return MemoryActivatedFont::create(srcFontContainerRef, container); | 135 ATSFontContainerRef container; |
| 136 uint32_t fontID; |
| 137 // Send cross-process request to load font. |
| 138 if (!PlatformBridge::loadFont(nsFont, &container, &fontID)) |
| 139 return 0; |
| 140 |
| 141 // Now that we have the fontID from the browser process, we can consult |
| 142 // the ID cache. |
| 143 font = fontCacheByFontID().get(fontID); |
| 144 if (font) { |
| 145 // We can safely discard the new container since we already have the |
| 146 // font in our cache. |
| 147 // FIXME: PlatformBridge::loadFont() should consult the id cache |
| 148 // before activating the font. Then we can save this activate/deactive |
| 149 // dance altogether. |
| 150 ATSFontDeactivate(container, 0, kATSOptionFlagsDefault); |
| 151 return font; |
| 152 } |
| 153 |
| 154 return MemoryActivatedFont::create(fontID, nsFont, container); |
104 } | 155 } |
105 | 156 |
106 } // namespace | 157 } // namespace |
107 | 158 |
108 PassRefPtr<MemoryActivatedFont> MemoryActivatedFont::create(ATSFontContainerRef
srcFontContainerRef, ATSFontContainerRef container) | 159 PassRefPtr<MemoryActivatedFont> MemoryActivatedFont::create(uint32_t fontID, NSF
ont* nsFont, ATSFontContainerRef container) |
109 { | 160 { |
110 MemoryActivatedFont* font = new MemoryActivatedFont(srcFontContainerRef, conta
iner); | 161 MemoryActivatedFont* font = new MemoryActivatedFont(fontID, nsFont, container)
; |
111 if (!font->cgFont()) // Object construction failed. | 162 if (!font->cgFont()) // Object construction failed. |
112 { | 163 { |
113 delete font; | 164 delete font; |
114 return 0; | 165 return 0; |
115 } | 166 } |
116 return adoptRef(font); | 167 return adoptRef(font); |
117 } | 168 } |
118 | 169 |
119 MemoryActivatedFont::MemoryActivatedFont(ATSFontContainerRef srcFontContainerRef
, ATSFontContainerRef container) | 170 MemoryActivatedFont::MemoryActivatedFont(uint32_t fontID, NSFont* nsFont, ATSFon
tContainerRef container) |
120 : m_fontContainer(container) | 171 : m_fontContainer(container) |
121 , m_atsFontRef(kATSFontRefUnspecified) | 172 , m_atsFontRef(kATSFontRefUnspecified) |
122 , m_srcFontContainerRef(srcFontContainerRef) | 173 , m_fontID(fontID) |
| 174 , m_inSandboxHashKey(hashKeyFromNSFont(nsFont)) |
123 { | 175 { |
124 if (!container) | 176 if (!container) |
125 return; | 177 return; |
126 | 178 |
127 // Count the number of fonts in the container. | 179 // Count the number of fonts in the container. |
128 ItemCount fontCount = 0; | 180 ItemCount fontCount = 0; |
129 OSStatus err = ATSFontFindFromContainer(container, kATSOptionFlagsDefault, 0
, 0, &fontCount); | 181 OSStatus err = ATSFontFindFromContainer(container, kATSOptionFlagsDefault, 0
, 0, &fontCount); |
130 if (err != noErr || fontCount < 1) | 182 if (err != noErr || fontCount < 1) |
131 return; | 183 return; |
132 | 184 |
133 // For now always assume that we want the first font in the container. | 185 // For now always assume that we want the first font in the container. |
134 ATSFontFindFromContainer(container, kATSOptionFlagsDefault, 1, &m_atsFontRef
, 0); | 186 ATSFontFindFromContainer(container, kATSOptionFlagsDefault, 1, &m_atsFontRef
, 0); |
135 | 187 |
136 if (!m_atsFontRef) | 188 if (!m_atsFontRef) |
137 return; | 189 return; |
138 | 190 |
139 // Cache CGFont representation of the font. | 191 // Cache CGFont representation of the font. |
140 m_cgFont.adoptCF(CGFontCreateWithPlatformFont(&m_atsFontRef)); | 192 m_cgFont.adoptCF(CGFontCreateWithPlatformFont(&m_atsFontRef)); |
141 | 193 |
142 if (!m_cgFont.get()) | 194 if (!m_cgFont) |
143 return; | 195 return; |
144 | 196 |
145 // Add ourselves to cache. | 197 // Add ourselves to caches. |
146 fontCacheBySrcFontContainerRef().add(m_srcFontContainerRef, this); | 198 fontCacheByFontID().add(fontID, this); |
| 199 fontCacheByFontName().add(m_inSandboxHashKey, this); |
147 } | 200 } |
148 | 201 |
149 // Destructor - Unload font container from memory and remove ourselves | 202 // Destructor - Unload font container from memory and remove ourselves |
150 // from cache. | 203 // from cache. |
151 MemoryActivatedFont::~MemoryActivatedFont() | 204 MemoryActivatedFont::~MemoryActivatedFont() |
152 { | 205 { |
153 if (m_cgFont.get()) { | 206 if (m_cgFont) { |
154 // First remove ourselves from the caches. | 207 // First remove ourselves from the caches. |
155 ASSERT(fontCacheBySrcFontContainerRef().contains(m_srcFontContainerRef))
; | 208 ASSERT(fontCacheByFontID().contains(m_fontID)); |
| 209 ASSERT(fontCacheByFontName().contains(m_inSandboxHashKey)); |
156 | 210 |
157 fontCacheBySrcFontContainerRef().remove(m_srcFontContainerRef); | 211 fontCacheByFontID().remove(m_fontID); |
| 212 fontCacheByFontName().remove(m_inSandboxHashKey); |
158 | 213 |
159 // Make sure the CGFont is destroyed before its font container. | 214 // Make sure the CGFont is destroyed before its font container. |
160 m_cgFont.releaseRef(); | 215 m_cgFont.releaseRef(); |
161 } | 216 } |
162 | 217 |
163 if (m_fontContainer != kATSFontContainerRefUnspecified) | 218 if (m_fontContainer != kATSFontContainerRefUnspecified) |
164 ATSFontDeactivate(m_fontContainer, 0, kATSOptionFlagsDefault); | 219 ATSFontDeactivate(m_fontContainer, 0, kATSOptionFlagsDefault); |
165 } | 220 } |
166 | 221 |
167 // Given an NSFont, try to load a representation of that font into the cgFont | 222 // Given an NSFont, try to load a representation of that font into the cgFont |
(...skipping 17 matching lines...) Expand all Loading... |
185 { | 240 { |
186 outNSFont = nsFont; | 241 outNSFont = nsFont; |
187 cgFont = CTFontCopyGraphicsFont(toCTFontRef(outNSFont), 0); | 242 cgFont = CTFontCopyGraphicsFont(toCTFontRef(outNSFont), 0); |
188 if (OutOfProcessFontLoadingEnabled() && outNSFont && cgFont && isLastResortF
ont(cgFont)) { | 243 if (OutOfProcessFontLoadingEnabled() && outNSFont && cgFont && isLastResortF
ont(cgFont)) { |
189 // Release old CGFontRef since it points at the LastResort font which we
don't want. | 244 // Release old CGFontRef since it points at the LastResort font which we
don't want. |
190 CFRelease(cgFont); | 245 CFRelease(cgFont); |
191 cgFont = 0; | 246 cgFont = 0; |
192 | 247 |
193 // Font loading was blocked by the Sandbox. | 248 // Font loading was blocked by the Sandbox. |
194 m_inMemoryFont = loadFontFromBrowserProcess(outNSFont); | 249 m_inMemoryFont = loadFontFromBrowserProcess(outNSFont); |
195 if (m_inMemoryFont.get()) { | 250 if (m_inMemoryFont) { |
196 cgFont = m_inMemoryFont->cgFont(); | 251 cgFont = m_inMemoryFont->cgFont(); |
197 | 252 |
198 // Need to add an extra retain so output semantics of this function | 253 // Need to add an extra retain so output semantics of this function |
199 // are consistent. | 254 // are consistent. |
200 CFRetain(cgFont); | 255 CFRetain(cgFont); |
201 } else { | 256 } else { |
202 // If we still can't load the font, then return Times, | 257 // If we still can't load the font, then return Times, |
203 // rather than the LastResort font. | 258 // rather than the LastResort font. |
204 outNSFont = [NSFont fontWithName:@"Times" size:fontSize]; | 259 outNSFont = [NSFont fontWithName:@"Times" size:fontSize]; |
205 cgFont = CTFontCopyGraphicsFont(toCTFontRef(outNSFont), 0); | 260 cgFont = CTFontCopyGraphicsFont(toCTFontRef(outNSFont), 0); |
206 } | 261 } |
207 } | 262 } |
208 } | 263 } |
209 | 264 |
210 } // namespace WebCore | 265 } // namespace WebCore |
OLD | NEW |