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 49 static Mutex& mainBundleLocStringsMutex() 50 { 51 DEFINE_STATIC_LOCAL(Mutex, mutex, ()); 52 return mutex; 53 } 54 55 static LocalizedStringMap& mainBundleLocStrings() 56 { 57 DEFINE_STATIC_LOCAL(LocalizedStringMap, map, ()); 58 return map; 59 } 60 61 static Mutex& frameworkLocStringsMutex() 62 { 63 DEFINE_STATIC_LOCAL(Mutex, mutex, ()); 64 return mutex; 65 } 66 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: 76 LocalizedString(CFStringRef string) 77 : m_cfString(string) 78 { 79 ASSERT_ARG(string, string); 80 } 81 82 operator LPCTSTR() const; 83 operator CFStringRef() const { return m_cfString; } 84 85 private: 86 CFStringRef m_cfString; 87 mutable String m_string; 88 }; 89 90 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 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 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 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 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 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 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 219 CFStringRef WebLocalizedStringUTF8(WebLocalizableStringsBundle* stringsBundle, LPCSTR key) 220 { 221 if (!key) 222 return 0; 223 224 return localizedString(stringsBundle, String::fromUTF8(key)); 225 } 226 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 237 CFStringRef WebLocalizedString(WebLocalizableStringsBundle* stringsBundle, LPCTSTR key) 238 { 239 if (!key) 240 return 0; 241 242 return localizedString(stringsBundle, String(key)); 243 } 244 245 LPCTSTR WebLocalizedLPCTSTR(WebLocalizableStringsBundle* stringsBundle, LPCTSTR key) 246 { 247 if (!key) 248 return 0; 249 250 return localizedString(stringsBundle, String(key)); 251 } 252 253 void SetWebLocalizedStringMainBundle(CFBundleRef) 254 { 255 } 256