2 * Copyright (C) 2010 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #include "NetscapePluginModule.h"
28 #include <WebCore/WebCoreNSStringExtras.h>
29 #include <wtf/HashSet.h>
31 using namespace WebCore;
35 static bool getPluginArchitecture(CFBundleRef bundle, cpu_type_t& pluginArchitecture)
37 RetainPtr<CFArrayRef> pluginArchitecturesArray(AdoptCF, CFBundleCopyExecutableArchitectures(bundle));
38 if (!pluginArchitecturesArray)
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));
47 if (!CFNumberGetValue(number, kCFNumberSInt32Type, &architecture))
49 architectures.add(architecture);
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;
59 // We also support 32-bit Intel plug-ins on 64-bit Intel.
60 if (architectures.contains(kCFBundleExecutableArchitectureI386)) {
61 pluginArchitecture = CPU_TYPE_X86;
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;
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;
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;
83 #error "Unhandled architecture"
89 static bool getPluginInfoFromPropertyLists(CFBundleRef bundle, PluginInfo& pluginInfo)
91 // FIXME: Handle WebPluginMIMETypesFilenameKey.
93 CFDictionaryRef mimeTypes = static_cast<CFDictionaryRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypes")));
94 if (!mimeTypes || CFGetTypeID(mimeTypes) != CFDictionaryGetTypeID())
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;
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;
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()));
113 for (CFIndex i = 0; i < numMimeTypes; ++i) {
114 MimeClassInfo mimeClassInfo;
116 // If this MIME type is invalid, ignore it.
117 CFStringRef mimeType = mimeTypesVector[i];
118 if (!mimeType || CFGetTypeID(mimeType) != CFStringGetTypeID() || CFStringGetLength(mimeType) == 0)
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())
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;
131 mimeClassInfo.type = String(mimeType).lower();
132 mimeClassInfo.desc = mimeTypeDescription;
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);
140 for (CFIndex i = 0; i < numExtensions; ++i) {
141 CFStringRef extension = static_cast<CFStringRef>(CFArrayGetValueAtIndex(extensionsArray, i));
142 if (!extension || CFGetTypeID(extension) != CFStringGetTypeID())
145 mimeClassInfo.extensions.append(String(extension).lower());
148 // Add this MIME type.
149 pluginInfo.mimes.append(mimeClassInfo);
157 explicit ResourceMap(CFBundleRef bundle)
159 , m_currentResourceFile(CurResFile())
160 , m_bundleResourceMap(CFBundleOpenBundleResourceMap(m_bundle))
162 UseResFile(m_bundleResourceMap);
167 // Close the resource map.
168 CFBundleCloseBundleResourceMap(m_bundle, m_bundleResourceMap);
170 // And restore the old resource.
171 UseResFile(m_currentResourceFile);
174 bool isValid() const { return m_bundleResourceMap != -1; }
177 CFBundleRef m_bundle;
178 ResFileRefNum m_currentResourceFile;
179 ResFileRefNum m_bundleResourceMap;
182 static bool getStringListResource(ResID resourceID, Vector<String>& stringList) {
183 Handle stringListHandle = Get1Resource('STR#', resourceID);
184 if (!stringListHandle || !*stringListHandle)
187 // Get the string list size.
188 Size stringListSize = GetHandleSize(stringListHandle);
189 if (stringListSize < static_cast<Size>(sizeof(UInt16)))
192 CFStringEncoding stringEncoding = stringEncodingForResource(stringListHandle);
194 unsigned char* ptr = reinterpret_cast<unsigned char*>(*stringListHandle);
195 unsigned char* end = ptr + stringListSize;
197 // Get the number of strings in the string list.
198 UInt16 numStrings = *reinterpret_cast<UInt16*>(ptr);
199 ptr += sizeof(UInt16);
201 for (UInt16 i = 0; i < numStrings; ++i) {
202 // We're past the end of the string, bail.
206 // Get the string length.
207 unsigned char stringLength = *ptr++;
209 RetainPtr<CFStringRef> cfString(AdoptCF, CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, ptr, stringLength, stringEncoding, false, kCFAllocatorNull));
213 stringList.append(cfString.get());
223 static const ResID PluginNameOrDescriptionStringNumber = 126;
224 static const ResID MIMEDescriptionStringNumber = 127;
225 static const ResID MIMEListStringStringNumber = 128;
227 static bool getPluginInfoFromCarbonResources(CFBundleRef bundle, PluginInfo& pluginInfo)
229 ResourceMap resourceMap(bundle);
230 if (!resourceMap.isValid())
233 // Get the description and name string list.
234 Vector<String> descriptionAndName;
235 if (!getStringListResource(PluginNameOrDescriptionStringNumber, descriptionAndName))
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))
243 if (mimeTypesAndExtensions.size() % 2)
246 size_t numMimeTypes = mimeTypesAndExtensions.size() / 2;
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))
253 if (mimeTypeDescriptions.size() != numMimeTypes)
256 // Add all MIME types.
257 for (size_t i = 0; i < mimeTypesAndExtensions.size() / 2; ++i) {
258 MimeClassInfo mimeClassInfo;
260 const String& mimeType = mimeTypesAndExtensions[i * 2];
261 const String& description = mimeTypeDescriptions[i];
263 mimeClassInfo.type = mimeType.lower();
264 mimeClassInfo.desc = description;
266 Vector<String> extensions;
267 mimeTypesAndExtensions[i * 2 + 1].split(',', extensions);
269 for (size_t i = 0; i < extensions.size(); ++i)
270 mimeClassInfo.extensions.append(extensions[i].lower());
272 pluginInfo.mimes.append(mimeClassInfo);
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];
284 bool NetscapePluginModule::getPluginInfo(const String& pluginPath, PluginInfoStore::Plugin& plugin)
286 RetainPtr<CFStringRef> bundlePath(AdoptCF, pluginPath.createCFString());
287 RetainPtr<CFURLRef> bundleURL(AdoptCF, CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath.get(), kCFURLPOSIXPathStyle, false));
289 // Try to initialize the bundle.
290 RetainPtr<CFBundleRef> bundle(AdoptCF, CFBundleCreate(kCFAllocatorDefault, bundleURL.get()));
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'))
300 // Check that the architecture is valid.
301 cpu_type_t pluginArchitecture = 0;
302 if (!getPluginArchitecture(bundle.get(), pluginArchitecture))
305 // Check that there's valid info for this plug-in.
306 if (!getPluginInfoFromPropertyLists(bundle.get(), plugin.info) &&
307 !getPluginInfoFromCarbonResources(bundle.get(), plugin.info))
310 plugin.path = pluginPath;
311 plugin.pluginArchitecture = pluginArchitecture;
312 plugin.bundleIdentifier = CFBundleGetIdentifier(bundle.get());
313 plugin.versionNumber = CFBundleGetVersionNumber(bundle.get());
315 RetainPtr<CFStringRef> filename(AdoptCF, CFURLCopyLastPathComponent(bundleURL.get()));
316 plugin.info.file = filename.get();
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;
326 void NetscapePluginModule::determineQuirks()
328 PluginInfoStore::Plugin plugin;
329 if (!getPluginInfo(m_pluginPath, plugin))
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);
338 } // namespace WebKit