diff options
Diffstat (limited to 'webkit/glue/plugins/plugin_lib_mac.mm')
-rw-r--r-- | webkit/glue/plugins/plugin_lib_mac.mm | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/webkit/glue/plugins/plugin_lib_mac.mm b/webkit/glue/plugins/plugin_lib_mac.mm new file mode 100644 index 0000000..89444c8 --- /dev/null +++ b/webkit/glue/plugins/plugin_lib_mac.mm @@ -0,0 +1,348 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import <Carbon/Carbon.h> + +#include "webkit/glue/plugins/plugin_lib.h" + +#include "base/mac/scoped_cftyperef.h" +#include "base/native_library.h" +#include "base/scoped_ptr.h" +#include "base/string_split.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "base/utf_string_conversions.h" +#include "webkit/glue/plugins/plugin_list.h" + +static const short kSTRTypeDefinitionResourceID = 128; +static const short kSTRTypeDescriptionResourceID = 127; +static const short kSTRPluginDescriptionResourceID = 126; + +using base::mac::ScopedCFTypeRef; + +namespace NPAPI { + +namespace { + +NSDictionary* GetMIMETypes(CFBundleRef bundle) { + NSString* mime_filename = + (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, + CFSTR("WebPluginMIMETypesFilename")); + + if (mime_filename) { + + // get the file + + NSString* mime_path = + [NSString stringWithFormat:@"%@/Library/Preferences/%@", + NSHomeDirectory(), mime_filename]; + NSDictionary* mime_file_dict = + [NSDictionary dictionaryWithContentsOfFile:mime_path]; + + // is it valid? + + bool valid_file = false; + if (mime_file_dict) { + NSString* l10n_name = + [mime_file_dict objectForKey:@"WebPluginLocalizationName"]; + NSString* preferred_l10n = [[NSLocale currentLocale] localeIdentifier]; + if ([l10n_name isEqualToString:preferred_l10n]) + valid_file = true; + } + + if (valid_file) + return [mime_file_dict objectForKey:@"WebPluginMIMETypes"]; + + // dammit, I didn't want to have to do this + + typedef void (*CreateMIMETypesPrefsPtr)(void); + CreateMIMETypesPrefsPtr create_prefs_file = + (CreateMIMETypesPrefsPtr)CFBundleGetFunctionPointerForName( + bundle, CFSTR("BP_CreatePluginMIMETypesPreferences")); + if (!create_prefs_file) + return nil; + create_prefs_file(); + + // one more time + + mime_file_dict = [NSDictionary dictionaryWithContentsOfFile:mime_path]; + if (mime_file_dict) + return [mime_file_dict objectForKey:@"WebPluginMIMETypes"]; + else + return nil; + + } else { + return (NSDictionary*)CFBundleGetValueForInfoDictionaryKey(bundle, + CFSTR("WebPluginMIMETypes")); + } +} + +bool ReadPlistPluginInfo(const FilePath& filename, CFBundleRef bundle, + WebPluginInfo* info) { + NSDictionary* mime_types = GetMIMETypes(bundle); + if (!mime_types) + return false; // no type info here; try elsewhere + + for (NSString* mime_type in [mime_types allKeys]) { + NSDictionary* mime_dict = [mime_types objectForKey:mime_type]; + NSString* mime_desc = [mime_dict objectForKey:@"WebPluginTypeDescription"]; + NSArray* mime_exts = [mime_dict objectForKey:@"WebPluginExtensions"]; + + WebPluginMimeType mime; + mime.mime_type = base::SysNSStringToUTF8([mime_type lowercaseString]); + // Remove PDF from the list of types handled by QuickTime, since it provides + // a worse experience than just downloading the PDF. + if (mime.mime_type == "application/pdf" && + StartsWithASCII(filename.BaseName().value(), "QuickTime", false)) { + continue; + } + + if (mime_desc) + mime.description = base::SysNSStringToUTF16(mime_desc); + for (NSString* ext in mime_exts) + mime.file_extensions.push_back( + base::SysNSStringToUTF8([ext lowercaseString])); + + info->mime_types.push_back(mime); + } + + NSString* plugin_name = + (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, + CFSTR("WebPluginName")); + NSString* plugin_vers = + (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, + CFSTR("CFBundleShortVersionString")); + NSString* plugin_desc = + (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, + CFSTR("WebPluginDescription")); + + if (plugin_name) + info->name = base::SysNSStringToUTF16(plugin_name); + else + info->name = UTF8ToUTF16(filename.BaseName().value()); + info->path = filename; + if (plugin_vers) + info->version = base::SysNSStringToUTF16(plugin_vers); + if (plugin_desc) + info->desc = base::SysNSStringToUTF16(plugin_desc); + else + info->desc = UTF8ToUTF16(filename.BaseName().value()); + info->enabled = true; + + return true; +} + +class ScopedBundleResourceFile { + public: + ScopedBundleResourceFile(CFBundleRef bundle) : bundle_(bundle) { + old_ref_num_ = CurResFile(); + bundle_ref_num_ = CFBundleOpenBundleResourceMap(bundle); + UseResFile(bundle_ref_num_); + } + ~ScopedBundleResourceFile() { + UseResFile(old_ref_num_); + CFBundleCloseBundleResourceMap(bundle_, bundle_ref_num_); + } + + private: + CFBundleRef bundle_; + CFBundleRefNum bundle_ref_num_; + ResFileRefNum old_ref_num_; +}; + +bool GetSTRResource(CFBundleRef bundle, short res_id, + std::vector<std::string>* contents) { + Handle res_handle = Get1Resource('STR#', res_id); + if (!res_handle || !*res_handle) + return false; + + char* pointer = *res_handle; + short num_strings = *(short*)pointer; + pointer += sizeof(short); + for (short i = 0; i < num_strings; ++i) { + // Despite being 8-bits wide, these are legacy encoded. Make a round trip. + ScopedCFTypeRef<CFStringRef> str(CFStringCreateWithPascalStringNoCopy( + kCFAllocatorDefault, + (unsigned char*)pointer, + GetApplicationTextEncoding(), // is this right? + kCFAllocatorNull)); // perhaps CFStringGetSystemEncoding? + if (!str.get()) + return false; + contents->push_back(base::SysCFStringRefToUTF8(str.get())); + pointer += 1+*reinterpret_cast<unsigned char*>(pointer); + } + + return true; +} + +bool ReadSTRPluginInfo(const FilePath& filename, CFBundleRef bundle, + WebPluginInfo* info) { + ScopedBundleResourceFile res_file(bundle); + + std::vector<std::string> type_strings; + if (!GetSTRResource(bundle, kSTRTypeDefinitionResourceID, &type_strings)) + return false; + + std::vector<std::string> type_descs; + bool have_type_descs = GetSTRResource(bundle, + kSTRTypeDescriptionResourceID, + &type_descs); + + std::vector<std::string> plugin_descs; + bool have_plugin_descs = GetSTRResource(bundle, + kSTRPluginDescriptionResourceID, + &plugin_descs); + + size_t num_types = type_strings.size()/2; + + for (size_t i = 0; i < num_types; ++i) { + WebPluginMimeType mime; + mime.mime_type = StringToLowerASCII(type_strings[2*i]); + if (have_type_descs && i < type_descs.size()) + mime.description = UTF8ToUTF16(type_descs[i]); + base::SplitString( + StringToLowerASCII(type_strings[2*i+1]), ',', &mime.file_extensions); + + info->mime_types.push_back(mime); + } + + NSString* plugin_vers = + (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, + CFSTR("CFBundleShortVersionString")); + + if (have_plugin_descs && plugin_descs.size() > 1) + info->name = UTF8ToUTF16(plugin_descs[1]); + else + info->name = UTF8ToUTF16(filename.BaseName().value()); + info->path = filename; + if (plugin_vers) + info->version = base::SysNSStringToUTF16(plugin_vers); + if (have_plugin_descs && plugin_descs.size() > 0) + info->desc = UTF8ToUTF16(plugin_descs[0]); + else + info->desc = UTF8ToUTF16(filename.BaseName().value()); + info->enabled = true; + + return true; +} + +} // anonymous namespace + +bool PluginLib::ReadWebPluginInfo(const FilePath &filename, + WebPluginInfo* info) { + // There are two ways to get information about plugin capabilities. One is an + // Info.plist set of keys, documented at + // http://developer.apple.com/documentation/InternetWeb/Conceptual/WebKit_PluginProgTopic/Concepts/AboutPlugins.html . + // The other is a set of STR# resources, documented at + // https://developer.mozilla.org/En/Gecko_Plugin_API_Reference/Plug-in_Development_Overview . + // + // Historically, the data was maintained in the STR# resources. Apple, with + // the introduction of WebKit, noted the weaknesses of resources and moved the + // information into the Info.plist. Mozilla had always supported a + // NP_GetMIMEDescription() entry point for Unix plugins and also supports it + // on the Mac to supplement the STR# format. WebKit does not support + // NP_GetMIMEDescription() and neither do we. (That entry point is documented + // at https://developer.mozilla.org/en/NP_GetMIMEDescription .) We prefer the + // Info.plist format because it's more extensible and has a defined encoding, + // but will fall back to the STR# format of the data if it is not present in + // the Info.plist. + // + // The parsing of the data lives in the two functions ReadSTRPluginInfo() and + // ReadPlistPluginInfo(), but a summary of the formats follows. + // + // Each data type handled by a plugin has several properties: + // - <<type0mimetype>> + // - <<type0fileextension0>>..<<type0fileextensionk>> + // - <<type0description>> + // + // Each plugin may have any number of types defined. In addition, the plugin + // itself has properties: + // - <<plugindescription>> + // - <<pluginname>> + // + // For the Info.plist version, the data is formatted as follows (in text plist + // format): + // { + // ... the usual plist keys ... + // WebPluginDescription = <<plugindescription>>; + // WebPluginMIMETypes = { + // <<type0mimetype>> = { + // WebPluginExtensions = ( + // <<type0fileextension0>>, + // ... + // <<type0fileextensionk>>, + // ); + // WebPluginTypeDescription = <<type0description>>; + // }; + // <<type1mimetype>> = { ... }; + // ... + // <<typenmimetype>> = { ... }; + // }; + // WebPluginName = <<pluginname>>; + // } + // + // Alternatively (and this is undocumented), rather than a WebPluginMIMETypes + // key, there may be a WebPluginMIMETypesFilename key. If it is present, then + // it is the name of a file in the user's preferences folder in which to find + // the WebPluginMIMETypes key. If the key is present but the file doesn't + // exist, we must load the plugin and call a specific function to have the + // plugin create the file. + // + // If we do not find those keys in the Info.plist, we fall back to the STR# + // resources. In them, the data is formatted as follows: + // STR# 128 + // (1) <<type0mimetype>> + // (2) <<type0fileextension0>>,...,<<type0fileextensionk>> + // (3) <<type1mimetype>> + // (4) <<type1fileextension0>>,...,<<type1fileextensionk>> + // (...) + // (2n+1) <<typenmimetype>> + // (2n+2) <<typenfileextension0>>,...,<<typenfileextensionk>> + // STR# 127 + // (1) <<type0description>> + // (2) <<type1description>> + // (...) + // (n+1) <<typendescription>> + // STR# 126 + // (1) <<plugindescription>> + // (2) <<pluginname>> + // + // Strictly speaking, only STR# 128 is required. + + ScopedCFTypeRef<CFURLRef> bundle_url(CFURLCreateFromFileSystemRepresentation( + kCFAllocatorDefault, (const UInt8*)filename.value().c_str(), + filename.value().length(), true)); + if (!bundle_url) + return false; + ScopedCFTypeRef<CFBundleRef> bundle(CFBundleCreate(kCFAllocatorDefault, + bundle_url.get())); + if (!bundle) + return false; + + // preflight + + OSType type = 0; + CFBundleGetPackageInfo(bundle.get(), &type, NULL); + if (type != FOUR_CHAR_CODE('BRPL')) + return false; + + CFErrorRef error; + Boolean would_load = CFBundlePreflightExecutable(bundle.get(), &error); + if (!would_load) + return false; + + // get the info + + if (ReadPlistPluginInfo(filename, bundle.get(), info)) + return true; + + if (ReadSTRPluginInfo(filename, bundle.get(), info)) + return true; + + // ... or not + + return false; +} + +} // namespace NPAPI |