summaryrefslogtreecommitdiffstats
path: root/webkit/glue/plugins/plugin_lib_mac.mm
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/glue/plugins/plugin_lib_mac.mm')
-rw-r--r--webkit/glue/plugins/plugin_lib_mac.mm348
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