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 41class LocalizedString; 42 43using namespace WebCore; 44 45WebLocalizableStringsBundle WebKitLocalizableStringsBundle = { "com.apple.WebKit", 0 }; 46 47typedef HashMap<String, LocalizedString*> LocalizedStringMap; 48 49static Mutex& mainBundleLocStringsMutex() 50{ 51 DEFINE_STATIC_LOCAL(Mutex, mutex, ()); 52 return mutex; 53} 54 55static LocalizedStringMap& mainBundleLocStrings() 56{ 57 DEFINE_STATIC_LOCAL(LocalizedStringMap, map, ()); 58 return map; 59} 60 61static Mutex& frameworkLocStringsMutex() 62{ 63 DEFINE_STATIC_LOCAL(Mutex, mutex, ()); 64 return mutex; 65} 66 67static LocalizedStringMap frameworkLocStrings() 68{ 69 DEFINE_STATIC_LOCAL(LocalizedStringMap, map, ()); 70 return map; 71} 72 73class LocalizedString { 74 WTF_MAKE_NONCOPYABLE(LocalizedString); 75public: 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 85private: 86 CFStringRef m_cfString; 87 mutable String m_string; 88}; 89 90LocalizedString::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 104static 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 151static 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 165static 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 180static 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 195static 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 207static 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 219CFStringRef WebLocalizedStringUTF8(WebLocalizableStringsBundle* stringsBundle, LPCSTR key) 220{ 221 if (!key) 222 return 0; 223 224 return localizedString(stringsBundle, String::fromUTF8(key)); 225} 226 227LPCTSTR 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 237CFStringRef WebLocalizedString(WebLocalizableStringsBundle* stringsBundle, LPCTSTR key) 238{ 239 if (!key) 240 return 0; 241 242 return localizedString(stringsBundle, String(key)); 243} 244 245LPCTSTR WebLocalizedLPCTSTR(WebLocalizableStringsBundle* stringsBundle, LPCTSTR key) 246{ 247 if (!key) 248 return 0; 249 250 return localizedString(stringsBundle, String(key)); 251} 252 253void SetWebLocalizedStringMainBundle(CFBundleRef) 254{ 255} 256