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 "PlatformBridge.h" 34#import <AppKit/NSFont.h> 35#import <wtf/HashMap.h> 36 37namespace WebCore { 38 39namespace { 40 41typedef HashMap<ATSFontContainerRef, MemoryActivatedFont*> FontContainerRefMemoryFontHash; 42 43// 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// On system versions >=10.6 cross-process font loading is required. 46bool OutOfProcessFontLoadingEnabled() 47{ 48 static SInt32 systemVersion = 0; 49 if (!systemVersion) { 50 if (Gestalt(gestaltSystemVersion, &systemVersion) != noErr) 51 return false; 52 } 53 54 return systemVersion >= 0x1060; 55} 56 57FontContainerRefMemoryFontHash& fontCacheBySrcFontContainerRef() 58{ 59 DEFINE_STATIC_LOCAL(FontContainerRefMemoryFontHash, srcFontRefCache, ()); 60 return srcFontRefCache; 61} 62 63ATSFontContainerRef fontContainerRefFromNSFont(NSFont* srcFont) 64{ 65 ATSFontRef fontRef = CTFontGetPlatformFont(toCTFontRef(srcFont), 0); 66 if (!fontRef) 67 return kATSFontContainerRefUnspecified; 68 ATSFontContainerRef fontContainer = kATSFontContainerRefUnspecified; 69 if (ATSFontGetContainer(fontRef, 0, &fontContainer) != noErr) 70 return kATSFontContainerRefUnspecified; 71 return fontContainer; 72} 73 74// The only way we can tell that an in-process font has failed to load 75// is if CTFontCopyGraphicsFont() returns the LastResort font. 76bool isLastResortFont(CGFontRef cgFont) 77{ 78 NSString* fontName = (NSString*)CGFontCopyPostScriptName(cgFont); 79 return [fontName isEqualToString:@"LastResort"]; 80} 81 82// Given an in-process font which has failed to load, return a 83// MemoryActivatedFont* corresponding to an in-memory representation of the 84// same font loaded from the browser process. 85// On failure this function returns a PassRefPtr pointing to 0. 86PassRefPtr<MemoryActivatedFont> loadFontFromBrowserProcess(NSFont* nsFont) 87{ 88 ATSFontContainerRef container; 89 // Send cross-process request to load font. 90 if (!PlatformBridge::loadFont(nsFont, &container)) 91 return 0; 92 93 ATSFontContainerRef srcFontContainerRef = fontContainerRefFromNSFont(nsFont); 94 if (!srcFontContainerRef) { 95 ATSFontDeactivate(container, 0, kATSOptionFlagsDefault); 96 return 0; 97 } 98 99 PassRefPtr<MemoryActivatedFont> font = adoptRef(fontCacheBySrcFontContainerRef().get(srcFontContainerRef)); 100 if (font.get()) 101 return font; 102 103 return MemoryActivatedFont::create(srcFontContainerRef, container); 104} 105 106} // namespace 107 108PassRefPtr<MemoryActivatedFont> MemoryActivatedFont::create(ATSFontContainerRef srcFontContainerRef, ATSFontContainerRef container) 109{ 110 MemoryActivatedFont* font = new MemoryActivatedFont(srcFontContainerRef, container); 111 if (!font->cgFont()) // Object construction failed. 112 { 113 delete font; 114 return 0; 115 } 116 return adoptRef(font); 117} 118 119MemoryActivatedFont::MemoryActivatedFont(ATSFontContainerRef srcFontContainerRef, ATSFontContainerRef container) 120 : m_fontContainer(container) 121 , m_atsFontRef(kATSFontRefUnspecified) 122 , m_srcFontContainerRef(srcFontContainerRef) 123{ 124 if (!container) 125 return; 126 127 // Count the number of fonts in the container. 128 ItemCount fontCount = 0; 129 OSStatus err = ATSFontFindFromContainer(container, kATSOptionFlagsDefault, 0, 0, &fontCount); 130 if (err != noErr || fontCount < 1) 131 return; 132 133 // For now always assume that we want the first font in the container. 134 ATSFontFindFromContainer(container, kATSOptionFlagsDefault, 1, &m_atsFontRef, 0); 135 136 if (!m_atsFontRef) 137 return; 138 139 // Cache CGFont representation of the font. 140 m_cgFont.adoptCF(CGFontCreateWithPlatformFont(&m_atsFontRef)); 141 142 if (!m_cgFont.get()) 143 return; 144 145 // Add ourselves to cache. 146 fontCacheBySrcFontContainerRef().add(m_srcFontContainerRef, this); 147} 148 149// Destructor - Unload font container from memory and remove ourselves 150// from cache. 151MemoryActivatedFont::~MemoryActivatedFont() 152{ 153 if (m_cgFont.get()) { 154 // First remove ourselves from the caches. 155 ASSERT(fontCacheBySrcFontContainerRef().contains(m_srcFontContainerRef)); 156 157 fontCacheBySrcFontContainerRef().remove(m_srcFontContainerRef); 158 159 // Make sure the CGFont is destroyed before its font container. 160 m_cgFont.releaseRef(); 161 } 162 163 if (m_fontContainer != kATSFontContainerRefUnspecified) 164 ATSFontDeactivate(m_fontContainer, 0, kATSOptionFlagsDefault); 165} 166 167// Given an NSFont, try to load a representation of that font into the cgFont 168// parameter. If loading is blocked by the sandbox, the font may be loaded 169// cross-process. 170// If sandbox loading also fails, a fallback font is loaded. 171// 172// Considerations: 173// * cgFont must be CFRelease()ed by the caller when done. 174// 175// Parameters: 176// * nsFont - The font we wish to load. 177// * fontSize - point size of the font we wish to load. 178// * outNSFont - The font that was actually loaded, may be different from nsFont 179// if a fallback font was used. 180// * cgFont - on output this contains the CGFontRef corresponding to the NSFont 181// that was picked in the end. The caller is responsible for calling 182// CFRelease() on this parameter when done with it. 183// * fontID - on output, the ID corresponding to nsFont. 184void FontPlatformData::loadFont(NSFont* nsFont, float fontSize, NSFont*& outNSFont, CGFontRef& cgFont) 185{ 186 outNSFont = nsFont; 187 cgFont = CTFontCopyGraphicsFont(toCTFontRef(outNSFont), 0); 188 if (OutOfProcessFontLoadingEnabled() && outNSFont && cgFont && isLastResortFont(cgFont)) { 189 // Release old CGFontRef since it points at the LastResort font which we don't want. 190 CFRelease(cgFont); 191 cgFont = 0; 192 193 // Font loading was blocked by the Sandbox. 194 m_inMemoryFont = loadFontFromBrowserProcess(outNSFont); 195 if (m_inMemoryFont.get()) { 196 cgFont = m_inMemoryFont->cgFont(); 197 198 // Need to add an extra retain so output semantics of this function 199 // are consistent. 200 CFRetain(cgFont); 201 } else { 202 // If we still can't load the font, then return Times, 203 // rather than the LastResort font. 204 outNSFont = [NSFont fontWithName:@"Times" size:fontSize]; 205 cgFont = CTFontCopyGraphicsFont(toCTFontRef(outNSFont), 0); 206 } 207 } 208} 209 210} // namespace WebCore 211