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