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