Home | History | Annotate | Download | only in win
      1 /*
      2  * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "config.h"
     30 #include "FontDatabase.h"
     31 
     32 #include "CString.h"
     33 #include "FileSystem.h"
     34 #include "PlatformString.h"
     35 #include <WebKitSystemInterface/WebKitSystemInterface.h>
     36 #include <shlobj.h>
     37 #include <wtf/RetainPtr.h>
     38 
     39 namespace WebCore {
     40 
     41 static String systemFontsDirectory()
     42 {
     43     static bool initialized;
     44     static String directory;
     45 
     46     if (!initialized) {
     47         initialized = true;
     48 
     49         Vector<UChar> buffer(MAX_PATH);
     50         if (FAILED(SHGetFolderPath(0, CSIDL_FONTS | CSIDL_FLAG_CREATE, 0, 0, buffer.data())))
     51             return directory;
     52         buffer.resize(wcslen(buffer.data()));
     53 
     54         directory = String::adopt(buffer);
     55     }
     56 
     57     return directory;
     58 }
     59 
     60 static String fontsPlistPath()
     61 {
     62     static String path = pathByAppendingComponent(localUserSpecificStorageDirectory(), "FontsList.plist");
     63     return path;
     64 }
     65 
     66 static bool systemHasFontsNewerThanFontsPlist()
     67 {
     68     WIN32_FILE_ATTRIBUTE_DATA plistAttributes = {0};
     69     if (!GetFileAttributesEx(fontsPlistPath().charactersWithNullTermination(), GetFileExInfoStandard, &plistAttributes))
     70         return true;
     71 
     72     WIN32_FILE_ATTRIBUTE_DATA fontsDirectoryAttributes = {0};
     73     if (!GetFileAttributesEx(systemFontsDirectory().charactersWithNullTermination(), GetFileExInfoStandard, &fontsDirectoryAttributes))
     74         return true;
     75 
     76     return CompareFileTime(&plistAttributes.ftLastWriteTime, &fontsDirectoryAttributes.ftLastWriteTime) < 0;
     77 }
     78 
     79 static RetainPtr<CFPropertyListRef> readFontPlist()
     80 {
     81     CString plistPath = fontsPlistPath().utf8();
     82 
     83     RetainPtr<CFURLRef> url(AdoptCF, CFURLCreateFromFileSystemRepresentation(0, reinterpret_cast<const UInt8*>(plistPath.data()), plistPath.length(), false));
     84     if (!url)
     85         return 0;
     86 
     87     RetainPtr<CFReadStreamRef> stream(AdoptCF, CFReadStreamCreateWithFile(0, url.get()));
     88     if (!stream)
     89         return 0;
     90 
     91     if (!CFReadStreamOpen(stream.get()))
     92         return 0;
     93 
     94     CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0 | kCFPropertyListXMLFormat_v1_0;
     95     RetainPtr<CFPropertyListRef> plist(AdoptCF, CFPropertyListCreateFromStream(0, stream.get(), 0, kCFPropertyListMutableContainersAndLeaves, &format, 0));
     96 
     97     CFReadStreamClose(stream.get());
     98 
     99     return plist;
    100 }
    101 
    102 static bool populateFontDatabaseFromPlist(CFPropertyListRef plist)
    103 {
    104     if (!plist)
    105         return false;
    106 
    107     wkAddFontsFromPlist(plist);
    108     return true;
    109 }
    110 
    111 static bool populateFontDatabaseFromFileSystem()
    112 {
    113     RetainPtr<CFStringRef> directory(AdoptCF, systemFontsDirectory().createCFString());
    114     if (!directory)
    115         return false;
    116 
    117     wkAddFontsInDirectory(directory.get());
    118     return true;
    119 }
    120 
    121 static CFStringRef fontFilenamesFromRegistryKey()
    122 {
    123     static CFStringRef key = CFSTR("WebKitFontFilenamesFromRegistry");
    124     return key;
    125 }
    126 
    127 static void writeFontDatabaseToPlist(CFPropertyListRef cgFontDBPropertyList, CFPropertyListRef filenamesFromRegistry)
    128 {
    129     if (!cgFontDBPropertyList)
    130         return;
    131 
    132     RetainPtr<CFDataRef> data;
    133 
    134     if (!filenamesFromRegistry || CFGetTypeID(cgFontDBPropertyList) != CFDictionaryGetTypeID())
    135         data.adoptCF(CFPropertyListCreateXMLData(kCFAllocatorDefault, cgFontDBPropertyList));
    136     else {
    137         RetainPtr<CFMutableDictionaryRef> dictionary(AdoptCF, CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 2, static_cast<CFDictionaryRef>(cgFontDBPropertyList)));
    138         CFDictionarySetValue(dictionary.get(), fontFilenamesFromRegistryKey(), filenamesFromRegistry);
    139         data.adoptCF(CFPropertyListCreateXMLData(kCFAllocatorDefault, dictionary.get()));
    140     }
    141 
    142     if (!data)
    143         return;
    144 
    145     safeCreateFile(fontsPlistPath(), data.get());
    146 }
    147 
    148 static RetainPtr<CFArrayRef> fontFilenamesFromRegistry()
    149 {
    150     RetainPtr<CFMutableArrayRef> filenames(AdoptCF, CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
    151 
    152     HKEY key;
    153     if (FAILED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"), 0, KEY_READ, &key)))
    154         return filenames;
    155 
    156     DWORD valueCount;
    157     DWORD maxNameLength;
    158     DWORD maxValueLength;
    159     if (FAILED(RegQueryInfoKey(key, 0, 0, 0, 0, 0, 0, &valueCount, &maxNameLength, &maxValueLength, 0, 0))) {
    160         RegCloseKey(key);
    161         return filenames;
    162     }
    163 
    164     Vector<TCHAR> name(maxNameLength + 1);
    165     Vector<BYTE> value(maxValueLength + 1);
    166 
    167     for (size_t i = 0; i < valueCount; ++i) {
    168         DWORD nameLength = name.size();
    169         DWORD valueLength = value.size();
    170         DWORD type;
    171         if (FAILED(RegEnumValue(key, i, name.data(), &nameLength, 0, &type, value.data(), &valueLength)))
    172             continue;
    173         if (type != REG_SZ)
    174             continue;
    175 
    176         RetainPtr<CFDataRef> filename(AdoptCF, CFDataCreate(kCFAllocatorDefault, value.data(), valueLength));
    177         CFArrayAppendValue(filenames.get(), filename.get());
    178     }
    179 
    180     RegCloseKey(key);
    181     return filenames;
    182 }
    183 
    184 void populateFontDatabase()
    185 {
    186     static bool initialized;
    187     if (initialized)
    188         return;
    189     initialized = true;
    190 
    191     if (wkCanCreateCGFontWithLOGFONT())
    192         return;
    193 
    194     RetainPtr<CFPropertyListRef> propertyList = readFontPlist();
    195     RetainPtr<CFArrayRef> lastFilenamesFromRegistry;
    196     if (propertyList && CFGetTypeID(propertyList.get()) == CFDictionaryGetTypeID()) {
    197         CFDictionaryRef dictionary = static_cast<CFDictionaryRef>(propertyList.get());
    198         CFArrayRef array = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, fontFilenamesFromRegistryKey()));
    199         if (array && CFGetTypeID(array) == CFArrayGetTypeID())
    200             lastFilenamesFromRegistry = array;
    201     }
    202     RetainPtr<CFArrayRef> currentFilenamesFromRegistry = fontFilenamesFromRegistry();
    203     bool registryChanged = !lastFilenamesFromRegistry || !CFEqual(lastFilenamesFromRegistry.get(), currentFilenamesFromRegistry.get());
    204 
    205     if (!registryChanged && !systemHasFontsNewerThanFontsPlist()) {
    206         if (populateFontDatabaseFromPlist(propertyList.get()))
    207             return;
    208     }
    209 
    210     if (populateFontDatabaseFromFileSystem()) {
    211         wkAddFontsFromRegistry();
    212         RetainPtr<CFPropertyListRef> cgFontDBPropertyList(AdoptCF, wkCreateFontsPlist());
    213         writeFontDatabaseToPlist(cgFontDBPropertyList.get(), currentFilenamesFromRegistry.get());
    214     }
    215 }
    216 
    217 } // namespace WebCore
    218