WebCore: WebCore part of
[WebKit-https.git] / WebCore / platform / graphics / win / FontDatabase.cpp
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 CFStringRef cgFontDBKey()
128 {
129     static CFStringRef key = CFSTR("WebKitCGFontDB");
130     return key;
131 }
132
133 static void writeFontDatabaseToPlist(CFPropertyListRef cgFontDBPropertyList, CFPropertyListRef filenamesFromRegistry)
134 {
135     RetainPtr<CFMutableDictionaryRef> dictionary(AdoptCF, CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
136
137     if (cgFontDBPropertyList)
138         CFDictionarySetValue(dictionary.get(), cgFontDBKey(), cgFontDBPropertyList);
139     if (filenamesFromRegistry)
140         CFDictionarySetValue(dictionary.get(), fontFilenamesFromRegistryKey(), filenamesFromRegistry);
141
142     RetainPtr<CFDataRef> data(AdoptCF, CFPropertyListCreateXMLData(kCFAllocatorDefault, dictionary.get()));
143     if (!data)
144         return;
145
146     safeCreateFile(fontsPlistPath(), data.get());
147 }
148
149 static RetainPtr<CFArrayRef> fontFilenamesFromRegistry()
150 {
151     RetainPtr<CFMutableArrayRef> filenames(AdoptCF, CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
152
153     HKEY key;
154     if (FAILED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"), 0, KEY_READ, &key)))
155         return filenames;
156
157     DWORD valueCount;
158     DWORD maxNameLength;
159     DWORD maxValueLength;
160     if (FAILED(RegQueryInfoKey(key, 0, 0, 0, 0, 0, 0, &valueCount, &maxNameLength, &maxValueLength, 0, 0))) {
161         RegCloseKey(key);
162         return filenames;
163     }
164
165     Vector<TCHAR> name(maxNameLength + 1);
166     Vector<BYTE> value(maxValueLength + 1);
167
168     for (size_t i = 0; i < valueCount; ++i) {
169         DWORD nameLength = name.size();
170         DWORD valueLength = value.size();
171         DWORD type;
172         if (FAILED(RegEnumValue(key, i, name.data(), &nameLength, 0, &type, value.data(), &valueLength)))
173             continue;
174         if (type != REG_SZ)
175             continue;
176
177         RetainPtr<CFDataRef> filename(AdoptCF, CFDataCreate(kCFAllocatorDefault, value.data(), valueLength));
178         CFArrayAppendValue(filenames.get(), filename.get());
179     }
180
181     RegCloseKey(key);
182     return filenames;
183 }
184
185 void populateFontDatabase()
186 {
187     static bool initialized;
188     if (initialized)
189         return;
190     initialized = true;
191
192     RetainPtr<CFPropertyListRef> propertyList = readFontPlist();
193     RetainPtr<CFArrayRef> lastFilenamesFromRegistry;
194     if (propertyList && CFGetTypeID(propertyList.get()) == CFDictionaryGetTypeID()) {
195         CFDictionaryRef dictionary = static_cast<CFDictionaryRef>(propertyList.get());
196         CFArrayRef array = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, fontFilenamesFromRegistryKey()));
197         if (array && CFGetTypeID(array) == CFArrayGetTypeID())
198             lastFilenamesFromRegistry = array;
199     }
200     RetainPtr<CFArrayRef> currentFilenamesFromRegistry = fontFilenamesFromRegistry();
201     bool registryChanged = !lastFilenamesFromRegistry || !CFEqual(lastFilenamesFromRegistry.get(), currentFilenamesFromRegistry.get());
202
203     if (!registryChanged && !systemHasFontsNewerThanFontsPlist()) {
204         RetainPtr<CFPropertyListRef> cgFontDBPropertyList;
205         if (propertyList) {
206             if (CFGetTypeID(propertyList.get()) == CFDictionaryGetTypeID()) {
207                 CFDictionaryRef dictionary = static_cast<CFDictionaryRef>(propertyList.get());
208                 cgFontDBPropertyList = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, cgFontDBKey()));
209             }
210             // Older versions of WebKit stored the CG font DB property list at the root of the property list.
211             if (!cgFontDBPropertyList)
212                 cgFontDBPropertyList = propertyList;
213         }
214
215         if (populateFontDatabaseFromPlist(cgFontDBPropertyList.get()))
216             return;
217     }
218
219     if (populateFontDatabaseFromFileSystem()) {
220         wkAddFontsFromRegistry();
221         RetainPtr<CFPropertyListRef> cgFontDBPropertyList(AdoptCF, wkCreateFontsPlist());
222         writeFontDatabaseToPlist(cgFontDBPropertyList.get(), currentFilenamesFromRegistry.get());
223     }
224 }
225
226 } // namespace WebCore