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.
27 #import "NetscapePluginModule.h"
29 #import <WebCore/WebCoreNSStringExtras.h>
30 #import <wtf/HashSet.h>
32 using namespace WebCore;
36 static bool getPluginArchitecture(CFBundleRef bundle, cpu_type_t& pluginArchitecture)
38 RetainPtr<CFArrayRef> pluginArchitecturesArray(AdoptCF, CFBundleCopyExecutableArchitectures(bundle));
39 if (!pluginArchitecturesArray)
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));
48 if (!CFNumberGetValue(number, kCFNumberSInt32Type, &architecture))
50 architectures.add(architecture);
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;
60 // We also support 32-bit Intel plug-ins on 64-bit Intel.
61 if (architectures.contains(kCFBundleExecutableArchitectureI386)) {
62 pluginArchitecture = CPU_TYPE_X86;
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;
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;
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;
84 #error "Unhandled architecture"
90 static RetainPtr<CFDictionaryRef> getMIMETypesFromPluginBundle(CFBundleRef bundle)
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));
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);
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())
108 return static_cast<CFDictionaryRef>(CFDictionaryGetValue(static_cast<CFDictionaryRef>(propertyList.get()), CFSTR("WebPluginMIMETypes")));
111 return static_cast<CFDictionaryRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypes")));
114 static bool getPluginInfoFromPropertyLists(CFBundleRef bundle, PluginInfo& pluginInfo)
116 RetainPtr<CFDictionaryRef> mimeTypes = getMIMETypesFromPluginBundle(bundle);
117 if (!mimeTypes || CFGetTypeID(mimeTypes.get()) != CFDictionaryGetTypeID())
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;
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;
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()));
136 for (CFIndex i = 0; i < numMimeTypes; ++i) {
137 MimeClassInfo mimeClassInfo;
139 // If this MIME type is invalid, ignore it.
140 CFStringRef mimeType = mimeTypesVector[i];
141 if (!mimeType || CFGetTypeID(mimeType) != CFStringGetTypeID() || CFStringGetLength(mimeType) == 0)
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())
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;
154 mimeClassInfo.type = String(mimeType).lower();
155 mimeClassInfo.desc = mimeTypeDescription;
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);
163 for (CFIndex i = 0; i < numExtensions; ++i) {
164 CFStringRef extension = static_cast<CFStringRef>(CFArrayGetValueAtIndex(extensionsArray, i));
165 if (!extension || CFGetTypeID(extension) != CFStringGetTypeID())
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);
174 for (size_t i = 0; i < extensionComponents.size(); ++i)
175 mimeClassInfo.extensions.append(extensionComponents[i]);
178 // Add this MIME type.
179 pluginInfo.mimes.append(mimeClassInfo);
187 explicit ResourceMap(CFBundleRef bundle)
189 , m_currentResourceFile(CurResFile())
190 , m_bundleResourceMap(CFBundleOpenBundleResourceMap(m_bundle))
192 UseResFile(m_bundleResourceMap);
197 // Close the resource map.
198 CFBundleCloseBundleResourceMap(m_bundle, m_bundleResourceMap);
200 // And restore the old resource.
201 UseResFile(m_currentResourceFile);
204 bool isValid() const { return m_bundleResourceMap != -1; }
207 CFBundleRef m_bundle;
208 ResFileRefNum m_currentResourceFile;
209 ResFileRefNum m_bundleResourceMap;
212 static bool getStringListResource(ResID resourceID, Vector<String>& stringList) {
213 Handle stringListHandle = Get1Resource('STR#', resourceID);
214 if (!stringListHandle || !*stringListHandle)
217 // Get the string list size.
218 Size stringListSize = GetHandleSize(stringListHandle);
219 if (stringListSize < static_cast<Size>(sizeof(UInt16)))
222 CFStringEncoding stringEncoding = stringEncodingForResource(stringListHandle);
224 unsigned char* ptr = reinterpret_cast<unsigned char*>(*stringListHandle);
225 unsigned char* end = ptr + stringListSize;
227 // Get the number of strings in the string list.
228 UInt16 numStrings = *reinterpret_cast<UInt16*>(ptr);
229 ptr += sizeof(UInt16);
231 for (UInt16 i = 0; i < numStrings; ++i) {
232 // We're past the end of the string, bail.
236 // Get the string length.
237 unsigned char stringLength = *ptr++;
239 RetainPtr<CFStringRef> cfString(AdoptCF, CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, ptr, stringLength, stringEncoding, false, kCFAllocatorNull));
243 stringList.append(cfString.get());
253 static const ResID PluginNameOrDescriptionStringNumber = 126;
254 static const ResID MIMEDescriptionStringNumber = 127;
255 static const ResID MIMEListStringStringNumber = 128;
257 static bool getPluginInfoFromCarbonResources(CFBundleRef bundle, PluginInfo& pluginInfo)
259 ResourceMap resourceMap(bundle);
260 if (!resourceMap.isValid())
263 // Get the description and name string list.
264 Vector<String> descriptionAndName;
265 if (!getStringListResource(PluginNameOrDescriptionStringNumber, descriptionAndName))
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))
273 if (mimeTypesAndExtensions.size() % 2)
276 // Now get the MIME type descriptions string list. This string list needs to be the same length as the number of MIME types.
277 Vector<String> mimeTypeDescriptions;
278 if (!getStringListResource(MIMEDescriptionStringNumber, mimeTypeDescriptions))
281 // Add all MIME types.
282 for (size_t i = 0; i < mimeTypesAndExtensions.size() / 2; ++i) {
283 MimeClassInfo mimeClassInfo;
285 const String& mimeType = mimeTypesAndExtensions[i * 2];
287 if (i < mimeTypeDescriptions.size())
288 description = mimeTypeDescriptions[i];
290 mimeClassInfo.type = mimeType.lower();
291 mimeClassInfo.desc = description;
293 Vector<String> extensions;
294 mimeTypesAndExtensions[i * 2 + 1].split(',', extensions);
296 for (size_t i = 0; i < extensions.size(); ++i)
297 mimeClassInfo.extensions.append(extensions[i].lower());
299 pluginInfo.mimes.append(mimeClassInfo);
302 // Set the description and name if they exist.
303 if (descriptionAndName.size() > 0)
304 pluginInfo.desc = descriptionAndName[0];
305 if (descriptionAndName.size() > 1)
306 pluginInfo.name = descriptionAndName[1];
311 bool NetscapePluginModule::getPluginInfo(const String& pluginPath, PluginInfoStore::Plugin& plugin)
313 RetainPtr<CFStringRef> bundlePath(AdoptCF, pluginPath.createCFString());
314 RetainPtr<CFURLRef> bundleURL(AdoptCF, CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath.get(), kCFURLPOSIXPathStyle, false));
316 // Try to initialize the bundle.
317 RetainPtr<CFBundleRef> bundle(AdoptCF, CFBundleCreate(kCFAllocatorDefault, bundleURL.get()));
321 // Check if this bundle is an NPAPI plug-in.
322 UInt32 packageType = 0;
323 CFBundleGetPackageInfo(bundle.get(), &packageType, 0);
324 if (packageType != FOUR_CHAR_CODE('BRPL'))
327 // Check that the architecture is valid.
328 cpu_type_t pluginArchitecture = 0;
329 if (!getPluginArchitecture(bundle.get(), pluginArchitecture))
332 // Check that there's valid info for this plug-in.
333 if (!getPluginInfoFromPropertyLists(bundle.get(), plugin.info) &&
334 !getPluginInfoFromCarbonResources(bundle.get(), plugin.info))
337 plugin.path = pluginPath;
338 plugin.pluginArchitecture = pluginArchitecture;
339 plugin.bundleIdentifier = CFBundleGetIdentifier(bundle.get());
340 plugin.versionNumber = CFBundleGetVersionNumber(bundle.get());
342 RetainPtr<CFStringRef> filename(AdoptCF, CFURLCopyLastPathComponent(bundleURL.get()));
343 plugin.info.file = filename.get();
345 if (plugin.info.name.isNull())
346 plugin.info.name = plugin.info.file;
347 if (plugin.info.desc.isNull())
348 plugin.info.desc = plugin.info.file;
353 void NetscapePluginModule::determineQuirks()
355 PluginInfoStore::Plugin plugin;
356 if (!getPluginInfo(m_pluginPath, plugin))
359 if (plugin.bundleIdentifier == "com.macromedia.Flash Player.plugin") {
360 // Flash requires that the return value of getprogname() be "WebKitPluginHost".
361 m_pluginQuirks.add(PluginQuirks::PrognameShouldBeWebKitPluginHost);
363 // Flash supports snapshotting.
364 m_pluginQuirks.add(PluginQuirks::SupportsSnapshotting);
367 if (plugin.bundleIdentifier == "com.microsoft.SilverlightPlugin") {
368 // Silverlight doesn't explicitly opt into transparency, so we'll do it whenever
369 // there's a 'background' attribute.
370 m_pluginQuirks.add(PluginQuirks::MakeTransparentIfBackgroundAttributeExists);
373 #ifndef NP_NO_QUICKDRAW
374 if (plugin.bundleIdentifier == "com.apple.ist.ds.appleconnect.webplugin") {
375 // The AppleConnect plug-in uses QuickDraw but doesn't paint or receive events
376 // so we'll allow it to be instantiated even though we don't support QuickDraw.
377 m_pluginQuirks.add(PluginQuirks::AllowHalfBakedQuickDrawSupport);
382 } // namespace WebKit