OSDN Git Service

am 2f7dd354: (-s ours) am 49fd7191: am e17c8aee: DO NOT MERGE: The clip should be...
[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 #include "NetscapePluginModule.h"
27
28 #include <WebCore/WebCoreNSStringExtras.h>
29 #include <wtf/HashSet.h>
30
31 using namespace WebCore;
32
33 namespace WebKit {
34
35 static bool getPluginArchitecture(CFBundleRef bundle, cpu_type_t& pluginArchitecture)
36 {
37     RetainPtr<CFArrayRef> pluginArchitecturesArray(AdoptCF, CFBundleCopyExecutableArchitectures(bundle));
38     if (!pluginArchitecturesArray)
39         return false;
40
41     // Turn the array into a set.
42     HashSet<unsigned> architectures;
43     for (CFIndex i = 0, numPluginArchitectures = CFArrayGetCount(pluginArchitecturesArray.get()); i < numPluginArchitectures; ++i) {
44         CFNumberRef number = static_cast<CFNumberRef>(CFArrayGetValueAtIndex(pluginArchitecturesArray.get(), i));
45         
46         SInt32 architecture;
47         if (!CFNumberGetValue(number, kCFNumberSInt32Type, &architecture))
48             continue;
49         architectures.add(architecture);
50     }
51     
52 #ifdef __x86_64__
53     // We only support 64-bit Intel plug-ins on 64-bit Intel.
54     if (architectures.contains(kCFBundleExecutableArchitectureX86_64)) {
55         pluginArchitecture = CPU_TYPE_X86_64;
56         return true;
57     }
58
59     // We also support 32-bit Intel plug-ins on 64-bit Intel.
60     if (architectures.contains(kCFBundleExecutableArchitectureI386)) {
61         pluginArchitecture = CPU_TYPE_X86;
62         return true;
63     }
64 #elif defined(__i386__)
65     // We only support 32-bit Intel plug-ins on 32-bit Intel.
66     if (architectures.contains(kCFBundleExecutableArchitectureI386)) {
67         pluginArchitecture = CPU_TYPE_X86;
68         return true;
69     }
70 #elif defined(__ppc64__)
71     // We only support 64-bit PPC plug-ins on 64-bit PPC.
72     if (architectures.contains(kCFBundleExecutableArchitecturePPC64)) {
73         pluginArchitecture = CPU_TYPE_POWERPC64;
74         return true;
75     }
76 #elif defined(__ppc__)
77     // We only support 32-bit PPC plug-ins on 32-bit PPC.
78     if (architectures.contains(kCFBundleExecutableArchitecturePPC)) {
79         pluginArchitecture = CPU_TYPE_POWERPC;
80         return true;
81     }
82 #else
83 #error "Unhandled architecture"
84 #endif
85
86     return false;
87 }
88
89 static bool getPluginInfoFromPropertyLists(CFBundleRef bundle, PluginInfo& pluginInfo)
90 {
91     // FIXME: Handle WebPluginMIMETypesFilenameKey.
92     
93     CFDictionaryRef mimeTypes = static_cast<CFDictionaryRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypes")));
94     if (!mimeTypes || CFGetTypeID(mimeTypes) != CFDictionaryGetTypeID())
95         return false;
96
97     // Get the plug-in name.
98     CFStringRef pluginName = static_cast<CFStringRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginName")));
99     if (pluginName && CFGetTypeID(pluginName) == CFStringGetTypeID())
100         pluginInfo.name = pluginName;
101     
102     // Get the plug-in description.
103     CFStringRef pluginDescription = static_cast<CFStringRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginDescription")));
104     if (pluginDescription && CFGetTypeID(pluginDescription) == CFStringGetTypeID())
105         pluginInfo.desc = pluginDescription;
106     
107     // Get the MIME type mapping dictionary.
108     CFIndex numMimeTypes = CFDictionaryGetCount(mimeTypes);
109     Vector<CFStringRef> mimeTypesVector(numMimeTypes);
110     Vector<CFDictionaryRef> mimeTypeInfoVector(numMimeTypes);
111     CFDictionaryGetKeysAndValues(mimeTypes, reinterpret_cast<const void**>(mimeTypesVector.data()), reinterpret_cast<const void**>(mimeTypeInfoVector.data()));
112     
113     for (CFIndex i = 0; i < numMimeTypes; ++i) {
114         MimeClassInfo mimeClassInfo;
115         
116         // If this MIME type is invalid, ignore it.
117         CFStringRef mimeType = mimeTypesVector[i];
118         if (!mimeType || CFGetTypeID(mimeType) != CFStringGetTypeID() || CFStringGetLength(mimeType) == 0)
119             continue;
120
121         // If this MIME type doesn't have a valid info dictionary, ignore it.
122         CFDictionaryRef mimeTypeInfo = mimeTypeInfoVector[i];
123         if (!mimeTypeInfo || CFGetTypeID(mimeTypeInfo) != CFDictionaryGetTypeID())
124             continue;
125
126         // Get the MIME type description.
127         CFStringRef mimeTypeDescription = static_cast<CFStringRef>(CFDictionaryGetValue(mimeTypeInfo, CFSTR("WebPluginTypeDescription")));
128         if (mimeTypeDescription && CFGetTypeID(mimeTypeDescription) != CFStringGetTypeID())
129             mimeTypeDescription = 0;
130
131         mimeClassInfo.type = String(mimeType).lower();
132         mimeClassInfo.desc = mimeTypeDescription;
133
134         // Now get the extensions for this MIME type.
135         CFIndex numExtensions = 0;
136         CFArrayRef extensionsArray = static_cast<CFArrayRef>(CFDictionaryGetValue(mimeTypeInfo, CFSTR("WebPluginExtensions")));
137         if (extensionsArray && CFGetTypeID(extensionsArray) == CFArrayGetTypeID())
138             numExtensions = CFArrayGetCount(extensionsArray);
139
140         for (CFIndex i = 0; i < numExtensions; ++i) {
141             CFStringRef extension = static_cast<CFStringRef>(CFArrayGetValueAtIndex(extensionsArray, i));
142             if (!extension || CFGetTypeID(extension) != CFStringGetTypeID())
143                 continue;
144             
145             mimeClassInfo.extensions.append(String(extension).lower());
146         }
147
148         // Add this MIME type.
149         pluginInfo.mimes.append(mimeClassInfo);
150     }
151
152     return true;    
153 }
154
155 class ResourceMap {
156 public:
157     explicit ResourceMap(CFBundleRef bundle)
158         : m_bundle(bundle)
159         , m_currentResourceFile(CurResFile())
160         , m_bundleResourceMap(CFBundleOpenBundleResourceMap(m_bundle))
161     {
162         UseResFile(m_bundleResourceMap);
163     }
164
165     ~ResourceMap()
166     {
167         // Close the resource map.
168         CFBundleCloseBundleResourceMap(m_bundle, m_bundleResourceMap);
169         
170         // And restore the old resource.
171         UseResFile(m_currentResourceFile);
172     }
173
174     bool isValid() const { return m_bundleResourceMap != -1; }
175
176 private:
177     CFBundleRef m_bundle;
178     ResFileRefNum m_currentResourceFile;
179     ResFileRefNum m_bundleResourceMap;
180 };
181
182 static bool getStringListResource(ResID resourceID, Vector<String>& stringList) {
183     Handle stringListHandle = Get1Resource('STR#', resourceID);
184     if (!stringListHandle || !*stringListHandle)
185         return false;
186
187     // Get the string list size.
188     Size stringListSize = GetHandleSize(stringListHandle);
189     if (stringListSize < static_cast<Size>(sizeof(UInt16)))
190         return false;
191
192     CFStringEncoding stringEncoding = stringEncodingForResource(stringListHandle);
193
194     unsigned char* ptr = reinterpret_cast<unsigned char*>(*stringListHandle);
195     unsigned char* end = ptr + stringListSize;
196     
197     // Get the number of strings in the string list.
198     UInt16 numStrings = *reinterpret_cast<UInt16*>(ptr);
199     ptr += sizeof(UInt16);
200
201     for (UInt16 i = 0; i < numStrings; ++i) {
202         // We're past the end of the string, bail.
203         if (ptr >= end)
204             return false;
205
206         // Get the string length.
207         unsigned char stringLength = *ptr++;
208
209         RetainPtr<CFStringRef> cfString(AdoptCF, CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, ptr, stringLength, stringEncoding, false, kCFAllocatorNull));
210         if (!cfString.get())
211             return false;
212
213         stringList.append(cfString.get());
214         ptr += stringLength;
215     }
216
217     if (ptr != end)
218         return false;
219
220     return true;
221 }
222
223 static const ResID PluginNameOrDescriptionStringNumber = 126;
224 static const ResID MIMEDescriptionStringNumber = 127;
225 static const ResID MIMEListStringStringNumber = 128;
226
227 static bool getPluginInfoFromCarbonResources(CFBundleRef bundle, PluginInfo& pluginInfo)
228 {
229     ResourceMap resourceMap(bundle);
230     if (!resourceMap.isValid())
231         return false;
232
233     // Get the description and name string list.
234     Vector<String> descriptionAndName;
235     if (!getStringListResource(PluginNameOrDescriptionStringNumber, descriptionAndName))
236         return false;
237
238     // Get the MIME types and extensions string list. This list needs to be a multiple of two.
239     Vector<String> mimeTypesAndExtensions;
240     if (!getStringListResource(MIMEListStringStringNumber, mimeTypesAndExtensions))
241         return false;
242
243     if (mimeTypesAndExtensions.size() % 2)
244         return false;
245
246     size_t numMimeTypes = mimeTypesAndExtensions.size() / 2;
247     
248     // Now get the MIME type descriptions string list. This string list needs to be the same length as the number of MIME types.
249     Vector<String> mimeTypeDescriptions;
250     if (!getStringListResource(MIMEDescriptionStringNumber, mimeTypeDescriptions))
251         return false;
252
253     if (mimeTypeDescriptions.size() != numMimeTypes)
254         return false;
255
256     // Add all MIME types.
257     for (size_t i = 0; i < mimeTypesAndExtensions.size() / 2; ++i) {
258         MimeClassInfo mimeClassInfo;
259         
260         const String& mimeType = mimeTypesAndExtensions[i * 2];
261         const String& description = mimeTypeDescriptions[i];
262         
263         mimeClassInfo.type = mimeType.lower();
264         mimeClassInfo.desc = description;
265         
266         Vector<String> extensions;
267         mimeTypesAndExtensions[i * 2 + 1].split(',', extensions);
268         
269         for (size_t i = 0; i < extensions.size(); ++i)
270             mimeClassInfo.extensions.append(extensions[i].lower());
271
272         pluginInfo.mimes.append(mimeClassInfo);
273     }
274
275     // Set the description and name if they exist.
276     if (descriptionAndName.size() > 0)
277         pluginInfo.desc = descriptionAndName[0];
278     if (descriptionAndName.size() > 1)
279         pluginInfo.name = descriptionAndName[1];
280
281     return true;
282 }
283
284 bool NetscapePluginModule::getPluginInfo(const String& pluginPath, PluginInfoStore::Plugin& plugin)
285 {
286     RetainPtr<CFStringRef> bundlePath(AdoptCF, pluginPath.createCFString());
287     RetainPtr<CFURLRef> bundleURL(AdoptCF, CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath.get(), kCFURLPOSIXPathStyle, false));
288     
289     // Try to initialize the bundle.
290     RetainPtr<CFBundleRef> bundle(AdoptCF, CFBundleCreate(kCFAllocatorDefault, bundleURL.get()));
291     if (!bundle)
292         return false;
293     
294     // Check if this bundle is an NPAPI plug-in.
295     UInt32 packageType = 0;
296     CFBundleGetPackageInfo(bundle.get(), &packageType, 0);
297     if (packageType != FOUR_CHAR_CODE('BRPL'))
298         return false;
299     
300     // Check that the architecture is valid.
301     cpu_type_t pluginArchitecture = 0;
302     if (!getPluginArchitecture(bundle.get(), pluginArchitecture))
303         return false;
304     
305     // Check that there's valid info for this plug-in.
306     if (!getPluginInfoFromPropertyLists(bundle.get(), plugin.info) &&
307         !getPluginInfoFromCarbonResources(bundle.get(), plugin.info))
308         return false;
309     
310     plugin.path = pluginPath;
311     plugin.pluginArchitecture = pluginArchitecture;
312     plugin.bundleIdentifier = CFBundleGetIdentifier(bundle.get());
313     plugin.versionNumber = CFBundleGetVersionNumber(bundle.get());
314     
315     RetainPtr<CFStringRef> filename(AdoptCF, CFURLCopyLastPathComponent(bundleURL.get()));
316     plugin.info.file = filename.get();
317     
318     if (plugin.info.name.isNull())
319         plugin.info.name = plugin.info.file;
320     if (plugin.info.desc.isNull())
321         plugin.info.desc = plugin.info.file;
322     
323     return true;
324 }
325
326 void NetscapePluginModule::determineQuirks()
327 {
328     PluginInfoStore::Plugin plugin;
329     if (!getPluginInfo(m_pluginPath, plugin))
330         return;
331
332     if (plugin.bundleIdentifier == "com.macromedia.Flash Player.plugin") {
333         // Flash requires that the return value of getprogname() be "WebKitPluginHost".
334         m_pluginQuirks.add(PluginQuirks::PrognameShouldBeWebKitPluginHost);
335     }
336 }
337
338 } // namespace WebKit