| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #import <Carbon/Carbon.h> | |
| 6 | |
| 7 #include "webkit/glue/plugins/plugin_lib.h" | |
| 8 | |
| 9 #include "base/mac/scoped_cftyperef.h" | |
| 10 #include "base/native_library.h" | |
| 11 #include "base/scoped_ptr.h" | |
| 12 #include "base/string_split.h" | |
| 13 #include "base/string_util.h" | |
| 14 #include "base/sys_string_conversions.h" | |
| 15 #include "base/utf_string_conversions.h" | |
| 16 #include "webkit/glue/plugins/plugin_list.h" | |
| 17 | |
| 18 static const short kSTRTypeDefinitionResourceID = 128; | |
| 19 static const short kSTRTypeDescriptionResourceID = 127; | |
| 20 static const short kSTRPluginDescriptionResourceID = 126; | |
| 21 | |
| 22 using base::mac::ScopedCFTypeRef; | |
| 23 | |
| 24 namespace NPAPI { | |
| 25 | |
| 26 namespace { | |
| 27 | |
| 28 NSDictionary* GetMIMETypes(CFBundleRef bundle) { | |
| 29 NSString* mime_filename = | |
| 30 (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, | |
| 31 CFSTR("WebPluginMIMETypesFilename")); | |
| 32 | |
| 33 if (mime_filename) { | |
| 34 | |
| 35 // get the file | |
| 36 | |
| 37 NSString* mime_path = | |
| 38 [NSString stringWithFormat:@"%@/Library/Preferences/%@", | |
| 39 NSHomeDirectory(), mime_filename]; | |
| 40 NSDictionary* mime_file_dict = | |
| 41 [NSDictionary dictionaryWithContentsOfFile:mime_path]; | |
| 42 | |
| 43 // is it valid? | |
| 44 | |
| 45 bool valid_file = false; | |
| 46 if (mime_file_dict) { | |
| 47 NSString* l10n_name = | |
| 48 [mime_file_dict objectForKey:@"WebPluginLocalizationName"]; | |
| 49 NSString* preferred_l10n = [[NSLocale currentLocale] localeIdentifier]; | |
| 50 if ([l10n_name isEqualToString:preferred_l10n]) | |
| 51 valid_file = true; | |
| 52 } | |
| 53 | |
| 54 if (valid_file) | |
| 55 return [mime_file_dict objectForKey:@"WebPluginMIMETypes"]; | |
| 56 | |
| 57 // dammit, I didn't want to have to do this | |
| 58 | |
| 59 typedef void (*CreateMIMETypesPrefsPtr)(void); | |
| 60 CreateMIMETypesPrefsPtr create_prefs_file = | |
| 61 (CreateMIMETypesPrefsPtr)CFBundleGetFunctionPointerForName( | |
| 62 bundle, CFSTR("BP_CreatePluginMIMETypesPreferences")); | |
| 63 if (!create_prefs_file) | |
| 64 return nil; | |
| 65 create_prefs_file(); | |
| 66 | |
| 67 // one more time | |
| 68 | |
| 69 mime_file_dict = [NSDictionary dictionaryWithContentsOfFile:mime_path]; | |
| 70 if (mime_file_dict) | |
| 71 return [mime_file_dict objectForKey:@"WebPluginMIMETypes"]; | |
| 72 else | |
| 73 return nil; | |
| 74 | |
| 75 } else { | |
| 76 return (NSDictionary*)CFBundleGetValueForInfoDictionaryKey(bundle, | |
| 77 CFSTR("WebPluginMIMETypes")); | |
| 78 } | |
| 79 } | |
| 80 | |
| 81 bool ReadPlistPluginInfo(const FilePath& filename, CFBundleRef bundle, | |
| 82 WebPluginInfo* info) { | |
| 83 NSDictionary* mime_types = GetMIMETypes(bundle); | |
| 84 if (!mime_types) | |
| 85 return false; // no type info here; try elsewhere | |
| 86 | |
| 87 for (NSString* mime_type in [mime_types allKeys]) { | |
| 88 NSDictionary* mime_dict = [mime_types objectForKey:mime_type]; | |
| 89 NSString* mime_desc = [mime_dict objectForKey:@"WebPluginTypeDescription"]; | |
| 90 NSArray* mime_exts = [mime_dict objectForKey:@"WebPluginExtensions"]; | |
| 91 | |
| 92 WebPluginMimeType mime; | |
| 93 mime.mime_type = base::SysNSStringToUTF8([mime_type lowercaseString]); | |
| 94 // Remove PDF from the list of types handled by QuickTime, since it provides | |
| 95 // a worse experience than just downloading the PDF. | |
| 96 if (mime.mime_type == "application/pdf" && | |
| 97 StartsWithASCII(filename.BaseName().value(), "QuickTime", false)) { | |
| 98 continue; | |
| 99 } | |
| 100 | |
| 101 if (mime_desc) | |
| 102 mime.description = base::SysNSStringToUTF16(mime_desc); | |
| 103 for (NSString* ext in mime_exts) | |
| 104 mime.file_extensions.push_back( | |
| 105 base::SysNSStringToUTF8([ext lowercaseString])); | |
| 106 | |
| 107 info->mime_types.push_back(mime); | |
| 108 } | |
| 109 | |
| 110 NSString* plugin_name = | |
| 111 (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, | |
| 112 CFSTR("WebPluginName")); | |
| 113 NSString* plugin_vers = | |
| 114 (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, | |
| 115 CFSTR("CFBundleShortVersionString")); | |
| 116 NSString* plugin_desc = | |
| 117 (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, | |
| 118 CFSTR("WebPluginDescription")); | |
| 119 | |
| 120 if (plugin_name) | |
| 121 info->name = base::SysNSStringToUTF16(plugin_name); | |
| 122 else | |
| 123 info->name = UTF8ToUTF16(filename.BaseName().value()); | |
| 124 info->path = filename; | |
| 125 if (plugin_vers) | |
| 126 info->version = base::SysNSStringToUTF16(plugin_vers); | |
| 127 if (plugin_desc) | |
| 128 info->desc = base::SysNSStringToUTF16(plugin_desc); | |
| 129 else | |
| 130 info->desc = UTF8ToUTF16(filename.BaseName().value()); | |
| 131 info->enabled = true; | |
| 132 | |
| 133 return true; | |
| 134 } | |
| 135 | |
| 136 class ScopedBundleResourceFile { | |
| 137 public: | |
| 138 ScopedBundleResourceFile(CFBundleRef bundle) : bundle_(bundle) { | |
| 139 old_ref_num_ = CurResFile(); | |
| 140 bundle_ref_num_ = CFBundleOpenBundleResourceMap(bundle); | |
| 141 UseResFile(bundle_ref_num_); | |
| 142 } | |
| 143 ~ScopedBundleResourceFile() { | |
| 144 UseResFile(old_ref_num_); | |
| 145 CFBundleCloseBundleResourceMap(bundle_, bundle_ref_num_); | |
| 146 } | |
| 147 | |
| 148 private: | |
| 149 CFBundleRef bundle_; | |
| 150 CFBundleRefNum bundle_ref_num_; | |
| 151 ResFileRefNum old_ref_num_; | |
| 152 }; | |
| 153 | |
| 154 bool GetSTRResource(CFBundleRef bundle, short res_id, | |
| 155 std::vector<std::string>* contents) { | |
| 156 Handle res_handle = Get1Resource('STR#', res_id); | |
| 157 if (!res_handle || !*res_handle) | |
| 158 return false; | |
| 159 | |
| 160 char* pointer = *res_handle; | |
| 161 short num_strings = *(short*)pointer; | |
| 162 pointer += sizeof(short); | |
| 163 for (short i = 0; i < num_strings; ++i) { | |
| 164 // Despite being 8-bits wide, these are legacy encoded. Make a round trip. | |
| 165 ScopedCFTypeRef<CFStringRef> str(CFStringCreateWithPascalStringNoCopy( | |
| 166 kCFAllocatorDefault, | |
| 167 (unsigned char*)pointer, | |
| 168 GetApplicationTextEncoding(), // is this right? | |
| 169 kCFAllocatorNull)); // perhaps CFStringGetSystemEncoding? | |
| 170 if (!str.get()) | |
| 171 return false; | |
| 172 contents->push_back(base::SysCFStringRefToUTF8(str.get())); | |
| 173 pointer += 1+*reinterpret_cast<unsigned char*>(pointer); | |
| 174 } | |
| 175 | |
| 176 return true; | |
| 177 } | |
| 178 | |
| 179 bool ReadSTRPluginInfo(const FilePath& filename, CFBundleRef bundle, | |
| 180 WebPluginInfo* info) { | |
| 181 ScopedBundleResourceFile res_file(bundle); | |
| 182 | |
| 183 std::vector<std::string> type_strings; | |
| 184 if (!GetSTRResource(bundle, kSTRTypeDefinitionResourceID, &type_strings)) | |
| 185 return false; | |
| 186 | |
| 187 std::vector<std::string> type_descs; | |
| 188 bool have_type_descs = GetSTRResource(bundle, | |
| 189 kSTRTypeDescriptionResourceID, | |
| 190 &type_descs); | |
| 191 | |
| 192 std::vector<std::string> plugin_descs; | |
| 193 bool have_plugin_descs = GetSTRResource(bundle, | |
| 194 kSTRPluginDescriptionResourceID, | |
| 195 &plugin_descs); | |
| 196 | |
| 197 size_t num_types = type_strings.size()/2; | |
| 198 | |
| 199 for (size_t i = 0; i < num_types; ++i) { | |
| 200 WebPluginMimeType mime; | |
| 201 mime.mime_type = StringToLowerASCII(type_strings[2*i]); | |
| 202 if (have_type_descs && i < type_descs.size()) | |
| 203 mime.description = UTF8ToUTF16(type_descs[i]); | |
| 204 base::SplitString( | |
| 205 StringToLowerASCII(type_strings[2*i+1]), ',', &mime.file_extensions); | |
| 206 | |
| 207 info->mime_types.push_back(mime); | |
| 208 } | |
| 209 | |
| 210 NSString* plugin_vers = | |
| 211 (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, | |
| 212 CFSTR("CFBundleShortVersionString")); | |
| 213 | |
| 214 if (have_plugin_descs && plugin_descs.size() > 1) | |
| 215 info->name = UTF8ToUTF16(plugin_descs[1]); | |
| 216 else | |
| 217 info->name = UTF8ToUTF16(filename.BaseName().value()); | |
| 218 info->path = filename; | |
| 219 if (plugin_vers) | |
| 220 info->version = base::SysNSStringToUTF16(plugin_vers); | |
| 221 if (have_plugin_descs && plugin_descs.size() > 0) | |
| 222 info->desc = UTF8ToUTF16(plugin_descs[0]); | |
| 223 else | |
| 224 info->desc = UTF8ToUTF16(filename.BaseName().value()); | |
| 225 info->enabled = true; | |
| 226 | |
| 227 return true; | |
| 228 } | |
| 229 | |
| 230 } // anonymous namespace | |
| 231 | |
| 232 bool PluginLib::ReadWebPluginInfo(const FilePath &filename, | |
| 233 WebPluginInfo* info) { | |
| 234 // There are two ways to get information about plugin capabilities. One is an | |
| 235 // Info.plist set of keys, documented at | |
| 236 // http://developer.apple.com/documentation/InternetWeb/Conceptual/WebKit_Plug
inProgTopic/Concepts/AboutPlugins.html . | |
| 237 // The other is a set of STR# resources, documented at | |
| 238 // https://developer.mozilla.org/En/Gecko_Plugin_API_Reference/Plug-in_Develop
ment_Overview . | |
| 239 // | |
| 240 // Historically, the data was maintained in the STR# resources. Apple, with | |
| 241 // the introduction of WebKit, noted the weaknesses of resources and moved the | |
| 242 // information into the Info.plist. Mozilla had always supported a | |
| 243 // NP_GetMIMEDescription() entry point for Unix plugins and also supports it | |
| 244 // on the Mac to supplement the STR# format. WebKit does not support | |
| 245 // NP_GetMIMEDescription() and neither do we. (That entry point is documented | |
| 246 // at https://developer.mozilla.org/en/NP_GetMIMEDescription .) We prefer the | |
| 247 // Info.plist format because it's more extensible and has a defined encoding, | |
| 248 // but will fall back to the STR# format of the data if it is not present in | |
| 249 // the Info.plist. | |
| 250 // | |
| 251 // The parsing of the data lives in the two functions ReadSTRPluginInfo() and | |
| 252 // ReadPlistPluginInfo(), but a summary of the formats follows. | |
| 253 // | |
| 254 // Each data type handled by a plugin has several properties: | |
| 255 // - <<type0mimetype>> | |
| 256 // - <<type0fileextension0>>..<<type0fileextensionk>> | |
| 257 // - <<type0description>> | |
| 258 // | |
| 259 // Each plugin may have any number of types defined. In addition, the plugin | |
| 260 // itself has properties: | |
| 261 // - <<plugindescription>> | |
| 262 // - <<pluginname>> | |
| 263 // | |
| 264 // For the Info.plist version, the data is formatted as follows (in text plist | |
| 265 // format): | |
| 266 // { | |
| 267 // ... the usual plist keys ... | |
| 268 // WebPluginDescription = <<plugindescription>>; | |
| 269 // WebPluginMIMETypes = { | |
| 270 // <<type0mimetype>> = { | |
| 271 // WebPluginExtensions = ( | |
| 272 // <<type0fileextension0>>, | |
| 273 // ... | |
| 274 // <<type0fileextensionk>>, | |
| 275 // ); | |
| 276 // WebPluginTypeDescription = <<type0description>>; | |
| 277 // }; | |
| 278 // <<type1mimetype>> = { ... }; | |
| 279 // ... | |
| 280 // <<typenmimetype>> = { ... }; | |
| 281 // }; | |
| 282 // WebPluginName = <<pluginname>>; | |
| 283 // } | |
| 284 // | |
| 285 // Alternatively (and this is undocumented), rather than a WebPluginMIMETypes | |
| 286 // key, there may be a WebPluginMIMETypesFilename key. If it is present, then | |
| 287 // it is the name of a file in the user's preferences folder in which to find | |
| 288 // the WebPluginMIMETypes key. If the key is present but the file doesn't | |
| 289 // exist, we must load the plugin and call a specific function to have the | |
| 290 // plugin create the file. | |
| 291 // | |
| 292 // If we do not find those keys in the Info.plist, we fall back to the STR# | |
| 293 // resources. In them, the data is formatted as follows: | |
| 294 // STR# 128 | |
| 295 // (1) <<type0mimetype>> | |
| 296 // (2) <<type0fileextension0>>,...,<<type0fileextensionk>> | |
| 297 // (3) <<type1mimetype>> | |
| 298 // (4) <<type1fileextension0>>,...,<<type1fileextensionk>> | |
| 299 // (...) | |
| 300 // (2n+1) <<typenmimetype>> | |
| 301 // (2n+2) <<typenfileextension0>>,...,<<typenfileextensionk>> | |
| 302 // STR# 127 | |
| 303 // (1) <<type0description>> | |
| 304 // (2) <<type1description>> | |
| 305 // (...) | |
| 306 // (n+1) <<typendescription>> | |
| 307 // STR# 126 | |
| 308 // (1) <<plugindescription>> | |
| 309 // (2) <<pluginname>> | |
| 310 // | |
| 311 // Strictly speaking, only STR# 128 is required. | |
| 312 | |
| 313 ScopedCFTypeRef<CFURLRef> bundle_url(CFURLCreateFromFileSystemRepresentation( | |
| 314 kCFAllocatorDefault, (const UInt8*)filename.value().c_str(), | |
| 315 filename.value().length(), true)); | |
| 316 if (!bundle_url) | |
| 317 return false; | |
| 318 ScopedCFTypeRef<CFBundleRef> bundle(CFBundleCreate(kCFAllocatorDefault, | |
| 319 bundle_url.get())); | |
| 320 if (!bundle) | |
| 321 return false; | |
| 322 | |
| 323 // preflight | |
| 324 | |
| 325 OSType type = 0; | |
| 326 CFBundleGetPackageInfo(bundle.get(), &type, NULL); | |
| 327 if (type != FOUR_CHAR_CODE('BRPL')) | |
| 328 return false; | |
| 329 | |
| 330 CFErrorRef error; | |
| 331 Boolean would_load = CFBundlePreflightExecutable(bundle.get(), &error); | |
| 332 if (!would_load) | |
| 333 return false; | |
| 334 | |
| 335 // get the info | |
| 336 | |
| 337 if (ReadPlistPluginInfo(filename, bundle.get(), info)) | |
| 338 return true; | |
| 339 | |
| 340 if (ReadSTRPluginInfo(filename, bundle.get(), info)) | |
| 341 return true; | |
| 342 | |
| 343 // ... or not | |
| 344 | |
| 345 return false; | |
| 346 } | |
| 347 | |
| 348 } // namespace NPAPI | |
| OLD | NEW |