1 /*
2 * Copyright (C) 2006, 2007 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 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "WebKitDLL.h"
28
29 #include "WebLocalizableStrings.h"
30
31 #include <WebCore/PlatformString.h>
32 #include <wtf/text/CString.h>
33 #include <wtf/text/StringHash.h>
34
35 #include <wtf/Assertions.h>
36 #include <wtf/HashMap.h>
37 #include <wtf/RetainPtr.h>
38 #include <wtf/StdLibExtras.h>
39 #include <CoreFoundation/CoreFoundation.h>
40
41 class LocalizedString;
42
43 using namespace WebCore;
44
45 WebLocalizableStringsBundle WebKitLocalizableStringsBundle = { "com.apple.WebKit", 0 };
46
47 typedef HashMap<String, LocalizedString*> LocalizedStringMap;
48
mainBundleLocStringsMutex()49 static Mutex& mainBundleLocStringsMutex()
50 {
51 DEFINE_STATIC_LOCAL(Mutex, mutex, ());
52 return mutex;
53 }
54
mainBundleLocStrings()55 static LocalizedStringMap& mainBundleLocStrings()
56 {
57 DEFINE_STATIC_LOCAL(LocalizedStringMap, map, ());
58 return map;
59 }
60
frameworkLocStringsMutex()61 static Mutex& frameworkLocStringsMutex()
62 {
63 DEFINE_STATIC_LOCAL(Mutex, mutex, ());
64 return mutex;
65 }
66
frameworkLocStrings()67 static LocalizedStringMap frameworkLocStrings()
68 {
69 DEFINE_STATIC_LOCAL(LocalizedStringMap, map, ());
70 return map;
71 }
72
73 class LocalizedString {
74 WTF_MAKE_NONCOPYABLE(LocalizedString);
75 public:
LocalizedString(CFStringRef string)76 LocalizedString(CFStringRef string)
77 : m_cfString(string)
78 {
79 ASSERT_ARG(string, string);
80 }
81
82 operator LPCTSTR() const;
operator CFStringRef() const83 operator CFStringRef() const { return m_cfString; }
84
85 private:
86 CFStringRef m_cfString;
87 mutable String m_string;
88 };
89
operator LPCTSTR() const90 LocalizedString::operator LPCTSTR() const
91 {
92 if (!m_string.isEmpty())
93 return m_string.charactersWithNullTermination();
94
95 m_string = m_cfString;
96
97 for (unsigned int i = 1; i < m_string.length(); i++)
98 if (m_string[i] == '@' && (m_string[i - 1] == '%' || (i > 2 && m_string[i - 1] == '$' && m_string[i - 2] >= '1' && m_string[i - 2] <= '9' && m_string[i - 3] == '%')))
99 m_string.replace(i, 1, "s");
100
101 return m_string.charactersWithNullTermination();
102 }
103
createWebKitBundle()104 static CFBundleRef createWebKitBundle()
105 {
106 static CFBundleRef bundle;
107 static bool initialized;
108
109 if (initialized)
110 return bundle;
111 initialized = true;
112
113 WCHAR pathStr[MAX_PATH];
114 DWORD length = ::GetModuleFileNameW(gInstance, pathStr, MAX_PATH);
115 if (!length || (length == MAX_PATH && GetLastError() == ERROR_INSUFFICIENT_BUFFER))
116 return 0;
117
118 bool found = false;
119 for (int i = length - 1; i >= 0; i--) {
120 // warning C6385: Invalid data: accessing 'pathStr', the readable size is '520' bytes, but '2000' bytes might be read
121 #pragma warning(suppress: 6385)
122 if (pathStr[i] == L'\\') {
123 // warning C6386: Buffer overrun: accessing 'pathStr', the writable size is '520' bytes, but '1996' bytes might be written
124 #pragma warning(suppress: 6386)
125 pathStr[i] = 0;
126 found = true;
127 break;
128 }
129 }
130 if (!found)
131 return 0;
132
133 if (wcscat_s(pathStr, MAX_PATH, L"\\WebKit.resources"))
134 return 0;
135
136 String bundlePathString(pathStr);
137 CFStringRef bundlePathCFString = bundlePathString.createCFString();
138 if (!bundlePathCFString)
139 return 0;
140
141 CFURLRef bundleURLRef = CFURLCreateWithFileSystemPath(0, bundlePathCFString, kCFURLWindowsPathStyle, true);
142 CFRelease(bundlePathCFString);
143 if (!bundleURLRef)
144 return 0;
145
146 bundle = CFBundleCreate(0, bundleURLRef);
147 CFRelease(bundleURLRef);
148 return bundle;
149 }
150
cfBundleForStringsBundle(WebLocalizableStringsBundle * stringsBundle)151 static CFBundleRef cfBundleForStringsBundle(WebLocalizableStringsBundle* stringsBundle)
152 {
153 if (!stringsBundle) {
154 static CFBundleRef mainBundle = CFBundleGetMainBundle();
155 return mainBundle;
156 }
157
158 createWebKitBundle();
159
160 if (!stringsBundle->bundle)
161 stringsBundle->bundle = CFBundleGetBundleWithIdentifier(RetainPtr<CFStringRef>(AdoptCF, CFStringCreateWithCString(0, stringsBundle->identifier, kCFStringEncodingASCII)).get());
162 return stringsBundle->bundle;
163 }
164
copyLocalizedStringFromBundle(WebLocalizableStringsBundle * stringsBundle,const String & key)165 static CFStringRef copyLocalizedStringFromBundle(WebLocalizableStringsBundle* stringsBundle, const String& key)
166 {
167 static CFStringRef notFound = CFSTR("localized string not found");
168
169 CFBundleRef bundle = cfBundleForStringsBundle(stringsBundle);
170 if (!bundle)
171 return notFound;
172
173 RetainPtr<CFStringRef> keyString(AdoptCF, key.createCFString());
174 CFStringRef result = CFCopyLocalizedStringWithDefaultValue(keyString.get(), 0, bundle, notFound, 0);
175
176 ASSERT_WITH_MESSAGE(result != notFound, "could not find localizable string %s in bundle", key);
177 return result;
178 }
179
findCachedString(WebLocalizableStringsBundle * stringsBundle,const String & key)180 static LocalizedString* findCachedString(WebLocalizableStringsBundle* stringsBundle, const String& key)
181 {
182 if (!stringsBundle) {
183 MutexLocker lock(mainBundleLocStringsMutex());
184 return mainBundleLocStrings().get(key);
185 }
186
187 if (stringsBundle->bundle == WebKitLocalizableStringsBundle.bundle) {
188 MutexLocker lock(frameworkLocStringsMutex());
189 return frameworkLocStrings().get(key);
190 }
191
192 return 0;
193 }
194
cacheString(WebLocalizableStringsBundle * stringsBundle,const String & key,LocalizedString * value)195 static void cacheString(WebLocalizableStringsBundle* stringsBundle, const String& key, LocalizedString* value)
196 {
197 if (!stringsBundle) {
198 MutexLocker lock(mainBundleLocStringsMutex());
199 mainBundleLocStrings().set(key, value);
200 return;
201 }
202
203 MutexLocker lock(frameworkLocStringsMutex());
204 frameworkLocStrings().set(key, value);
205 }
206
localizedString(WebLocalizableStringsBundle * stringsBundle,const String & key)207 static const LocalizedString& localizedString(WebLocalizableStringsBundle* stringsBundle, const String& key)
208 {
209 LocalizedString* string = findCachedString(stringsBundle, key);
210 if (string)
211 return *string;
212
213 string = new LocalizedString(copyLocalizedStringFromBundle(stringsBundle, key));
214 cacheString(stringsBundle, key, string);
215
216 return *string;
217 }
218
WebLocalizedStringUTF8(WebLocalizableStringsBundle * stringsBundle,LPCSTR key)219 CFStringRef WebLocalizedStringUTF8(WebLocalizableStringsBundle* stringsBundle, LPCSTR key)
220 {
221 if (!key)
222 return 0;
223
224 return localizedString(stringsBundle, String::fromUTF8(key));
225 }
226
WebLocalizedLPCTSTRUTF8(WebLocalizableStringsBundle * stringsBundle,LPCSTR key)227 LPCTSTR WebLocalizedLPCTSTRUTF8(WebLocalizableStringsBundle* stringsBundle, LPCSTR key)
228 {
229 if (!key)
230 return 0;
231
232 return localizedString(stringsBundle, String::fromUTF8(key));
233 }
234
235 // These functions are deprecated.
236
WebLocalizedString(WebLocalizableStringsBundle * stringsBundle,LPCTSTR key)237 CFStringRef WebLocalizedString(WebLocalizableStringsBundle* stringsBundle, LPCTSTR key)
238 {
239 if (!key)
240 return 0;
241
242 return localizedString(stringsBundle, String(key));
243 }
244
WebLocalizedLPCTSTR(WebLocalizableStringsBundle * stringsBundle,LPCTSTR key)245 LPCTSTR WebLocalizedLPCTSTR(WebLocalizableStringsBundle* stringsBundle, LPCTSTR key)
246 {
247 if (!key)
248 return 0;
249
250 return localizedString(stringsBundle, String(key));
251 }
252
SetWebLocalizedStringMainBundle(CFBundleRef)253 void SetWebLocalizedStringMainBundle(CFBundleRef)
254 {
255 }
256