OSDN Git Service

Merge changes I55c6d71a,Ifb3277d4,Ia1b847a2,I7ba9cf3f,Ida2b2a8a,I1280ec90,I72f818d5...
[android-x86/external-webkit.git] / Source / WebKit2 / Shared / Plugins / Netscape / mac / NetscapePluginModuleMac.mm
1 /*
2  * Copyright (C) 2010 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 INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "NetscapePluginModule.h"
28
29 #import <WebCore/WebCoreNSStringExtras.h>
30 #import <wtf/HashSet.h>
31
32 using namespace WebCore;
33
34 namespace WebKit {
35
36 static bool getPluginArchitecture(CFBundleRef bundle, cpu_type_t& pluginArchitecture)
37 {
38     RetainPtr<CFArrayRef> pluginArchitecturesArray(AdoptCF, CFBundleCopyExecutableArchitectures(bundle));
39     if (!pluginArchitecturesArray)
40         return false;
41
42     // Turn the array into a set.
43     HashSet<unsigned> architectures;
44     for (CFIndex i = 0, numPluginArchitectures = CFArrayGetCount(pluginArchitecturesArray.get()); i < numPluginArchitectures; ++i) {
45         CFNumberRef number = static_cast<CFNumberRef>(CFArrayGetValueAtIndex(pluginArchitecturesArray.get(), i));
46         
47         SInt32 architecture;
48         if (!CFNumberGetValue(number, kCFNumberSInt32Type, &architecture))
49             continue;
50         architectures.add(architecture);
51     }
52     
53 #ifdef __x86_64__
54     // We only support 64-bit Intel plug-ins on 64-bit Intel.
55     if (architectures.contains(kCFBundleExecutableArchitectureX86_64)) {
56         pluginArchitecture = CPU_TYPE_X86_64;
57         return true;
58     }
59
60     // We also support 32-bit Intel plug-ins on 64-bit Intel.
61     if (architectures.contains(kCFBundleExecutableArchitectureI386)) {
62         pluginArchitecture = CPU_TYPE_X86;
63         return true;
64     }
65 #elif defined(__i386__)
66     // We only support 32-bit Intel plug-ins on 32-bit Intel.
67     if (architectures.contains(kCFBundleExecutableArchitectureI386)) {
68         pluginArchitecture = CPU_TYPE_X86;
69         return true;
70     }
71 #elif defined(__ppc64__)
72     // We only support 64-bit PPC plug-ins on 64-bit PPC.
73     if (architectures.contains(kCFBundleExecutableArchitecturePPC64)) {
74         pluginArchitecture = CPU_TYPE_POWERPC64;
75         return true;
76     }
77 #elif defined(__ppc__)
78     // We only support 32-bit PPC plug-ins on 32-bit PPC.
79     if (architectures.contains(kCFBundleExecutableArchitecturePPC)) {
80         pluginArchitecture = CPU_TYPE_POWERPC;
81         return true;
82     }
83 #else
84 #error "Unhandled architecture"
85 #endif
86
87     return false;
88 }
89     
90 static RetainPtr<CFDictionaryRef> getMIMETypesFromPluginBundle(CFBundleRef bundle)
91 {
92     CFStringRef propertyListFilename = static_cast<CFStringRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypesFilename")));
93     if (propertyListFilename) {
94         RetainPtr<CFStringRef> propertyListPath(AdoptCF, CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("%@/Library/Preferences/%@"), NSHomeDirectory(), propertyListFilename));
95         RetainPtr<CFURLRef> propertyListURL(AdoptCF, CFURLCreateWithFileSystemPath(kCFAllocatorDefault, propertyListPath.get(), kCFURLPOSIXPathStyle, FALSE));
96
97         CFDataRef propertyListData;
98         CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, propertyListURL.get(), &propertyListData, 0, 0, 0);
99         RetainPtr<CFPropertyListRef> propertyList(AdoptCF, CFPropertyListCreateWithData(kCFAllocatorDefault, propertyListData, kCFPropertyListImmutable, 0, 0));
100         if (propertyListData)
101             CFRelease(propertyListData);
102         
103         // FIXME: Have the plug-in create the MIME types property list if it doesn't exist.
104         // https://bugs.webkit.org/show_bug.cgi?id=57204
105         if (!propertyList || CFGetTypeID(propertyList.get()) != CFDictionaryGetTypeID())
106             return 0;
107         
108         return static_cast<CFDictionaryRef>(CFDictionaryGetValue(static_cast<CFDictionaryRef>(propertyList.get()), CFSTR("WebPluginMIMETypes")));
109     }
110     
111     return static_cast<CFDictionaryRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypes")));
112 }
113
114 static bool getPluginInfoFromPropertyLists(CFBundleRef bundle, PluginInfo& pluginInfo)
115 {
116     RetainPtr<CFDictionaryRef> mimeTypes = getMIMETypesFromPluginBundle(bundle);
117     if (!mimeTypes || CFGetTypeID(mimeTypes.get()) != CFDictionaryGetTypeID())
118         return false;
119
120     // Get the plug-in name.
121     CFStringRef pluginName = static_cast<CFStringRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginName")));
122     if (pluginName && CFGetTypeID(pluginName) == CFStringGetTypeID())
123         pluginInfo.name = pluginName;
124     
125     // Get the plug-in description.
126     CFStringRef pluginDescription = static_cast<CFStringRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginDescription")));
127     if (pluginDescription && CFGetTypeID(pluginDescription) == CFStringGetTypeID())
128         pluginInfo.desc = pluginDescription;
129     
130     // Get the MIME type mapping dictionary.
131     CFIndex numMimeTypes = CFDictionaryGetCount(mimeTypes.get());          
132     Vector<CFStringRef> mimeTypesVector(numMimeTypes);
133     Vector<CFDictionaryRef> mimeTypeInfoVector(numMimeTypes);
134     CFDictionaryGetKeysAndValues(mimeTypes.get(), reinterpret_cast<const void**>(mimeTypesVector.data()), reinterpret_cast<const void**>(mimeTypeInfoVector.data()));
135     
136     for (CFIndex i = 0; i < numMimeTypes; ++i) {
137         MimeClassInfo mimeClassInfo;
138         
139         // If this MIME type is invalid, ignore it.
140         CFStringRef mimeType = mimeTypesVector[i];
141         if (!mimeType || CFGetTypeID(mimeType) != CFStringGetTypeID() || CFStringGetLength(mimeType) == 0)
142             continue;
143
144         // If this MIME type doesn't have a valid info dictionary, ignore it.
145         CFDictionaryRef mimeTypeInfo = mimeTypeInfoVector[i];
146         if (!mimeTypeInfo || CFGetTypeID(mimeTypeInfo) != CFDictionaryGetTypeID())
147             continue;
148
149         // Get the MIME type description.
150         CFStringRef mimeTypeDescription = static_cast<CFStringRef>(CFDictionaryGetValue(mimeTypeInfo, CFSTR("WebPluginTypeDescription")));
151         if (mimeTypeDescription && CFGetTypeID(mimeTypeDescription) != CFStringGetTypeID())
152             mimeTypeDescription = 0;
153
154         mimeClassInfo.type = String(mimeType).lower();
155         mimeClassInfo.desc = mimeTypeDescription;
156
157         // Now get the extensions for this MIME type.
158         CFIndex numExtensions = 0;
159         CFArrayRef extensionsArray = static_cast<CFArrayRef>(CFDictionaryGetValue(mimeTypeInfo, CFSTR("WebPluginExtensions")));
160         if (extensionsArray && CFGetTypeID(extensionsArray) == CFArrayGetTypeID())
161             numExtensions = CFArrayGetCount(extensionsArray);
162
163         for (CFIndex i = 0; i < numExtensions; ++i) {
164             CFStringRef extension = static_cast<CFStringRef>(CFArrayGetValueAtIndex(extensionsArray, i));
165             if (!extension || CFGetTypeID(extension) != CFStringGetTypeID())
166                 continue;
167
168             // The DivX plug-in lists multiple extensions in a comma separated string instead of using
169             // multiple array elements in the property list. Work around this here by splitting the
170             // extension string into components.
171             Vector<String> extensionComponents;
172             String(extension).lower().split(',', extensionComponents);
173
174             for (size_t i = 0; i < extensionComponents.size(); ++i)
175                 mimeClassInfo.extensions.append(extensionComponents[i]);
176         }
177
178         // Add this MIME type.
179         pluginInfo.mimes.append(mimeClassInfo);
180     }
181
182     return true;    
183 }
184
185 class ResourceMap {
186 public:
187     explicit ResourceMap(CFBundleRef bundle)
188         : m_bundle(bundle)
189         , m_currentResourceFile(CurResFile())
190         , m_bundleResourceMap(CFBundleOpenBundleResourceMap(m_bundle))
191     {
192         UseResFile(m_bundleResourceMap);
193     }
194
195     ~ResourceMap()
196     {
197         // Close the resource map.
198         CFBundleCloseBundleResourceMap(m_bundle, m_bundleResourceMap);
199         
200         // And restore the old resource.
201         UseResFile(m_currentResourceFile);
202     }
203
204     bool isValid() const { return m_bundleResourceMap != -1; }
205
206 private:
207     CFBundleRef m_bundle;
208     ResFileRefNum m_currentResourceFile;
209     ResFileRefNum m_bundleResourceMap;
210 };
211
212 static bool getStringListResource(ResID resourceID, Vector<String>& stringList) {
213     Handle stringListHandle = Get1Resource('STR#', resourceID);
214     if (!stringListHandle || !*stringListHandle)
215         return false;
216
217     // Get the string list size.
218     Size stringListSize = GetHandleSize(stringListHandle);
219     if (stringListSize < static_cast<Size>(sizeof(UInt16)))
220         return false;
221
222     CFStringEncoding stringEncoding = stringEncodingForResource(stringListHandle);
223
224     unsigned char* ptr = reinterpret_cast<unsigned char*>(*stringListHandle);
225     unsigned char* end = ptr + stringListSize;
226     
227     // Get the number of strings in the string list.
228     UInt16 numStrings = *reinterpret_cast<UInt16*>(ptr);
229     ptr += sizeof(UInt16);
230
231     for (UInt16 i = 0; i < numStrings; ++i) {
232         // We're past the end of the string, bail.
233         if (ptr >= end)
234             return false;
235
236         // Get the string length.
237         unsigned char stringLength = *ptr++;
238
239         RetainPtr<CFStringRef> cfString(AdoptCF, CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, ptr, stringLength, stringEncoding, false, kCFAllocatorNull));
240         if (!cfString.get())
241             return false;
242
243         stringList.append(cfString.get());
244         ptr += stringLength;
245     }
246
247     if (ptr != end)
248         return false;
249
250     return true;
251 }
252
253 static const ResID PluginNameOrDescriptionStringNumber = 126;
254 static const ResID MIMEDescriptionStringNumber = 127;
255 static const ResID MIMEListStringStringNumber = 128;
256
257 static bool getPluginInfoFromCarbonResources(CFBundleRef bundle, PluginInfo& pluginInfo)
258 {
259     ResourceMap resourceMap(bundle);
260     if (!resourceMap.isValid())
261         return false;
262
263     // Get the description and name string list.
264     Vector<String> descriptionAndName;
265     if (!getStringListResource(PluginNameOrDescriptionStringNumber, descriptionAndName))
266         return false;
267
268     // Get the MIME types and extensions string list. This list needs to be a multiple of two.
269     Vector<String> mimeTypesAndExtensions;
270     if (!getStringListResource(MIMEListStringStringNumber, mimeTypesAndExtensions))
271         return false;
272
273     if (mimeTypesAndExtensions.size() % 2)
274         return false;
275
276     size_t numMimeTypes = mimeTypesAndExtensions.size() / 2;
277     
278     // Now get the MIME type descriptions string list. This string list needs to be the same length as the number of MIME types.
279     Vector<String> mimeTypeDescriptions;
280     if (!getStringListResource(MIMEDescriptionStringNumber, mimeTypeDescriptions))
281         return false;
282
283     if (mimeTypeDescriptions.size() != numMimeTypes)
284         return false;
285
286     // Add all MIME types.
287     for (size_t i = 0; i < mimeTypesAndExtensions.size() / 2; ++i) {
288         MimeClassInfo mimeClassInfo;
289         
290         const String& mimeType = mimeTypesAndExtensions[i * 2];
291         const String& description = mimeTypeDescriptions[i];
292         
293         mimeClassInfo.type = mimeType.lower();
294         mimeClassInfo.desc = description;
295         
296         Vector<String> extensions;
297         mimeTypesAndExtensions[i * 2 + 1].split(',', extensions);
298         
299         for (size_t i = 0; i < extensions.size(); ++i)
300             mimeClassInfo.extensions.append(extensions[i].lower());
301
302         pluginInfo.mimes.append(mimeClassInfo);
303     }
304
305     // Set the description and name if they exist.
306     if (descriptionAndName.size() > 0)
307         pluginInfo.desc = descriptionAndName[0];
308     if (descriptionAndName.size() > 1)
309         pluginInfo.name = descriptionAndName[1];
310
311     return true;
312 }
313
314 bool NetscapePluginModule::getPluginInfo(const String& pluginPath, PluginInfoStore::Plugin& plugin)
315 {
316     RetainPtr<CFStringRef> bundlePath(AdoptCF, pluginPath.createCFString());
317     RetainPtr<CFURLRef> bundleURL(AdoptCF, CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath.get(), kCFURLPOSIXPathStyle, false));
318     
319     // Try to initialize the bundle.
320     RetainPtr<CFBundleRef> bundle(AdoptCF, CFBundleCreate(kCFAllocatorDefault, bundleURL.get()));
321     if (!bundle)
322         return false;
323     
324     // Check if this bundle is an NPAPI plug-in.
325     UInt32 packageType = 0;
326     CFBundleGetPackageInfo(bundle.get(), &packageType, 0);
327     if (packageType != FOUR_CHAR_CODE('BRPL'))
328         return false;
329     
330     // Check that the architecture is valid.
331     cpu_type_t pluginArchitecture = 0;
332     if (!getPluginArchitecture(bundle.get(), pluginArchitecture))
333         return false;
334     
335     // Check that there's valid info for this plug-in.
336     if (!getPluginInfoFromPropertyLists(bundle.get(), plugin.info) &&
337         !getPluginInfoFromCarbonResources(bundle.get(), plugin.info))
338         return false;
339     
340     plugin.path = pluginPath;
341     plugin.pluginArchitecture = pluginArchitecture;
342     plugin.bundleIdentifier = CFBundleGetIdentifier(bundle.get());
343     plugin.versionNumber = CFBundleGetVersionNumber(bundle.get());
344     
345     RetainPtr<CFStringRef> filename(AdoptCF, CFURLCopyLastPathComponent(bundleURL.get()));
346     plugin.info.file = filename.get();
347     
348     if (plugin.info.name.isNull())
349         plugin.info.name = plugin.info.file;
350     if (plugin.info.desc.isNull())
351         plugin.info.desc = plugin.info.file;
352     
353     return true;
354 }
355
356 void NetscapePluginModule::determineQuirks()
357 {
358     PluginInfoStore::Plugin plugin;
359     if (!getPluginInfo(m_pluginPath, plugin))
360         return;
361
362     if (plugin.bundleIdentifier == "com.macromedia.Flash Player.plugin") {
363         // Flash requires that the return value of getprogname() be "WebKitPluginHost".
364         m_pluginQuirks.add(PluginQuirks::PrognameShouldBeWebKitPluginHost);
365
366         // Flash supports snapshotting.
367         m_pluginQuirks.add(PluginQuirks::SupportsSnapshotting);
368     }
369
370     if (plugin.bundleIdentifier == "com.microsoft.SilverlightPlugin") {
371         // Silverlight doesn't explicitly opt into transparency, so we'll do it whenever
372         // there's a 'background' attribute.
373         m_pluginQuirks.add(PluginQuirks::MakeTransparentIfBackgroundAttributeExists);
374     }
375
376 #ifndef NP_NO_QUICKDRAW
377     if (plugin.bundleIdentifier == "com.apple.ist.ds.appleconnect.webplugin") {
378         // The AppleConnect plug-in uses QuickDraw but doesn't paint or receive events
379         // so we'll allow it to be instantiated even though we don't support QuickDraw.
380         m_pluginQuirks.add(PluginQuirks::AllowHalfBakedQuickDrawSupport);
381     }
382 #endif
383 }
384
385 } // namespace WebKit