1/*
2 * Copyright (C) 2010, 2012 Apple Inc. All rights reserved.
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 "PluginInfoStore.h"
28
29#if ENABLE(NETSCAPE_PLUGIN_API)
30
31#include "PluginModuleInfo.h"
32#include <WebCore/KURL.h>
33#include <WebCore/MIMETypeRegistry.h>
34#include <algorithm>
35#include <wtf/ListHashSet.h>
36#include <wtf/StdLibExtras.h>
37
38using namespace WebCore;
39
40namespace WebKit {
41
42PluginInfoStore::PluginInfoStore()
43    : m_pluginListIsUpToDate(false)
44    , m_client(0)
45{
46}
47
48void PluginInfoStore::setAdditionalPluginsDirectories(const Vector<String>& directories)
49{
50    m_additionalPluginsDirectories = directories;
51    refresh();
52}
53
54void PluginInfoStore::refresh()
55{
56    m_pluginListIsUpToDate = false;
57}
58
59template <typename T, typename U>
60static void addFromVector(T& hashSet, const U& vector)
61{
62    for (size_t i = 0; i < vector.size(); ++i)
63        hashSet.add(vector[i]);
64}
65
66// We use a ListHashSet so that plugins will be loaded from the additional plugins directories first
67// (which in turn means those plugins will be preferred if two plugins claim the same MIME type).
68#if OS(WINDOWS)
69typedef ListHashSet<String, 32, CaseFoldingHash> PathHashSet;
70#else
71typedef ListHashSet<String, 32> PathHashSet;
72#endif
73
74void PluginInfoStore::loadPluginsIfNecessary()
75{
76    if (m_pluginListIsUpToDate)
77        return;
78
79    PathHashSet uniquePluginPaths;
80
81    // First, load plug-ins from the additional plug-ins directories specified.
82    for (size_t i = 0; i < m_additionalPluginsDirectories.size(); ++i)
83        addFromVector(uniquePluginPaths, pluginPathsInDirectory(m_additionalPluginsDirectories[i]));
84
85    // Then load plug-ins from the standard plug-ins directories.
86    Vector<String> directories = pluginsDirectories();
87    for (size_t i = 0; i < directories.size(); ++i)
88        addFromVector(uniquePluginPaths, pluginPathsInDirectory(directories[i]));
89
90    // Then load plug-ins that are not in the standard plug-ins directories.
91    addFromVector(uniquePluginPaths, individualPluginPaths());
92
93    m_plugins.clear();
94
95    PathHashSet::const_iterator end = uniquePluginPaths.end();
96    for (PathHashSet::const_iterator it = uniquePluginPaths.begin(); it != end; ++it)
97        loadPlugin(m_plugins, *it);
98
99    m_pluginListIsUpToDate = true;
100
101    if (m_client)
102        m_client->pluginInfoStoreDidLoadPlugins(this);
103}
104
105void PluginInfoStore::loadPlugin(Vector<PluginModuleInfo>& plugins, const String& pluginPath)
106{
107    PluginModuleInfo plugin;
108
109    if (!getPluginInfo(pluginPath, plugin))
110        return;
111
112    if (!shouldUsePlugin(plugins, plugin))
113        return;
114
115    plugins.append(plugin);
116}
117
118Vector<PluginModuleInfo> PluginInfoStore::plugins()
119{
120    loadPluginsIfNecessary();
121    return m_plugins;
122}
123
124PluginModuleInfo PluginInfoStore::findPluginForMIMEType(const String& mimeType, PluginData::AllowedPluginTypes allowedPluginTypes) const
125{
126    ASSERT(!mimeType.isNull());
127
128    for (size_t i = 0; i < m_plugins.size(); ++i) {
129        const PluginModuleInfo& plugin = m_plugins[i];
130
131        if (allowedPluginTypes == PluginData::OnlyApplicationPlugins && !plugin.info.isApplicationPlugin)
132            continue;
133
134        for (size_t j = 0; j < plugin.info.mimes.size(); ++j) {
135            const MimeClassInfo& mimeClassInfo = plugin.info.mimes[j];
136            if (mimeClassInfo.type == mimeType)
137                return plugin;
138        }
139    }
140
141    return PluginModuleInfo();
142}
143
144PluginModuleInfo PluginInfoStore::findPluginForExtension(const String& extension, String& mimeType, PluginData::AllowedPluginTypes allowedPluginTypes) const
145{
146    ASSERT(!extension.isNull());
147
148    for (size_t i = 0; i < m_plugins.size(); ++i) {
149        const PluginModuleInfo& plugin = m_plugins[i];
150
151        if (allowedPluginTypes == PluginData::OnlyApplicationPlugins && !plugin.info.isApplicationPlugin)
152            continue;
153
154        for (size_t j = 0; j < plugin.info.mimes.size(); ++j) {
155            const MimeClassInfo& mimeClassInfo = plugin.info.mimes[j];
156
157            const Vector<String>& extensions = mimeClassInfo.extensions;
158
159            if (std::find(extensions.begin(), extensions.end(), extension) != extensions.end()) {
160                // We found a supported extension, set the correct MIME type.
161                mimeType = mimeClassInfo.type;
162                return plugin;
163            }
164        }
165    }
166
167    return PluginModuleInfo();
168}
169
170static inline String pathExtension(const KURL& url)
171{
172    String extension;
173    String filename = url.lastPathComponent();
174    if (!filename.endsWith('/')) {
175        int extensionPos = filename.reverseFind('.');
176        if (extensionPos != -1)
177            extension = filename.substring(extensionPos + 1);
178    }
179
180    return extension;
181}
182
183#if !PLATFORM(MAC)
184PluginModuleLoadPolicy PluginInfoStore::defaultLoadPolicyForPlugin(const PluginModuleInfo&)
185{
186    return PluginModuleLoadNormally;
187}
188
189String PluginInfoStore::getMIMETypeForExtension(const String& extension)
190{
191    return MIMETypeRegistry::getMIMETypeForExtension(extension);
192}
193
194PluginModuleInfo PluginInfoStore::findPluginWithBundleIdentifier(const String&)
195{
196    ASSERT_NOT_REACHED();
197    return PluginModuleInfo();
198}
199
200#endif
201
202PluginModuleInfo PluginInfoStore::findPlugin(String& mimeType, const KURL& url, PluginData::AllowedPluginTypes allowedPluginTypes)
203{
204    loadPluginsIfNecessary();
205
206    // First, check if we can get the plug-in based on its MIME type.
207    if (!mimeType.isNull()) {
208        PluginModuleInfo plugin = findPluginForMIMEType(mimeType, allowedPluginTypes);
209        if (!plugin.path.isNull())
210            return plugin;
211    }
212
213    // Next, check if any plug-ins claim to support the URL extension.
214    String extension = pathExtension(url).lower();
215    if (!extension.isNull() && mimeType.isEmpty()) {
216        PluginModuleInfo plugin = findPluginForExtension(extension, mimeType, allowedPluginTypes);
217        if (!plugin.path.isNull())
218            return plugin;
219
220        // Finally, try to get the MIME type from the extension in a platform specific manner and use that.
221        String extensionMimeType = getMIMETypeForExtension(extension);
222        if (!extensionMimeType.isNull()) {
223            PluginModuleInfo plugin = findPluginForMIMEType(extensionMimeType, allowedPluginTypes);
224            if (!plugin.path.isNull()) {
225                mimeType = extensionMimeType;
226                return plugin;
227            }
228        }
229    }
230
231    return PluginModuleInfo();
232}
233
234PluginModuleInfo PluginInfoStore::infoForPluginWithPath(const String& pluginPath) const
235{
236    for (size_t i = 0; i < m_plugins.size(); ++i) {
237        if (m_plugins[i].path == pluginPath)
238            return m_plugins[i];
239    }
240
241    ASSERT_NOT_REACHED();
242    return PluginModuleInfo();
243}
244
245} // namespace WebKit
246
247#endif // ENABLE(NETSCAPE_PLUGIN_API)
248