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 "platform/fonts/mac/MemoryActivatedFont.h" 31 32 #import <AppKit/NSFont.h> 33 #import "platform/LinkHash.h" 34 #import "platform/fonts/FontPlatformData.h" 35 #import "public/platform/mac/WebSandboxSupport.h" 36 #import "public/platform/Platform.h" 37 #import "wtf/HashMap.h" 38 39 namespace blink { 40 41 namespace { 42 43 typedef HashMap<uint32, MemoryActivatedFont*> FontContainerRefMemoryFontHash; 44 typedef HashMap<WTF::String, MemoryActivatedFont*> FontNameMemoryFontHash; 45 46 // Caching: 47 // 48 // Requesting a font from the browser process is expensive and so is 49 // "activating" said font. Caching of loaded fonts is complicated by the fact 50 // that it's impossible to get a unique identifier for the on-disk font file 51 // from inside the sandboxed renderer process. 52 // This means that when loading a font we need to round-trip through the browser 53 // process in order to get the unique font file identifer which we might already 54 // have activated and cached. 55 // 56 // In order to save as much work as we can, we maintain 2 levels of caching 57 // for the font data: 58 // 1. A dumb cache keyed by the font name/style (information we can determine 59 // from inside the sandbox). 60 // 2. A smarter cache keyed by the real "unique font id". 61 // 62 // In order to perform a lookup in #2 we need to consult with the browser to get 63 // us the lookup key. While this doesn't save us the font load, it does save 64 // us font activation. 65 // 66 // It's important to remember that existing FontPlatformData objects are already 67 // cached, so a cache miss in the code in this file isn't necessarily so bad. 68 69 FontContainerRefMemoryFontHash& fontCacheByFontID() 70 { 71 DEFINE_STATIC_LOCAL(FontContainerRefMemoryFontHash, srcFontIDCache, ()); 72 return srcFontIDCache; 73 } 74 75 76 FontNameMemoryFontHash& fontCacheByFontName() 77 { 78 DEFINE_STATIC_LOCAL(FontNameMemoryFontHash, srcFontNameCache, ()); 79 return srcFontNameCache; 80 } 81 82 // Given a font specified by |srcFont|, use the information we can query in 83 // the sandbox to construct a key which we hope will be as unique as possible 84 // to the containing font file. 85 WTF::String hashKeyFromNSFont(NSFont* srcFont) 86 { 87 NSFontDescriptor* desc = [srcFont fontDescriptor]; 88 NSFontSymbolicTraits traits = [desc symbolicTraits]; 89 return WTF::String::format("%s %x", [[srcFont fontName] UTF8String], traits); 90 } 91 92 // The only way we can tell that an in-process font has failed to load 93 // is if CTFontCopyGraphicsFont() returns the LastResort font. 94 bool isLastResortFont(CGFontRef cgFont) 95 { 96 NSString* fontName = (NSString*)CGFontCopyPostScriptName(cgFont); 97 return [fontName isEqualToString:@"LastResort"]; 98 } 99 100 // Given an in-process font which has failed to load, return a 101 // MemoryActivatedFont* corresponding to an in-memory representation of the 102 // same font loaded from the browser process. 103 // On failure this function returns a PassRefPtr pointing to 0. 104 PassRefPtr<MemoryActivatedFont> loadFontFromBrowserProcess(NSFont* nsFont) 105 { 106 // First try to lookup in our cache with the limited information we have. 107 WTF::String hashKey = hashKeyFromNSFont(nsFont); 108 RefPtr<MemoryActivatedFont> font(fontCacheByFontName().get(hashKey)); 109 if (font) 110 return font; 111 112 CGFontRef tmpCGFont; 113 uint32_t fontID; 114 // Send cross-process request to load font. 115 blink::WebSandboxSupport* sandboxSupport = blink::Platform::current()->sandboxSupport(); 116 if (!sandboxSupport) { 117 // This function should only be called in response to an error loading a 118 // font due to being blocked by the sandbox. 119 // This by definition shouldn't happen if there is no sandbox support. 120 ASSERT_NOT_REACHED(); 121 return nullptr; 122 } 123 if (!sandboxSupport->loadFont(nsFont, &tmpCGFont, &fontID)) 124 return nullptr; 125 126 RetainPtr<CGFontRef> cgFont(tmpCGFont); 127 // Now that we have the fontID from the browser process, we can consult 128 // the ID cache. 129 font = fontCacheByFontID().get(fontID); 130 if (font) 131 // FIXME: WebSandboxSupport::loadFont() should consult the id cache 132 // before activating the font. 133 return font; 134 135 return MemoryActivatedFont::create(fontID, nsFont, cgFont.get()); 136 } 137 138 } // namespace 139 140 PassRefPtr<MemoryActivatedFont> MemoryActivatedFont::create(uint32_t fontID, NSFont* nsFont, CGFontRef cgFont) 141 { 142 return adoptRef(new MemoryActivatedFont(fontID, nsFont, cgFont)); 143 } 144 145 MemoryActivatedFont::MemoryActivatedFont(uint32_t fontID, NSFont* nsFont, CGFontRef cgFont) 146 : m_cgFont(cgFont) 147 , m_fontID(fontID) 148 , m_inSandboxHashKey(hashKeyFromNSFont(nsFont)) 149 { 150 // Add ourselves to caches. 151 fontCacheByFontID().add(fontID, this); 152 fontCacheByFontName().add(m_inSandboxHashKey, this); 153 } 154 155 // Destructor - Unload font container from memory and remove ourselves 156 // from cache. 157 MemoryActivatedFont::~MemoryActivatedFont() 158 { 159 // First remove ourselves from the caches. 160 ASSERT(fontCacheByFontID().contains(m_fontID)); 161 ASSERT(fontCacheByFontName().contains(m_inSandboxHashKey)); 162 163 fontCacheByFontID().remove(m_fontID); 164 fontCacheByFontName().remove(m_inSandboxHashKey); 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 or null if loading failed. 179 // * cgFont - on output this contains the CGFontRef corresponding to the NSFont 180 // that was picked in the end. The caller is responsible for calling 181 // CFRelease() on this parameter when done with it. 182 // * fontID - on output, the ID corresponding to nsFont. 183 void FontPlatformData::loadFont(NSFont* nsFont, float fontSize, NSFont*& outNSFont, CGFontRef& cgFont) 184 { 185 outNSFont = nsFont; 186 cgFont = CTFontCopyGraphicsFont(toCTFontRef(outNSFont), 0); 187 if (outNSFont && cgFont && isLastResortFont(cgFont)) { 188 // Release old CGFontRef since it points at the LastResort font which we don't want. 189 CFRelease(cgFont); 190 cgFont = 0; 191 192 // Font loading was blocked by the Sandbox. 193 m_inMemoryFont = loadFontFromBrowserProcess(outNSFont); 194 if (m_inMemoryFont) { 195 cgFont = m_inMemoryFont->cgFont(); 196 197 // Need to add an extra retain so output semantics of this function 198 // are consistent. 199 CFRetain(cgFont); 200 } else { 201 // If we still can't load the font, set |outNSFont| to null so that FontPlatformData won't be used. 202 outNSFont = 0; 203 } 204 } 205 } 206 207 } // namespace blink 208