2008-11-12 Tor Arne Vestbø <tavestbo@trolltech.com>
[WebKit-https.git] / WebCore / plugins / mac / PluginPackageMac.cpp
1 /*
2  * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
3  * Copyright (C) 2008 Collabora Ltd. All rights reserved.
4  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #ifndef __LP64__
29
30 #include "config.h"
31 #include "PluginPackage.h"
32
33 #include <wtf/RetainPtr.h>
34 #include "CString.h"
35 #include "MIMETypeRegistry.h"
36 #include "NotImplemented.h"
37 #include "npruntime_impl.h"
38 #include "PluginDatabase.h"
39 #include "PluginDebug.h"
40 #include "WebCoreNSStringExtras.h"
41
42 #include <CoreFoundation/CoreFoundation.h>
43
44 #define PluginNameOrDescriptionStringNumber     126
45 #define MIMEDescriptionStringNumber             127
46 #define MIMEListStringStringNumber              128
47
48 namespace WebCore {
49
50 void PluginPackage::determineQuirks(const String& mimeType)
51 {
52     if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) {
53         // Because a single process cannot create multiple VMs, and we cannot reliably unload a
54         // Java VM, we cannot unload the Java plugin, or we'll lose reference to our only VM
55         m_quirks.add(PluginQuirkDontUnloadPlugin);
56
57         // Setting the window region to an empty region causes bad scrolling repaint problems
58         // with the Java plug-in.
59         m_quirks.add(PluginQuirkDontClipToZeroRectWhenScrolling);
60     }
61
62     if (mimeType == "application/x-shockwave-flash") {
63         // The flash plugin only requests windowless plugins if we return a mozilla user agent
64         m_quirks.add(PluginQuirkWantsMozillaUserAgent);
65         m_quirks.add(PluginQuirkThrottleInvalidate);
66         m_quirks.add(PluginQuirkThrottleWMUserPlusOneMessages);
67         m_quirks.add(PluginQuirkFlashURLNotifyBug);
68     }
69
70 }
71
72 typedef void (*BP_CreatePluginMIMETypesPreferencesFuncPtr)(void);
73
74 static WTF::RetainPtr<CFDictionaryRef> readPListFile(CFStringRef fileName, bool createFile, CFBundleRef bundle)
75 {
76     if (createFile) {
77         BP_CreatePluginMIMETypesPreferencesFuncPtr funcPtr =
78             (BP_CreatePluginMIMETypesPreferencesFuncPtr)CFBundleGetFunctionPointerForName(bundle, CFSTR("BP_CreatePluginMIMETypesPreferences"));
79         if (funcPtr)
80             funcPtr();
81     }
82
83     WTF::RetainPtr<CFDictionaryRef> map;
84     WTF::RetainPtr<CFURLRef> url =
85         CFURLCreateWithFileSystemPath(kCFAllocatorDefault, fileName, kCFURLPOSIXPathStyle, false);
86
87     CFDataRef resource = 0;
88     SInt32 code;
89     if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url.get(), &resource, 0, 0, &code))
90         return map;
91
92     WTF::RetainPtr<CFPropertyListRef> propertyList =
93             CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource, kCFPropertyListImmutable, 0);
94
95     CFRelease(resource);
96
97     if (!propertyList)
98         return map;
99
100     if (CFGetTypeID(propertyList.get()) != CFDictionaryGetTypeID())
101         return map;
102
103     map = static_cast<CFDictionaryRef>(static_cast<CFPropertyListRef>(propertyList.get()));
104     return map;
105 }
106
107 static Vector<String> stringListFromResourceId(SInt16 id)
108 {
109     Vector<String> list;
110
111     Handle handle = Get1Resource('STR#', id);
112     if (!handle)
113         return list;
114
115     CFStringEncoding encoding = stringEncodingForResource(handle);
116
117     unsigned char* p = (unsigned char*)*handle;
118     if (!p)
119         return list;
120
121     SInt16 count = *(SInt16*)p;
122     p += sizeof(SInt16);
123
124     for (SInt16 i = 0; i < count; ++i) {
125         unsigned char length = *p;
126         WTF::RetainPtr<CFStringRef> str = CFStringCreateWithPascalString(0, p, encoding);
127         list.append(str.get());
128         p += 1 + length;
129     }
130
131     return list;
132 }
133
134 bool PluginPackage::fetchInfo()
135 {
136     if (!load())
137         return false;
138
139     WTF::RetainPtr<CFDictionaryRef> mimeDict;
140
141     WTF::RetainPtr<CFTypeRef> mimeTypesFileName = CFBundleGetValueForInfoDictionaryKey(m_module, CFSTR("WebPluginMIMETypesFilename"));
142     if (mimeTypesFileName && CFGetTypeID(mimeTypesFileName.get()) == CFStringGetTypeID()) {
143
144         WTF::RetainPtr<CFStringRef> fileName = (CFStringRef)mimeTypesFileName.get();
145         WTF::RetainPtr<CFStringRef> homeDir = homeDirectoryPath().createCFString();
146         WTF::RetainPtr<CFStringRef> path = CFStringCreateWithFormat(0, 0, CFSTR("%@/Library/Preferences/%@"), homeDir.get(), fileName.get());
147
148         WTF::RetainPtr<CFDictionaryRef> plist = readPListFile(path.get(), /*createFile*/ false, m_module);
149         if (plist) {
150             // If the plist isn't localized, have the plug-in recreate it in the preferred language.
151             WTF::RetainPtr<CFStringRef> localizationName =
152                 (CFStringRef)CFDictionaryGetValue(plist.get(), CFSTR("WebPluginLocalizationName"));
153             CFLocaleRef locale = CFLocaleCopyCurrent();
154             if (localizationName != CFLocaleGetIdentifier(locale))
155                 plist = readPListFile(path.get(), /*createFile*/ true, m_module);
156
157             CFRelease(locale);
158         } else {
159             // Plist doesn't exist, ask the plug-in to create it.
160             plist = readPListFile(path.get(), /*createFile*/ true, m_module);
161         }
162
163         mimeDict = (CFDictionaryRef)CFDictionaryGetValue(plist.get(), CFSTR("WebPluginMIMETypes"));
164     }
165
166     if (!mimeDict)
167         mimeDict = (CFDictionaryRef)CFBundleGetValueForInfoDictionaryKey(m_module, CFSTR("WebPluginMIMETypes"));
168
169     if (mimeDict) {
170         CFIndex propCount = CFDictionaryGetCount(mimeDict.get());
171         Vector<const void*, 128> keys(propCount);
172         Vector<const void*, 128> values(propCount);
173         CFDictionaryGetKeysAndValues(mimeDict.get(), keys.data(), values.data());
174         for (int i = 0; i < propCount; ++i) {
175             String mimeType = (CFStringRef)keys[i];
176             mimeType = mimeType.lower();
177
178             WTF::RetainPtr<CFDictionaryRef> extensionsDict = (CFDictionaryRef)values[i];
179
180             WTF:RetainPtr<CFNumberRef> enabled = (CFNumberRef)CFDictionaryGetValue(extensionsDict.get(), CFSTR("WebPluginTypeEnabled"));
181             if (enabled) {
182                 int enabledValue = 0;
183                 if (CFNumberGetValue(enabled.get(), kCFNumberIntType, &enabledValue) && enabledValue == 0)
184                     continue;
185             }
186
187             Vector<String> mimeExtensions;
188             WTF::RetainPtr<CFArrayRef> extensions = (CFArrayRef)CFDictionaryGetValue(extensionsDict.get(), CFSTR("WebPluginExtensions"));
189             if (extensions) {
190                 CFIndex extensionCount = CFArrayGetCount(extensions.get());
191                 for (CFIndex i = 0; i < extensionCount; ++i) {
192                     String extension =(CFStringRef)CFArrayGetValueAtIndex(extensions.get(), i);
193                     extension = extension.lower();
194                     mimeExtensions.append(extension);
195                 }
196             }
197             m_mimeToExtensions.set(mimeType, mimeExtensions);
198
199             String description = (CFStringRef)CFDictionaryGetValue(extensionsDict.get(), CFSTR("WebPluginTypeDescription"));
200             m_mimeToDescriptions.set(mimeType, description);
201         }
202
203         m_name = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(m_module, CFSTR("WebPluginName"));
204         m_description = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(m_module, CFSTR("WebPluginDescription"));
205
206     } else {
207         int resFile = CFBundleOpenBundleResourceMap(m_module);
208
209         UseResFile(resFile);
210
211         Vector<String> mimes = stringListFromResourceId(MIMEListStringStringNumber);
212
213         if (mimes.size() % 2 != 0)
214             return false;
215
216         Vector<String> descriptions = stringListFromResourceId(MIMEDescriptionStringNumber);
217         if (descriptions.size() != mimes.size() / 2)
218             return false;
219
220         for (size_t i = 0;  i < mimes.size(); i += 2) {
221             String mime = mimes[i].lower();
222             Vector<String> extensions;
223             mimes[i + 1].lower().split(UChar(','), extensions);
224
225             m_mimeToExtensions.set(mime, extensions);
226
227             m_mimeToDescriptions.set(mime, descriptions[i / 2]);
228         }
229
230         Vector<String> names = stringListFromResourceId(PluginNameOrDescriptionStringNumber);
231         if (names.size() == 2) {
232             m_description = names[0];
233             m_name = names[1];
234         }
235
236         CFBundleCloseBundleResourceMap(m_module, resFile);
237     }
238
239     return true;
240 }
241
242 bool PluginPackage::load()
243 {
244     if (m_isLoaded) {
245         m_loadCount++;
246         return true;
247     }
248
249     WTF::RetainPtr<CFStringRef> path(AdoptCF, m_path.createCFString());
250     WTF::RetainPtr<CFURLRef> url(AdoptCF, CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(),
251                                                                         kCFURLPOSIXPathStyle, false));
252     m_module = CFBundleCreate(NULL, url.get());
253     if (!m_module || !CFBundleLoadExecutable(m_module)) {
254         LOG(Plugin, "%s not loaded", m_path.utf8().data());
255         return false;
256     }
257
258     m_isLoaded = true;
259
260     NP_GetEntryPointsFuncPtr NP_GetEntryPoints = 0;
261     NP_InitializeFuncPtr NP_Initialize;
262     NPError npErr;
263
264     NP_Initialize = (NP_InitializeFuncPtr)CFBundleGetFunctionPointerForName(m_module, CFSTR("NP_Initialize"));
265     NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)CFBundleGetFunctionPointerForName(m_module, CFSTR("NP_GetEntryPoints"));
266     m_NPP_Shutdown = (NPP_ShutdownProcPtr)CFBundleGetFunctionPointerForName(m_module, CFSTR("NP_Shutdown"));
267
268     if (!NP_Initialize || !NP_GetEntryPoints || !m_NPP_Shutdown)
269         goto abort;
270
271     memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs));
272     m_pluginFuncs.size = sizeof(m_pluginFuncs);
273
274     npErr = NP_GetEntryPoints(&m_pluginFuncs);
275     LOG_NPERROR(npErr);
276     if (npErr != NPERR_NO_ERROR)
277         goto abort;
278
279     m_browserFuncs.size = sizeof (m_browserFuncs);
280     m_browserFuncs.version = NP_VERSION_MINOR;
281     m_browserFuncs.geturl = NPN_GetURL;
282     m_browserFuncs.posturl = NPN_PostURL;
283     m_browserFuncs.requestread = NPN_RequestRead;
284     m_browserFuncs.newstream = NPN_NewStream;
285     m_browserFuncs.write = NPN_Write;
286     m_browserFuncs.destroystream = NPN_DestroyStream;
287     m_browserFuncs.status = NPN_Status;
288     m_browserFuncs.uagent = NPN_UserAgent;
289     m_browserFuncs.memalloc = NPN_MemAlloc;
290     m_browserFuncs.memfree = NPN_MemFree;
291     m_browserFuncs.memflush = NPN_MemFlush;
292     m_browserFuncs.reloadplugins = NPN_ReloadPlugins;
293     m_browserFuncs.geturlnotify = NPN_GetURLNotify;
294     m_browserFuncs.posturlnotify = NPN_PostURLNotify;
295     m_browserFuncs.getvalue = NPN_GetValue;
296     m_browserFuncs.setvalue = NPN_SetValue;
297     m_browserFuncs.invalidaterect = NPN_InvalidateRect;
298     m_browserFuncs.invalidateregion = NPN_InvalidateRegion;
299     m_browserFuncs.forceredraw = NPN_ForceRedraw;
300     m_browserFuncs.getJavaEnv = NPN_GetJavaEnv;
301     m_browserFuncs.getJavaPeer = NPN_GetJavaPeer;
302     m_browserFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState;
303     m_browserFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState;
304
305     m_browserFuncs.releasevariantvalue = _NPN_ReleaseVariantValue;
306     m_browserFuncs.getstringidentifier = _NPN_GetStringIdentifier;
307     m_browserFuncs.getstringidentifiers = _NPN_GetStringIdentifiers;
308     m_browserFuncs.getintidentifier = _NPN_GetIntIdentifier;
309     m_browserFuncs.identifierisstring = _NPN_IdentifierIsString;
310     m_browserFuncs.utf8fromidentifier = _NPN_UTF8FromIdentifier;
311     m_browserFuncs.createobject = _NPN_CreateObject;
312     m_browserFuncs.retainobject = _NPN_RetainObject;
313     m_browserFuncs.releaseobject = _NPN_ReleaseObject;
314     m_browserFuncs.invoke = _NPN_Invoke;
315     m_browserFuncs.invokeDefault = _NPN_InvokeDefault;
316     m_browserFuncs.evaluate = _NPN_Evaluate;
317     m_browserFuncs.getproperty = _NPN_GetProperty;
318     m_browserFuncs.setproperty = _NPN_SetProperty;
319     m_browserFuncs.removeproperty = _NPN_RemoveProperty;
320     m_browserFuncs.hasproperty = _NPN_HasMethod;
321     m_browserFuncs.hasmethod = _NPN_HasProperty;
322     m_browserFuncs.setexception = _NPN_SetException;
323     m_browserFuncs.enumerate = _NPN_Enumerate;
324
325     npErr = NP_Initialize(&m_browserFuncs);
326     if (npErr != NPERR_NO_ERROR)
327         goto abort;
328
329     m_loadCount++;
330     return true;
331
332 abort:
333     unloadWithoutShutdown();
334     return false;
335 }
336
337 unsigned PluginPackage::hash() const
338 {
339     unsigned hashCodes[2] = {
340         m_path.impl()->hash(),
341         m_lastModified
342     };
343
344     return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), 2 * sizeof(unsigned) / sizeof(UChar));
345 }
346
347 bool PluginPackage::equal(const PluginPackage& a, const PluginPackage& b)
348 {
349     return a.m_description == b.m_description;
350 }
351
352 int PluginPackage::compareFileVersion(const PlatformModuleVersion& compareVersion) const
353 {
354     // return -1, 0, or 1 if plug-in version is less than, equal to, or greater than
355     // the passed version
356     if (m_moduleVersion != compareVersion)
357         return m_moduleVersion > compareVersion ? 1 : -1;
358     return 0;
359 }
360
361 } // namespace WebCore
362
363 #endif // !__LP64__