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 #pragma warning(push, 0) 32 #include <WebCore/CString.h> 33 #include <WebCore/PlatformString.h> 34 #include <WebCore/StringHash.h> 35 #pragma warning(pop) 36 37 #include <wtf/Assertions.h> 38 #include <wtf/HashMap.h> 39 #include <wtf/RetainPtr.h> 40 #include <wtf/StdLibExtras.h> 41 #include <CoreFoundation/CoreFoundation.h> 42 43 class LocalizedString; 44 45 using namespace WebCore; 46 47 WebLocalizableStringsBundle WebKitLocalizableStringsBundle = { "com.apple.WebKit", 0 }; 48 49 typedef HashMap<String, LocalizedString*> LocalizedStringMap; 50 51 static Mutex& mainBundleLocStringsMutex() 52 { 53 DEFINE_STATIC_LOCAL(Mutex, mutex, ()); 54 return mutex; 55 } 56 57 static LocalizedStringMap& mainBundleLocStrings() 58 { 59 DEFINE_STATIC_LOCAL(LocalizedStringMap, map, ()); 60 return map; 61 } 62 63 static Mutex& frameworkLocStringsMutex() 64 { 65 DEFINE_STATIC_LOCAL(Mutex, mutex, ()); 66 return mutex; 67 } 68 69 static LocalizedStringMap frameworkLocStrings() 70 { 71 DEFINE_STATIC_LOCAL(LocalizedStringMap, map, ()); 72 return map; 73 } 74 75 class LocalizedString : public Noncopyable { 76 public: 77 LocalizedString(CFStringRef string) 78 : m_cfString(string) 79 { 80 ASSERT_ARG(string, string); 81 } 82 83 operator LPCTSTR() const; 84 operator CFStringRef() const { return m_cfString; } 85 86 private: 87 CFStringRef m_cfString; 88 mutable String m_string; 89 }; 90 91 LocalizedString::operator LPCTSTR() const 92 { 93 if (!m_string.isEmpty()) 94 return m_string.charactersWithNullTermination(); 95 96 m_string = m_cfString; 97 98 for (unsigned int i = 1; i < m_string.length(); i++) 99 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] == '%'))) 100 m_string.replace(i, 1, "s"); 101 102 return m_string.charactersWithNullTermination(); 103 } 104 105 static CFBundleRef createWebKitBundle() 106 { 107 static CFBundleRef bundle; 108 static bool initialized; 109 110 if (initialized) 111 return bundle; 112 initialized = true; 113 114 WCHAR pathStr[MAX_PATH]; 115 DWORD length = ::GetModuleFileNameW(gInstance, pathStr, MAX_PATH); 116 if (!length || (length == MAX_PATH && GetLastError() == ERROR_INSUFFICIENT_BUFFER)) 117 return 0; 118 119 bool found = false; 120 for (int i = length - 1; i >= 0; i--) { 121 // warning C6385: Invalid data: accessing 'pathStr', the readable size is '520' bytes, but '2000' bytes might be read 122 #pragma warning(suppress: 6385) 123 if (pathStr[i] == L'\\') { 124 // warning C6386: Buffer overrun: accessing 'pathStr', the writable size is '520' bytes, but '1996' bytes might be written 125 #pragma warning(suppress: 6386) 126 pathStr[i] = 0; 127 found = true; 128 break; 129 } 130 } 131 if (!found) 132 return 0; 133 134 if (wcscat_s(pathStr, MAX_PATH, L"\\WebKit.resources")) 135 return 0; 136 137 String bundlePathString(pathStr); 138 CFStringRef bundlePathCFString = bundlePathString.createCFString(); 139 if (!bundlePathCFString) 140 return 0; 141 142 CFURLRef bundleURLRef = CFURLCreateWithFileSystemPath(0, bundlePathCFString, kCFURLWindowsPathStyle, true); 143 CFRelease(bundlePathCFString); 144 if (!bundleURLRef) 145 return 0; 146 147 bundle = CFBundleCreate(0, bundleURLRef); 148 CFRelease(bundleURLRef); 149 return bundle; 150 } 151 152 static CFBundleRef cfBundleForStringsBundle(WebLocalizableStringsBundle* stringsBundle) 153 { 154 if (!stringsBundle) { 155 static CFBundleRef mainBundle = CFBundleGetMainBundle(); 156 return mainBundle; 157 } 158 159 createWebKitBundle(); 160 161 if (!stringsBundle->bundle) 162 stringsBundle->bundle = CFBundleGetBundleWithIdentifier(RetainPtr<CFStringRef>(AdoptCF, CFStringCreateWithCString(0, stringsBundle->identifier, kCFStringEncodingASCII)).get()); 163 return stringsBundle->bundle; 164 } 165 166 static CFStringRef copyLocalizedStringFromBundle(WebLocalizableStringsBundle* stringsBundle, const String& key) 167 { 168 static CFStringRef notFound = CFSTR("localized string not found"); 169 170 CFBundleRef bundle = cfBundleForStringsBundle(stringsBundle); 171 if (!bundle) 172 return notFound; 173 174 RetainPtr<CFStringRef> keyString(AdoptCF, key.createCFString()); 175 CFStringRef result = CFCopyLocalizedStringWithDefaultValue(keyString.get(), 0, bundle, notFound, 0); 176 177 ASSERT_WITH_MESSAGE(result != notFound, "could not find localizable string %s in bundle", key); 178 return result; 179 } 180 181 static LocalizedString* findCachedString(WebLocalizableStringsBundle* stringsBundle, const String& key) 182 { 183 if (!stringsBundle) { 184 MutexLocker lock(mainBundleLocStringsMutex()); 185 return mainBundleLocStrings().get(key); 186 } 187 188 if (stringsBundle->bundle == WebKitLocalizableStringsBundle.bundle) { 189 MutexLocker lock(frameworkLocStringsMutex()); 190 return frameworkLocStrings().get(key); 191 } 192 193 return 0; 194 } 195 196 static void cacheString(WebLocalizableStringsBundle* stringsBundle, const String& key, LocalizedString* value) 197 { 198 if (!stringsBundle) { 199 MutexLocker lock(mainBundleLocStringsMutex()); 200 mainBundleLocStrings().set(key, value); 201 return; 202 } 203 204 MutexLocker lock(frameworkLocStringsMutex()); 205 frameworkLocStrings().set(key, value); 206 } 207 208 static const LocalizedString& localizedString(WebLocalizableStringsBundle* stringsBundle, const String& key) 209 { 210 LocalizedString* string = findCachedString(stringsBundle, key); 211 if (string) 212 return *string; 213 214 string = new LocalizedString(copyLocalizedStringFromBundle(stringsBundle, key)); 215 cacheString(stringsBundle, key, string); 216 217 return *string; 218 } 219 220 CFStringRef WebLocalizedStringUTF8(WebLocalizableStringsBundle* stringsBundle, LPCSTR key) 221 { 222 if (!key) 223 return 0; 224 225 return localizedString(stringsBundle, String::fromUTF8(key)); 226 } 227 228 LPCTSTR WebLocalizedLPCTSTRUTF8(WebLocalizableStringsBundle* stringsBundle, LPCSTR key) 229 { 230 if (!key) 231 return 0; 232 233 return localizedString(stringsBundle, String::fromUTF8(key)); 234 } 235 236 // These functions are deprecated. 237 238 CFStringRef WebLocalizedString(WebLocalizableStringsBundle* stringsBundle, LPCTSTR key) 239 { 240 if (!key) 241 return 0; 242 243 return localizedString(stringsBundle, String(key)); 244 } 245 246 LPCTSTR WebLocalizedLPCTSTR(WebLocalizableStringsBundle* stringsBundle, LPCTSTR key) 247 { 248 if (!key) 249 return 0; 250 251 return localizedString(stringsBundle, String(key)); 252 } 253 254 void SetWebLocalizedStringMainBundle(CFBundleRef) 255 { 256 } 257