| OLD | NEW | 
 | (Empty) | 
|    1 // Copyright (c) 2012 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 #include "content/common/plugin_list.h" |  | 
|    6  |  | 
|    7 #import <Carbon/Carbon.h> |  | 
|    8 #import <Foundation/Foundation.h> |  | 
|    9  |  | 
|   10 #include "base/files/file_enumerator.h" |  | 
|   11 #include "base/files/file_util.h" |  | 
|   12 #include "base/mac/foundation_util.h" |  | 
|   13 #include "base/mac/scoped_cftyperef.h" |  | 
|   14 #include "base/memory/scoped_ptr.h" |  | 
|   15 #include "base/native_library.h" |  | 
|   16 #include "base/strings/string_number_conversions.h" |  | 
|   17 #include "base/strings/string_split.h" |  | 
|   18 #include "base/strings/string_util.h" |  | 
|   19 #include "base/strings/sys_string_conversions.h" |  | 
|   20 #include "base/strings/utf_string_conversions.h" |  | 
|   21  |  | 
|   22 using base::ScopedCFTypeRef; |  | 
|   23  |  | 
|   24 namespace content { |  | 
|   25  |  | 
|   26 namespace { |  | 
|   27  |  | 
|   28 void GetPluginCommonDirectories(std::vector<base::FilePath>* plugin_dirs) { |  | 
|   29   // This once used ::FSFindFolder() and kInternetPlugInFolderType, which is |  | 
|   30   // deprecated since 10.8. There is no NSSearchPathDirectory replacement for |  | 
|   31   // kInternetPlugInFolderType, so append a hard-coded value to [~]/Library. |  | 
|   32   // Interestingly, Safari also hard-codes the location (see |  | 
|   33   // WebKit/WebKit/mac/Plugins/WebPluginDatabase.mm's +_defaultPlugInPaths). |  | 
|   34   const char kInternetPluginFolderName[] = "Internet Plug-Ins"; |  | 
|   35  |  | 
|   36   NSSearchPathDomainMask mask = NSUserDomainMask | NSLocalDomainMask; |  | 
|   37   // Note the returned urls are ordered according to the order of the domain |  | 
|   38   // mask constants: paths under $HOME come first, which take precedence. |  | 
|   39   NSArray* library_urls = |  | 
|   40       [[NSFileManager defaultManager] URLsForDirectory:NSLibraryDirectory |  | 
|   41                                              inDomains:mask]; |  | 
|   42   for (NSURL* url in library_urls) { |  | 
|   43     DCHECK([url isFileURL]); |  | 
|   44     base::FilePath path = base::mac::NSStringToFilePath([url path]) |  | 
|   45                               .AppendASCII(kInternetPluginFolderName); |  | 
|   46     if (base::PathExists(path)) |  | 
|   47       plugin_dirs->push_back(path); |  | 
|   48   } |  | 
|   49 } |  | 
|   50  |  | 
|   51 // Returns true if the plugin should be prevented from loading. |  | 
|   52 bool IsBlacklistedPlugin(const WebPluginInfo& info) { |  | 
|   53   // We blacklist Gears by included MIME type, since that is more stable than |  | 
|   54   // its name. Be careful about adding any more plugins to this list though, |  | 
|   55   // since it's easy to accidentally blacklist plugins that support lots of |  | 
|   56   // MIME types. |  | 
|   57   for (const WebPluginMimeType& mime : info.mime_types) { |  | 
|   58     // The Gears plugin is Safari-specific, so don't load it. |  | 
|   59     if (mime.mime_type == "application/x-googlegears") |  | 
|   60       return true; |  | 
|   61   } |  | 
|   62  |  | 
|   63   // Versions of Flip4Mac 2.3 before 2.3.6 often hang the renderer, so don't |  | 
|   64   // load them. |  | 
|   65   if (base::StartsWith(info.name, |  | 
|   66                        base::ASCIIToUTF16("Flip4Mac Windows Media"), |  | 
|   67                        base::CompareCase::INSENSITIVE_ASCII) && |  | 
|   68       base::StartsWith(info.version, base::ASCIIToUTF16("2.3"), |  | 
|   69                        base::CompareCase::SENSITIVE)) { |  | 
|   70     std::vector<base::StringPiece16> components = base::SplitStringPiece( |  | 
|   71         info.version, base::ASCIIToUTF16("."), base::TRIM_WHITESPACE, |  | 
|   72         base::SPLIT_WANT_ALL); |  | 
|   73     int bugfix_version = 0; |  | 
|   74     return (components.size() >= 3 && |  | 
|   75             base::StringToInt(components[2], &bugfix_version) && |  | 
|   76             bugfix_version < 6); |  | 
|   77   } |  | 
|   78  |  | 
|   79   return false; |  | 
|   80 } |  | 
|   81  |  | 
|   82 NSDictionary* GetMIMETypes(CFBundleRef bundle) { |  | 
|   83   NSString* mime_filename = |  | 
|   84       (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, |  | 
|   85                      CFSTR("WebPluginMIMETypesFilename")); |  | 
|   86  |  | 
|   87   if (mime_filename) { |  | 
|   88  |  | 
|   89     // get the file |  | 
|   90  |  | 
|   91     NSString* mime_path = |  | 
|   92         [NSString stringWithFormat:@"%@/Library/Preferences/%@", |  | 
|   93          NSHomeDirectory(), mime_filename]; |  | 
|   94     NSDictionary* mime_file_dict = |  | 
|   95         [NSDictionary dictionaryWithContentsOfFile:mime_path]; |  | 
|   96  |  | 
|   97     // is it valid? |  | 
|   98  |  | 
|   99     bool valid_file = false; |  | 
|  100     if (mime_file_dict) { |  | 
|  101       NSString* l10n_name = |  | 
|  102           [mime_file_dict objectForKey:@"WebPluginLocalizationName"]; |  | 
|  103       NSString* preferred_l10n = [[NSLocale currentLocale] localeIdentifier]; |  | 
|  104       if ([l10n_name isEqualToString:preferred_l10n]) |  | 
|  105         valid_file = true; |  | 
|  106     } |  | 
|  107  |  | 
|  108     if (valid_file) |  | 
|  109       return [mime_file_dict objectForKey:@"WebPluginMIMETypes"]; |  | 
|  110  |  | 
|  111     // dammit, I didn't want to have to do this |  | 
|  112  |  | 
|  113     typedef void (*CreateMIMETypesPrefsPtr)(void); |  | 
|  114     CreateMIMETypesPrefsPtr create_prefs_file = |  | 
|  115         (CreateMIMETypesPrefsPtr)CFBundleGetFunctionPointerForName( |  | 
|  116         bundle, CFSTR("BP_CreatePluginMIMETypesPreferences")); |  | 
|  117     if (!create_prefs_file) |  | 
|  118       return nil; |  | 
|  119     create_prefs_file(); |  | 
|  120  |  | 
|  121     // one more time |  | 
|  122  |  | 
|  123     mime_file_dict = [NSDictionary dictionaryWithContentsOfFile:mime_path]; |  | 
|  124     if (mime_file_dict) |  | 
|  125       return [mime_file_dict objectForKey:@"WebPluginMIMETypes"]; |  | 
|  126     else |  | 
|  127       return nil; |  | 
|  128  |  | 
|  129   } else { |  | 
|  130     return (NSDictionary*)CFBundleGetValueForInfoDictionaryKey(bundle, |  | 
|  131                               CFSTR("WebPluginMIMETypes")); |  | 
|  132   } |  | 
|  133 } |  | 
|  134  |  | 
|  135 bool ReadPlistPluginInfo(const base::FilePath& filename, CFBundleRef bundle, |  | 
|  136                          WebPluginInfo* info) { |  | 
|  137   NSDictionary* mime_types = GetMIMETypes(bundle); |  | 
|  138   if (!mime_types) |  | 
|  139     return false;  // no type info here; try elsewhere |  | 
|  140  |  | 
|  141   for (NSString* mime_type in [mime_types allKeys]) { |  | 
|  142     NSDictionary* mime_dict = [mime_types objectForKey:mime_type]; |  | 
|  143     NSNumber* type_enabled = [mime_dict objectForKey:@"WebPluginTypeEnabled"]; |  | 
|  144     NSString* mime_desc = [mime_dict objectForKey:@"WebPluginTypeDescription"]; |  | 
|  145     NSArray* mime_exts = [mime_dict objectForKey:@"WebPluginExtensions"]; |  | 
|  146  |  | 
|  147     // Skip any disabled types. |  | 
|  148     if (type_enabled && ![type_enabled boolValue]) |  | 
|  149       continue; |  | 
|  150  |  | 
|  151     WebPluginMimeType mime; |  | 
|  152     mime.mime_type = base::SysNSStringToUTF8([mime_type lowercaseString]); |  | 
|  153     // Remove PDF from the list of types handled by QuickTime, since it provides |  | 
|  154     // a worse experience than just downloading the PDF. |  | 
|  155     if (mime.mime_type == "application/pdf" && |  | 
|  156         base::StartsWith(filename.BaseName().value(), "QuickTime", |  | 
|  157                          base::CompareCase::INSENSITIVE_ASCII)) { |  | 
|  158       continue; |  | 
|  159     } |  | 
|  160  |  | 
|  161     if (mime_desc) |  | 
|  162       mime.description = base::SysNSStringToUTF16(mime_desc); |  | 
|  163     for (NSString* ext in mime_exts) |  | 
|  164       mime.file_extensions.push_back( |  | 
|  165           base::SysNSStringToUTF8([ext lowercaseString])); |  | 
|  166  |  | 
|  167     info->mime_types.push_back(mime); |  | 
|  168   } |  | 
|  169  |  | 
|  170   NSString* plugin_name = |  | 
|  171       (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, |  | 
|  172       CFSTR("WebPluginName")); |  | 
|  173   NSString* plugin_vers = |  | 
|  174       (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, |  | 
|  175       CFSTR("CFBundleShortVersionString")); |  | 
|  176   NSString* plugin_desc = |  | 
|  177       (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle, |  | 
|  178       CFSTR("WebPluginDescription")); |  | 
|  179  |  | 
|  180   if (plugin_name) |  | 
|  181     info->name = base::SysNSStringToUTF16(plugin_name); |  | 
|  182   else |  | 
|  183     info->name = base::UTF8ToUTF16(filename.BaseName().value()); |  | 
|  184   info->path = filename; |  | 
|  185   if (plugin_vers) |  | 
|  186     info->version = base::SysNSStringToUTF16(plugin_vers); |  | 
|  187   if (plugin_desc) |  | 
|  188     info->desc = base::SysNSStringToUTF16(plugin_desc); |  | 
|  189   else |  | 
|  190     info->desc = base::UTF8ToUTF16(filename.BaseName().value()); |  | 
|  191  |  | 
|  192   return true; |  | 
|  193 } |  | 
|  194  |  | 
|  195 }  // namespace |  | 
|  196  |  | 
|  197 bool PluginList::ReadWebPluginInfo(const base::FilePath &filename, |  | 
|  198                                    WebPluginInfo* info) { |  | 
|  199   // There are three ways to get information about plugin capabilities: |  | 
|  200   // 1) a set of Info.plist keys, documented at |  | 
|  201   // http://developer.apple.com/documentation/InternetWeb/Conceptual/WebKit_Plug
     inProgTopic/Concepts/AboutPlugins.html . |  | 
|  202   // 2) a set of STR# resources, documented at |  | 
|  203   // https://developer.mozilla.org/En/Gecko_Plugin_API_Reference/Plug-in_Develop
     ment_Overview . |  | 
|  204   // 3) a NP_GetMIMEDescription() entry point, documented at |  | 
|  205   // https://developer.mozilla.org/en/NP_GetMIMEDescription |  | 
|  206   // |  | 
|  207   // Mozilla supported (3), but WebKit never has, so no plugins rely on it. Most |  | 
|  208   // browsers supported (2) and then added support for (1); Chromium originally |  | 
|  209   // supported (2) and (1), but now supports only (1) as (2) is deprecated. |  | 
|  210   // |  | 
|  211   // For the Info.plist version, the data is formatted as follows (in text plist |  | 
|  212   // format): |  | 
|  213   //  { |  | 
|  214   //    ... the usual plist keys ... |  | 
|  215   //    WebPluginDescription = <<plugindescription>>; |  | 
|  216   //    WebPluginMIMETypes = { |  | 
|  217   //      <<type0mimetype>> = { |  | 
|  218   //        WebPluginExtensions = ( |  | 
|  219   //                               <<type0fileextension0>>, |  | 
|  220   //                               ... |  | 
|  221   //                               <<type0fileextensionk>>, |  | 
|  222   //                               ); |  | 
|  223   //        WebPluginTypeDescription = <<type0description>>; |  | 
|  224   //      }; |  | 
|  225   //      <<type1mimetype>> = { ... }; |  | 
|  226   //      ... |  | 
|  227   //      <<typenmimetype>> = { ... }; |  | 
|  228   //    }; |  | 
|  229   //    WebPluginName = <<pluginname>>; |  | 
|  230   //  } |  | 
|  231   // |  | 
|  232   // Alternatively (and this is undocumented), rather than a WebPluginMIMETypes |  | 
|  233   // key, there may be a WebPluginMIMETypesFilename key. If it is present, then |  | 
|  234   // it is the name of a file in the user's preferences folder in which to find |  | 
|  235   // the WebPluginMIMETypes key. If the key is present but the file doesn't |  | 
|  236   // exist, we must load the plugin and call a specific function to have the |  | 
|  237   // plugin create the file. |  | 
|  238  |  | 
|  239   ScopedCFTypeRef<CFURLRef> bundle_url(CFURLCreateFromFileSystemRepresentation( |  | 
|  240       kCFAllocatorDefault, (const UInt8*)filename.value().c_str(), |  | 
|  241       filename.value().length(), true)); |  | 
|  242   if (!bundle_url) { |  | 
|  243     LOG_IF(ERROR, PluginList::DebugPluginLoading()) |  | 
|  244         << "PluginLib::ReadWebPluginInfo could not create bundle URL"; |  | 
|  245     return false; |  | 
|  246   } |  | 
|  247   ScopedCFTypeRef<CFBundleRef> bundle(CFBundleCreate(kCFAllocatorDefault, |  | 
|  248                                                      bundle_url.get())); |  | 
|  249   if (!bundle) { |  | 
|  250     LOG_IF(ERROR, PluginList::DebugPluginLoading()) |  | 
|  251         << "PluginLib::ReadWebPluginInfo could not create CFBundleRef"; |  | 
|  252     return false; |  | 
|  253   } |  | 
|  254  |  | 
|  255   // preflight |  | 
|  256  |  | 
|  257   OSType type = 0; |  | 
|  258   CFBundleGetPackageInfo(bundle.get(), &type, NULL); |  | 
|  259   if (type != FOUR_CHAR_CODE('BRPL')) { |  | 
|  260     LOG_IF(ERROR, PluginList::DebugPluginLoading()) |  | 
|  261         << "PluginLib::ReadWebPluginInfo bundle is not BRPL, is " << type; |  | 
|  262     return false; |  | 
|  263   } |  | 
|  264  |  | 
|  265   CFErrorRef error; |  | 
|  266   Boolean would_load = CFBundlePreflightExecutable(bundle.get(), &error); |  | 
|  267   if (!would_load) { |  | 
|  268     ScopedCFTypeRef<CFStringRef> error_string(CFErrorCopyDescription(error)); |  | 
|  269     LOG_IF(ERROR, PluginList::DebugPluginLoading()) |  | 
|  270         << "PluginLib::ReadWebPluginInfo bundle failed preflight: " |  | 
|  271         << base::SysCFStringRefToUTF8(error_string); |  | 
|  272     return false; |  | 
|  273   } |  | 
|  274  |  | 
|  275   // get the info |  | 
|  276  |  | 
|  277   if (ReadPlistPluginInfo(filename, bundle.get(), info)) |  | 
|  278     return true; |  | 
|  279  |  | 
|  280   // ... or not |  | 
|  281  |  | 
|  282   return false; |  | 
|  283 } |  | 
|  284  |  | 
|  285 void PluginList::GetPluginDirectories( |  | 
|  286     std::vector<base::FilePath>* plugin_dirs) { |  | 
|  287   if (PluginList::plugins_discovery_disabled_) |  | 
|  288     return; |  | 
|  289  |  | 
|  290   GetPluginCommonDirectories(plugin_dirs); |  | 
|  291 } |  | 
|  292  |  | 
|  293 void PluginList::GetPluginsInDir( |  | 
|  294     const base::FilePath& path, std::vector<base::FilePath>* plugins) { |  | 
|  295   base::FileEnumerator enumerator(path, |  | 
|  296                                   false, // not recursive |  | 
|  297                                   base::FileEnumerator::DIRECTORIES); |  | 
|  298   for (base::FilePath path = enumerator.Next(); !path.value().empty(); |  | 
|  299        path = enumerator.Next()) { |  | 
|  300     plugins->push_back(path); |  | 
|  301   } |  | 
|  302 } |  | 
|  303  |  | 
|  304 bool PluginList::ShouldLoadPluginUsingPluginList( |  | 
|  305     const WebPluginInfo& info, |  | 
|  306     std::vector<WebPluginInfo>* plugins) { |  | 
|  307   return !IsBlacklistedPlugin(info); |  | 
|  308 } |  | 
|  309  |  | 
|  310 }  // namespace content |  | 
| OLD | NEW |