Home | History | Annotate | Download | only in mac
      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     // 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))
    279         return false;
    280 
    281     // Add all MIME types.
    282     for (size_t i = 0; i < mimeTypesAndExtensions.size() / 2; ++i) {
    283         MimeClassInfo mimeClassInfo;
    284 
    285         const String& mimeType = mimeTypesAndExtensions[i * 2];
    286         String description;
    287         if (i < mimeTypeDescriptions.size())
    288             description = mimeTypeDescriptions[i];
    289 
    290         mimeClassInfo.type = mimeType.lower();
    291         mimeClassInfo.desc = description;
    292 
    293         Vector<String> extensions;
    294         mimeTypesAndExtensions[i * 2 + 1].split(',', extensions);
    295 
    296         for (size_t i = 0; i < extensions.size(); ++i)
    297             mimeClassInfo.extensions.append(extensions[i].lower());
    298 
    299         pluginInfo.mimes.append(mimeClassInfo);
    300     }
    301 
    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];
    307 
    308     return true;
    309 }
    310 
    311 bool NetscapePluginModule::getPluginInfo(const String& pluginPath, PluginInfoStore::Plugin& plugin)
    312 {
    313     RetainPtr<CFStringRef> bundlePath(AdoptCF, pluginPath.createCFString());
    314     RetainPtr<CFURLRef> bundleURL(AdoptCF, CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundlePath.get(), kCFURLPOSIXPathStyle, false));
    315 
    316     // Try to initialize the bundle.
    317     RetainPtr<CFBundleRef> bundle(AdoptCF, CFBundleCreate(kCFAllocatorDefault, bundleURL.get()));
    318     if (!bundle)
    319         return false;
    320 
    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'))
    325         return false;
    326 
    327     // Check that the architecture is valid.
    328     cpu_type_t pluginArchitecture = 0;
    329     if (!getPluginArchitecture(bundle.get(), pluginArchitecture))
    330         return false;
    331 
    332     // Check that there's valid info for this plug-in.
    333     if (!getPluginInfoFromPropertyLists(bundle.get(), plugin.info) &&
    334         !getPluginInfoFromCarbonResources(bundle.get(), plugin.info))
    335         return false;
    336 
    337     plugin.path = pluginPath;
    338     plugin.pluginArchitecture = pluginArchitecture;
    339     plugin.bundleIdentifier = CFBundleGetIdentifier(bundle.get());
    340     plugin.versionNumber = CFBundleGetVersionNumber(bundle.get());
    341 
    342     RetainPtr<CFStringRef> filename(AdoptCF, CFURLCopyLastPathComponent(bundleURL.get()));
    343     plugin.info.file = filename.get();
    344 
    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;
    349 
    350     return true;
    351 }
    352 
    353 void NetscapePluginModule::determineQuirks()
    354 {
    355     PluginInfoStore::Plugin plugin;
    356     if (!getPluginInfo(m_pluginPath, plugin))
    357         return;
    358 
    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);
    362 
    363         // Flash supports snapshotting.
    364         m_pluginQuirks.add(PluginQuirks::SupportsSnapshotting);
    365     }
    366 
    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);
    371     }
    372 
    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);
    378     }
    379 #endif
    380 }
    381 
    382 } // namespace WebKit
    383