Home | History | Annotate | Download | only in mac
      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