e6cf81a5effd93bbd36a5fe4f575c2e894c655d4
[WebKit-https.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 #if ENABLE(NETSCAPE_PLUGIN_API)
30
31 #import "PluginProcessProxy.h"
32 #import "PluginSandboxProfile.h"
33 #import <WebCore/WebCoreNSStringExtras.h>
34 #import <wtf/HashSet.h>
35 #import <wtf/MainThread.h>
36
37 using namespace WebCore;
38
39 namespace WebKit {
40
41 static bool getPluginArchitecture(CFBundleRef bundle, PluginModuleInfo& plugin)
42 {
43     RetainPtr<CFArrayRef> pluginArchitecturesArray = adoptCF(CFBundleCopyExecutableArchitectures(bundle));
44     if (!pluginArchitecturesArray)
45         return false;
46
47     // Turn the array into a set.
48     HashSet<unsigned> architectures;
49     for (CFIndex i = 0, numPluginArchitectures = CFArrayGetCount(pluginArchitecturesArray.get()); i < numPluginArchitectures; ++i) {
50         CFNumberRef number = static_cast<CFNumberRef>(CFArrayGetValueAtIndex(pluginArchitecturesArray.get(), i));
51         
52         SInt32 architecture;
53         if (!CFNumberGetValue(number, kCFNumberSInt32Type, &architecture))
54             continue;
55         architectures.add(architecture);
56     }
57     
58 #ifdef __x86_64__
59     // We only support 64-bit Intel plug-ins on 64-bit Intel.
60     if (architectures.contains(kCFBundleExecutableArchitectureX86_64)) {
61         plugin.pluginArchitecture = CPU_TYPE_X86_64;
62         return true;
63     }
64
65     // We also support 32-bit Intel plug-ins on 64-bit Intel.
66     if (architectures.contains(kCFBundleExecutableArchitectureI386)) {
67         plugin.pluginArchitecture = CPU_TYPE_X86;
68         return true;
69     }
70 #elif defined(__i386__)
71     // We only support 32-bit Intel plug-ins on 32-bit Intel.
72     if (architectures.contains(kCFBundleExecutableArchitectureI386)) {
73         plugin.pluginArchitecture = CPU_TYPE_X86;
74         return true;
75     }
76 #elif defined(__ppc64__)
77     // We only support 64-bit PPC plug-ins on 64-bit PPC.
78     if (architectures.contains(kCFBundleExecutableArchitecturePPC64)) {
79         plugin.pluginArchitecture = CPU_TYPE_POWERPC64;
80         return true;
81     }
82 #elif defined(__ppc__)
83     // We only support 32-bit PPC plug-ins on 32-bit PPC.
84     if (architectures.contains(kCFBundleExecutableArchitecturePPC)) {
85         plugin.pluginArchitecture = CPU_TYPE_POWERPC;
86         return true;
87     }
88 #else
89 #error "Unhandled architecture"
90 #endif
91
92     return false;
93 }
94
95 static RetainPtr<CFDictionaryRef> contentsOfPropertyListAtURL(CFURLRef propertyListURL)
96 {
97     RetainPtr<NSData> propertyListData = adoptNS([[NSData alloc] initWithContentsOfURL:(NSURL *)propertyListURL]);
98     if (!propertyListData)
99         return 0;
100
101     RetainPtr<CFPropertyListRef> propertyList = adoptCF(CFPropertyListCreateWithData(kCFAllocatorDefault, (CFDataRef)propertyListData.get(), kCFPropertyListImmutable, 0, 0));
102     if (!propertyList)
103         return 0;
104
105     if (CFGetTypeID(propertyList.get()) != CFDictionaryGetTypeID())
106         return 0;
107
108     return static_cast<CFDictionaryRef>(propertyList.get());
109 }
110
111 static RetainPtr<CFDictionaryRef> getMIMETypesFromPluginBundle(CFBundleRef bundle, const PluginModuleInfo& plugin)
112 {
113     CFStringRef propertyListFilename = static_cast<CFStringRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypesFilename")));
114     if (propertyListFilename) {
115         RetainPtr<CFStringRef> propertyListPath = adoptCF(CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("%@/Library/Preferences/%@"), NSHomeDirectory(), propertyListFilename));
116         RetainPtr<CFURLRef> propertyListURL = adoptCF(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, propertyListPath.get(), kCFURLPOSIXPathStyle, FALSE));
117
118         RetainPtr<CFDictionaryRef> propertyList = contentsOfPropertyListAtURL(propertyListURL.get());
119
120         if (!propertyList && PluginProcessProxy::createPropertyListFile(plugin))
121             propertyList = contentsOfPropertyListAtURL(propertyListURL.get());
122
123         if (!propertyList)
124             return 0;
125         
126         return static_cast<CFDictionaryRef>(CFDictionaryGetValue(propertyList.get(), CFSTR("WebPluginMIMETypes")));
127     }
128     
129     return static_cast<CFDictionaryRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypes")));
130 }
131
132 static bool getPluginInfoFromPropertyLists(CFBundleRef bundle, PluginModuleInfo& plugin)
133 {
134     RetainPtr<CFDictionaryRef> mimeTypes = getMIMETypesFromPluginBundle(bundle, plugin);
135     if (!mimeTypes || CFGetTypeID(mimeTypes.get()) != CFDictionaryGetTypeID())
136         return false;
137
138     // Get the plug-in name.
139     CFStringRef pluginName = static_cast<CFStringRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginName")));
140     if (pluginName && CFGetTypeID(pluginName) == CFStringGetTypeID())
141         plugin.info.name = pluginName;
142     
143     // Get the plug-in description.
144     CFStringRef pluginDescription = static_cast<CFStringRef>(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginDescription")));
145     if (pluginDescription && CFGetTypeID(pluginDescription) == CFStringGetTypeID())
146         plugin.info.desc = pluginDescription;
147     
148     // Get the MIME type mapping dictionary.
149     CFIndex numMimeTypes = CFDictionaryGetCount(mimeTypes.get());          
150     Vector<CFStringRef> mimeTypesVector(numMimeTypes);
151     Vector<CFDictionaryRef> mimeTypeInfoVector(numMimeTypes);
152     CFDictionaryGetKeysAndValues(mimeTypes.get(), reinterpret_cast<const void**>(mimeTypesVector.data()), reinterpret_cast<const void**>(mimeTypeInfoVector.data()));
153     
154     for (CFIndex i = 0; i < numMimeTypes; ++i) {
155         MimeClassInfo mimeClassInfo;
156         
157         // If this MIME type is invalid, ignore it.
158         CFStringRef mimeType = mimeTypesVector[i];
159         if (!mimeType || CFGetTypeID(mimeType) != CFStringGetTypeID() || CFStringGetLength(mimeType) == 0)
160             continue;
161
162         // If this MIME type doesn't have a valid info dictionary, ignore it.
163         CFDictionaryRef mimeTypeInfo = mimeTypeInfoVector[i];
164         if (!mimeTypeInfo || CFGetTypeID(mimeTypeInfo) != CFDictionaryGetTypeID())
165             continue;
166
167         // FIXME: Consider storing disabled MIME types.
168         CFTypeRef isEnabled = CFDictionaryGetValue(mimeTypeInfo, CFSTR("WebPluginTypeEnabled"));
169         if (isEnabled) {
170             if (CFGetTypeID(isEnabled) == CFNumberGetTypeID()) {
171                 int value;
172                 if (!CFNumberGetValue(static_cast<CFNumberRef>(isEnabled), kCFNumberIntType, &value) || !value)
173                     continue;
174             } else if (CFGetTypeID(isEnabled) == CFBooleanGetTypeID()) {
175                 if (!CFBooleanGetValue(static_cast<CFBooleanRef>(isEnabled)))
176                     continue;
177             } else
178                 continue;
179         }
180
181         // Get the MIME type description.
182         CFStringRef mimeTypeDescription = static_cast<CFStringRef>(CFDictionaryGetValue(mimeTypeInfo, CFSTR("WebPluginTypeDescription")));
183         if (mimeTypeDescription && CFGetTypeID(mimeTypeDescription) != CFStringGetTypeID())
184             mimeTypeDescription = 0;
185
186         mimeClassInfo.type = String(mimeType).lower();
187         mimeClassInfo.desc = mimeTypeDescription;
188
189         // Now get the extensions for this MIME type.
190         CFIndex numExtensions = 0;
191         CFArrayRef extensionsArray = static_cast<CFArrayRef>(CFDictionaryGetValue(mimeTypeInfo, CFSTR("WebPluginExtensions")));
192         if (extensionsArray && CFGetTypeID(extensionsArray) == CFArrayGetTypeID())
193             numExtensions = CFArrayGetCount(extensionsArray);
194
195         for (CFIndex i = 0; i < numExtensions; ++i) {
196             CFStringRef extension = static_cast<CFStringRef>(CFArrayGetValueAtIndex(extensionsArray, i));
197             if (!extension || CFGetTypeID(extension) != CFStringGetTypeID())
198                 continue;
199
200             // The DivX plug-in lists multiple extensions in a comma separated string instead of using
201             // multiple array elements in the property list. Work around this here by splitting the
202             // extension string into components.
203             Vector<String> extensionComponents;
204             String(extension).lower().split(',', extensionComponents);
205
206             for (size_t i = 0; i < extensionComponents.size(); ++i)
207                 mimeClassInfo.extensions.append(extensionComponents[i]);
208         }
209
210         // Add this MIME type.
211         plugin.info.mimes.append(mimeClassInfo);
212     }
213
214     return true;    
215 }
216
217 #pragma clang diagnostic push
218 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
219
220 class ResourceMap {
221 public:
222     explicit ResourceMap(CFBundleRef bundle)
223         : m_bundle(bundle)
224         , m_currentResourceFile(CurResFile())
225         , m_bundleResourceMap(CFBundleOpenBundleResourceMap(m_bundle))
226     {
227         UseResFile(m_bundleResourceMap);
228     }
229
230     ~ResourceMap()
231     {
232         // Close the resource map.
233         CFBundleCloseBundleResourceMap(m_bundle, m_bundleResourceMap);
234         
235         // And restore the old resource.
236         UseResFile(m_currentResourceFile);
237     }
238
239     bool isValid() const { return m_bundleResourceMap != -1; }
240
241 private:
242     CFBundleRef m_bundle;
243     ResFileRefNum m_currentResourceFile;
244     ResFileRefNum m_bundleResourceMap;
245 };
246
247 static bool getStringListResource(ResID resourceID, Vector<String>& stringList) {
248     Handle stringListHandle = Get1Resource('STR#', resourceID);
249     if (!stringListHandle || !*stringListHandle)
250         return false;
251
252     // Get the string list size.
253     Size stringListSize = GetHandleSize(stringListHandle);
254     if (stringListSize < static_cast<Size>(sizeof(UInt16)))
255         return false;
256
257     CFStringEncoding stringEncoding = stringEncodingForResource(stringListHandle);
258
259     unsigned char* ptr = reinterpret_cast<unsigned char*>(*stringListHandle);
260     unsigned char* end = ptr + stringListSize;
261     
262     // Get the number of strings in the string list.
263     UInt16 numStrings = *reinterpret_cast<UInt16*>(ptr);
264     ptr += sizeof(UInt16);
265
266     for (UInt16 i = 0; i < numStrings; ++i) {
267         // We're past the end of the string, bail.
268         if (ptr >= end)
269             return false;
270
271         // Get the string length.
272         unsigned char stringLength = *ptr++;
273
274         RetainPtr<CFStringRef> cfString = adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, ptr, stringLength, stringEncoding, false, kCFAllocatorNull));
275         if (!cfString.get())
276             return false;
277
278         stringList.append(cfString.get());
279         ptr += stringLength;
280     }
281
282     if (ptr != end)
283         return false;
284
285     return true;
286 }
287
288 #pragma clang diagnostic pop
289
290 static const ResID PluginNameOrDescriptionStringNumber = 126;
291 static const ResID MIMEDescriptionStringNumber = 127;
292 static const ResID MIMEListStringStringNumber = 128;
293
294 static bool getPluginInfoFromCarbonResources(CFBundleRef bundle, PluginModuleInfo& plugin)
295 {
296     ASSERT(RunLoop::isMain());
297
298     ResourceMap resourceMap(bundle);
299     if (!resourceMap.isValid())
300         return false;
301
302     // Get the description and name string list.
303     Vector<String> descriptionAndName;
304     if (!getStringListResource(PluginNameOrDescriptionStringNumber, descriptionAndName))
305         return false;
306
307     // Get the MIME types and extensions string list. This list needs to be a multiple of two.
308     Vector<String> mimeTypesAndExtensions;
309     if (!getStringListResource(MIMEListStringStringNumber, mimeTypesAndExtensions))
310         return false;
311
312     if (mimeTypesAndExtensions.size() % 2)
313         return false;
314
315     // Now get the MIME type descriptions string list. This string list needs to be the same length as the number of MIME types.
316     Vector<String> mimeTypeDescriptions;
317     if (!getStringListResource(MIMEDescriptionStringNumber, mimeTypeDescriptions))
318         return false;
319
320     // Add all MIME types.
321     for (size_t i = 0; i < mimeTypesAndExtensions.size() / 2; ++i) {
322         MimeClassInfo mimeClassInfo;
323         
324         const String& mimeType = mimeTypesAndExtensions[i * 2];
325         String description;
326         if (i < mimeTypeDescriptions.size())
327             description = mimeTypeDescriptions[i];
328         
329         mimeClassInfo.type = mimeType.lower();
330         mimeClassInfo.desc = description;
331         
332         Vector<String> extensions;
333         mimeTypesAndExtensions[i * 2 + 1].split(',', extensions);
334         
335         for (size_t i = 0; i < extensions.size(); ++i)
336             mimeClassInfo.extensions.append(extensions[i].lower());
337
338         plugin.info.mimes.append(mimeClassInfo);
339     }
340
341     // Set the description and name if they exist.
342     if (descriptionAndName.size() > 0)
343         plugin.info.desc = descriptionAndName[0];
344     if (descriptionAndName.size() > 1)
345         plugin.info.name = descriptionAndName[1];
346
347     return true;
348 }
349
350 bool NetscapePluginModule::getPluginInfo(const String& pluginPath, PluginModuleInfo& plugin)
351 {
352     RetainPtr<CFURLRef> bundleURL = adoptCF(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pluginPath.createCFString().get(), kCFURLPOSIXPathStyle, false));
353     
354     // Try to initialize the bundle.
355     RetainPtr<CFBundleRef> bundle = adoptCF(CFBundleCreate(kCFAllocatorDefault, bundleURL.get()));
356     if (!bundle)
357         return false;
358     
359     // Check if this bundle is an NPAPI plug-in.
360     UInt32 packageType = 0;
361     CFBundleGetPackageInfo(bundle.get(), &packageType, 0);
362     if (packageType != FOUR_CHAR_CODE('BRPL'))
363         return false;
364     
365     // Check that the architecture is valid.
366     if (!getPluginArchitecture(bundle.get(), plugin))
367         return false;
368
369     plugin.path = pluginPath;
370     plugin.bundleIdentifier = CFBundleGetIdentifier(bundle.get());
371     if (CFTypeRef versionTypeRef = CFBundleGetValueForInfoDictionaryKey(bundle.get(), kCFBundleVersionKey)) {
372         if (CFGetTypeID(versionTypeRef) == CFStringGetTypeID())
373             plugin.versionString = static_cast<CFStringRef>(versionTypeRef);
374     }
375
376     if (CFTypeRef shortVersionTypeRef = CFBundleGetValueForInfoDictionaryKey(bundle.get(), CFSTR("CFBundleShortVersionString"))) {
377         if (CFGetTypeID(shortVersionTypeRef) == CFStringGetTypeID())
378             plugin.shortVersionString = static_cast<CFStringRef>(shortVersionTypeRef);
379     }
380
381     if (CFTypeRef preferencePathTypeRef = CFBundleGetValueForInfoDictionaryKey(bundle.get(), CFSTR("WebPluginPreferencePanePath"))) {
382         if (CFGetTypeID(preferencePathTypeRef) == CFStringGetTypeID())
383             plugin.preferencePanePath = static_cast<CFStringRef>(preferencePathTypeRef);
384     }
385
386     // Check that there's valid info for this plug-in.
387     if (!getPluginInfoFromPropertyLists(bundle.get(), plugin) &&
388         !getPluginInfoFromCarbonResources(bundle.get(), plugin))
389         return false;
390
391     plugin.hasSandboxProfile = pluginHasSandboxProfile(plugin.bundleIdentifier);
392
393     RetainPtr<CFStringRef> filename = adoptCF(CFURLCopyLastPathComponent(bundleURL.get()));
394     plugin.info.file = filename.get();
395     
396     if (plugin.info.name.isNull())
397         plugin.info.name = plugin.info.file;
398     if (plugin.info.desc.isNull())
399         plugin.info.desc = plugin.info.file;
400
401     plugin.info.isApplicationPlugin = false;
402     plugin.info.clientLoadPolicy = PluginLoadClientPolicyUndefined;
403 #if PLATFORM(MAC)
404     plugin.info.bundleIdentifier = plugin.bundleIdentifier;
405     plugin.info.versionString = plugin.versionString;
406 #endif
407
408     return true;
409 }
410
411 bool NetscapePluginModule::createPluginMIMETypesPreferences(const String& pluginPath)
412 {
413     RetainPtr<CFURLRef> bundleURL = adoptCF(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pluginPath.createCFString().get(), kCFURLPOSIXPathStyle, false));
414     
415     RetainPtr<CFBundleRef> bundle = adoptCF(CFBundleCreate(kCFAllocatorDefault, bundleURL.get()));
416     if (!bundle)
417         return false;
418
419     if (!CFBundleLoadExecutable(bundle.get()))
420         return false;
421
422     void (*createPluginMIMETypesPreferences)(void) = reinterpret_cast<void (*)(void)>(CFBundleGetFunctionPointerForName(bundle.get(), CFSTR("BP_CreatePluginMIMETypesPreferences")));
423     if (!createPluginMIMETypesPreferences)
424         return false;
425
426     createPluginMIMETypesPreferences();
427     return true;
428 }
429
430 // FIXME: This doesn't need to be platform-specific.
431 class PluginVersion {
432 public:
433     static PluginVersion parse(const String& versionString);
434
435     bool isLessThan(unsigned componentA) const;
436     bool isValid() const { return !m_versionComponents.isEmpty(); }
437
438 private:
439     PluginVersion()
440     {
441     }
442
443     Vector<unsigned, 4> m_versionComponents;
444 };
445
446 PluginVersion PluginVersion::parse(const String& versionString)
447 {
448     PluginVersion version;
449
450     Vector<String> versionStringComponents;
451     versionString.split('.', versionStringComponents);
452     for (size_t i = 0; i < versionStringComponents.size(); ++i) {
453         bool successfullyParsed = false;
454         unsigned versionComponent = versionStringComponents[i].toUInt(&successfullyParsed);
455         if (!successfullyParsed)
456             return PluginVersion();
457
458         version.m_versionComponents.append(versionComponent);
459     }
460
461     return version;
462 }
463
464 bool PluginVersion::isLessThan(unsigned componentA) const
465 {
466     ASSERT(isValid());
467
468     return m_versionComponents[0] < componentA;
469 }
470
471 void NetscapePluginModule::determineQuirks()
472 {
473     PluginModuleInfo plugin;
474     if (!getPluginInfo(m_pluginPath, plugin))
475         return;
476
477     if (plugin.bundleIdentifier == "com.macromedia.Flash Player.plugin") {
478         // Flash requires that the return value of getprogname() be "WebKitPluginHost".
479         m_pluginQuirks.add(PluginQuirks::PrognameShouldBeWebKitPluginHost);
480
481         // Flash supports snapshotting.
482         m_pluginQuirks.add(PluginQuirks::SupportsSnapshotting);
483
484         // Flash returns a retained Core Animation layer.
485         m_pluginQuirks.add(PluginQuirks::ReturnsRetainedCoreAnimationLayer);
486
487         // Flash has a bug where NSExceptions can be released too early.
488         m_pluginQuirks.add(PluginQuirks::LeakAllThrownNSExceptions);
489     }
490
491     if (plugin.bundleIdentifier == "com.microsoft.SilverlightPlugin") {
492         // Silverlight doesn't explicitly opt into transparency, so we'll do it whenever
493         // there's a 'background' attribute that's set to a transparent color.
494         m_pluginQuirks.add(PluginQuirks::MakeOpaqueUnlessTransparentSilverlightBackgroundAttributeExists);
495
496         // Silverlight has a workaround for a leak in Safari 2. This workaround is
497         // applied when the user agent does not contain "Version/3" so we append it
498         // at the end of the user agent.
499         m_pluginQuirks.add(PluginQuirks::AppendVersion3UserAgent);
500
501         PluginVersion pluginVersion = PluginVersion::parse(plugin.versionString);
502         if (pluginVersion.isValid()) {
503             if (pluginVersion.isLessThan(4)) {
504                 // Versions of Silverlight prior to 4 don't retain the scriptable NPObject.
505                 m_pluginQuirks.add(PluginQuirks::ReturnsNonRetainedScriptableNPObject);
506             }
507         }
508     }
509
510     if (plugin.bundleIdentifier == "com.apple.ist.ds.appleconnect.webplugin") {
511         // <rdar://problem/8440903>: AppleConnect has a bug where it does not
512         // understand the parameter names specified in the <object> element that
513         // embeds its plug-in. 
514         m_pluginQuirks.add(PluginQuirks::WantsLowercaseParameterNames);
515
516 #ifndef NP_NO_QUICKDRAW
517         // The AppleConnect plug-in uses QuickDraw but doesn't paint or receive events
518         // so we'll allow it to be instantiated even though we don't support QuickDraw.
519         m_pluginQuirks.add(PluginQuirks::AllowHalfBakedQuickDrawSupport);
520 #endif
521     }
522
523 #ifndef NP_NO_QUICKDRAW
524     if (plugin.bundleIdentifier == "com.microsoft.sharepoint.browserplugin") {
525         // The Microsoft SharePoint plug-in uses QuickDraw but doesn't paint or receive events
526         // so we'll allow it to be instantiated even though we don't support QuickDraw.
527         m_pluginQuirks.add(PluginQuirks::AllowHalfBakedQuickDrawSupport);
528     }
529
530     if (plugin.bundleIdentifier == "com.jattesaker.macid2.NPPlugin") {
531         // The BankID plug-in uses QuickDraw but doesn't paint or receive events
532         // so we'll allow it to be instantiated even though we don't support QuickDraw.
533         m_pluginQuirks.add(PluginQuirks::AllowHalfBakedQuickDrawSupport);
534     }
535 #endif
536
537     if (plugin.bundleIdentifier == "com.adobe.acrobat.pdfviewerNPAPI") {
538         // The Adobe Reader plug-in wants wheel events.
539         m_pluginQuirks.add(PluginQuirks::WantsWheelEvents);
540     }
541 }
542
543 } // namespace WebKit
544
545 #endif // ENABLE(NETSCAPE_PLUGIN_API)