1/* 2 * Copyright (C) 2014 Igalia S.L. 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#include "config.h" 27#include "PluginInfoCache.h" 28 29#if ENABLE(NETSCAPE_PLUGIN_API) 30 31#include "NetscapePluginModule.h" 32#include <WebCore/FileSystem.h> 33#include <wtf/text/CString.h> 34 35namespace WebKit { 36 37static const unsigned gSchemaVersion = 2; 38 39PluginInfoCache& PluginInfoCache::shared() 40{ 41 static NeverDestroyed<PluginInfoCache> pluginInfoCache; 42 return pluginInfoCache; 43} 44 45PluginInfoCache::PluginInfoCache() 46 : m_cacheFile(g_key_file_new()) 47 , m_cachePath(g_build_filename(g_get_user_cache_dir(), "webkitgtk", "plugins", nullptr)) 48{ 49 g_key_file_load_from_file(m_cacheFile.get(), m_cachePath.get(), G_KEY_FILE_NONE, nullptr); 50 51 if (g_key_file_has_group(m_cacheFile.get(), "schema")) { 52 unsigned schemaVersion = static_cast<unsigned>(g_key_file_get_integer(m_cacheFile.get(), "schema", "version", nullptr)); 53 if (schemaVersion == gSchemaVersion) 54 return; 55 56 // Cache file using an old schema, create a new empty file. 57 m_cacheFile.reset(g_key_file_new()); 58 } 59 60 g_key_file_set_integer(m_cacheFile.get(), "schema", "version", static_cast<unsigned>(gSchemaVersion)); 61} 62 63PluginInfoCache::~PluginInfoCache() 64{ 65 if (m_saveToFileIdle.isScheduled()) { 66 m_saveToFileIdle.cancel(); 67 saveToFile(); 68 } 69} 70 71void PluginInfoCache::saveToFile() 72{ 73 std::lock_guard<std::mutex> lock(m_mutex); 74 75 gsize dataLength; 76 GUniquePtr<char> data(g_key_file_to_data(m_cacheFile.get(), &dataLength, nullptr)); 77 if (!data) 78 return; 79 80 g_file_set_contents(m_cachePath.get(), data.get(), dataLength, nullptr); 81} 82 83bool PluginInfoCache::getPluginInfo(const String& pluginPath, PluginModuleInfo& plugin) 84{ 85 CString pluginGroup = pluginPath.utf8(); 86 if (!g_key_file_has_group(m_cacheFile.get(), pluginGroup.data())) 87 return false; 88 89 time_t lastModified; 90 if (!WebCore::getFileModificationTime(pluginPath, lastModified)) 91 return false; 92 time_t cachedLastModified = static_cast<time_t>(g_key_file_get_uint64(m_cacheFile.get(), pluginGroup.data(), "mtime", nullptr)); 93 if (lastModified != cachedLastModified) 94 return false; 95 96 plugin.path = pluginPath; 97 plugin.info.file = WebCore::pathGetFileName(pluginPath); 98 99 GUniquePtr<char> stringValue(g_key_file_get_string(m_cacheFile.get(), pluginGroup.data(), "name", nullptr)); 100 plugin.info.name = String::fromUTF8(stringValue.get()); 101 102 stringValue.reset(g_key_file_get_string(m_cacheFile.get(), pluginGroup.data(), "description", nullptr)); 103 plugin.info.desc = String::fromUTF8(stringValue.get()); 104 105#if PLUGIN_ARCHITECTURE(X11) 106 stringValue.reset(g_key_file_get_string(m_cacheFile.get(), pluginGroup.data(), "mime-description", nullptr)); 107 NetscapePluginModule::parseMIMEDescription(String::fromUTF8(stringValue.get()), plugin.info.mimes); 108#endif 109 110 plugin.requiresGtk2 = g_key_file_get_boolean(m_cacheFile.get(), pluginGroup.data(), "requires-gtk2", nullptr); 111 112 return true; 113} 114 115void PluginInfoCache::updatePluginInfo(const String& pluginPath, const PluginModuleInfo& plugin) 116{ 117 time_t lastModified; 118 if (!WebCore::getFileModificationTime(pluginPath, lastModified)) 119 return; 120 121 CString pluginGroup = pluginPath.utf8(); 122 g_key_file_set_uint64(m_cacheFile.get(), pluginGroup.data(), "mtime", static_cast<guint64>(lastModified)); 123 g_key_file_set_string(m_cacheFile.get(), pluginGroup.data(), "name", plugin.info.name.utf8().data()); 124 g_key_file_set_string(m_cacheFile.get(), pluginGroup.data(), "description", plugin.info.desc.utf8().data()); 125 126#if PLUGIN_ARCHITECTURE(X11) 127 String mimeDescription = NetscapePluginModule::buildMIMEDescription(plugin.info.mimes); 128 g_key_file_set_string(m_cacheFile.get(), pluginGroup.data(), "mime-description", mimeDescription.utf8().data()); 129#endif 130 131 g_key_file_set_boolean(m_cacheFile.get(), pluginGroup.data(), "requires-gtk2", plugin.requiresGtk2); 132 133 // Save the cache file in an idle to make sure it happens in the main thread and 134 // it's done only once when this is called multiple times in a very short time. 135 std::lock_guard<std::mutex> lock(m_mutex); 136 if (m_saveToFileIdle.isScheduled()) 137 return; 138 139 m_saveToFileIdle.schedule("[WebKit] PluginInfoCache::saveToFile", std::bind(&PluginInfoCache::saveToFile, this), G_PRIORITY_DEFAULT_IDLE); 140} 141 142} // namespace WebKit 143 144#endif // ENABLE(NETSCAPE_PLUGIN_API) 145