Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebKit / UIProcess / Plugins / PluginInfoStore.cpp
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/MIMETypeRegistry.h>
33 #include <WebCore/SecurityOrigin.h>
34 #include <algorithm>
35 #include <wtf/ListHashSet.h>
36 #include <wtf/StdLibExtras.h>
37 #include <wtf/URL.h>
38
39 namespace WebKit {
40 using namespace WebCore;
41
42 PluginInfoStore::PluginInfoStore()
43     : m_pluginListIsUpToDate(false)
44 {
45 }
46
47 void PluginInfoStore::setAdditionalPluginsDirectories(const Vector<String>& directories)
48 {
49     m_additionalPluginsDirectories = directories;
50     refresh();
51 }
52
53 void PluginInfoStore::refresh()
54 {
55     m_pluginListIsUpToDate = false;
56 }
57
58 template <typename T, typename U>
59 static void addFromVector(T& hashSet, const U& vector)
60 {
61     for (size_t i = 0; i < vector.size(); ++i)
62         hashSet.add(vector[i]);
63 }
64
65 void PluginInfoStore::loadPluginsIfNecessary()
66 {
67     if (m_pluginListIsUpToDate)
68         return;
69
70     ListHashSet<String> uniquePluginPaths;
71
72     // First, load plug-ins from the additional plug-ins directories specified.
73     for (size_t i = 0; i < m_additionalPluginsDirectories.size(); ++i)
74         addFromVector(uniquePluginPaths, pluginPathsInDirectory(m_additionalPluginsDirectories[i]));
75
76     // Then load plug-ins from the standard plug-ins directories.
77     Vector<String> directories = pluginsDirectories();
78     for (size_t i = 0; i < directories.size(); ++i)
79         addFromVector(uniquePluginPaths, pluginPathsInDirectory(directories[i]));
80
81     // Then load plug-ins that are not in the standard plug-ins directories.
82     addFromVector(uniquePluginPaths, individualPluginPaths());
83
84     m_plugins.clear();
85
86     for (const auto& pluginPath : uniquePluginPaths)
87         loadPlugin(m_plugins, pluginPath);
88
89     m_pluginListIsUpToDate = true;
90 }
91
92 void PluginInfoStore::loadPlugin(Vector<PluginModuleInfo>& plugins, const String& pluginPath)
93 {
94     PluginModuleInfo plugin;
95     
96     if (!getPluginInfo(pluginPath, plugin))
97         return;
98
99     if (!shouldUsePlugin(plugins, plugin))
100         return;
101     
102     plugins.append(plugin);
103 }
104
105 Vector<PluginModuleInfo> PluginInfoStore::plugins()
106 {
107     loadPluginsIfNecessary();
108     return m_plugins;
109 }
110
111 PluginModuleInfo PluginInfoStore::findPluginForMIMEType(const String& mimeType, PluginData::AllowedPluginTypes allowedPluginTypes) const
112 {
113     ASSERT(!mimeType.isNull());
114
115     for (const auto& plugin : m_plugins) {
116         if (allowedPluginTypes == PluginData::OnlyApplicationPlugins && !plugin.info.isApplicationPlugin)
117             continue;
118
119         for (const auto& mimeClassInfo : plugin.info.mimes) {
120             if (mimeClassInfo.type == mimeType)
121                 return plugin;
122         }
123     }
124     
125     return PluginModuleInfo();
126 }
127
128 PluginModuleInfo PluginInfoStore::findPluginForExtension(const String& extension, String& mimeType, PluginData::AllowedPluginTypes allowedPluginTypes) const
129 {
130     ASSERT(!extension.isNull());
131
132     for (const auto& plugin : m_plugins) {
133         if (allowedPluginTypes == PluginData::OnlyApplicationPlugins && !plugin.info.isApplicationPlugin)
134             continue;
135
136         for (const auto& mimeClassInfo : plugin.info.mimes) {
137             if (mimeClassInfo.extensions.contains(extension)) {
138                 // We found a supported extension, set the correct MIME type.
139                 mimeType = mimeClassInfo.type;
140                 return plugin;
141             }
142         }
143     }
144     
145     return PluginModuleInfo();
146 }
147
148 static inline String pathExtension(const URL& url)
149 {
150     String extension;
151     String filename = url.lastPathComponent();
152     if (!filename.endsWith('/')) {
153         size_t extensionPos = filename.reverseFind('.');
154         if (extensionPos != notFound)
155             extension = filename.substring(extensionPos + 1);
156     }
157     return extension.convertToASCIILowercase();
158 }
159
160 #if !PLATFORM(COCOA)
161
162 bool PluginInfoStore::shouldAllowPluginToRunUnsandboxed(const String& pluginBundleIdentifier)
163 {
164     UNUSED_PARAM(pluginBundleIdentifier);
165     return false;
166 }
167
168 PluginModuleLoadPolicy PluginInfoStore::defaultLoadPolicyForPlugin(const PluginModuleInfo&)
169 {
170     return PluginModuleLoadNormally;
171 }
172     
173 PluginModuleInfo PluginInfoStore::findPluginWithBundleIdentifier(const String&)
174 {
175     ASSERT_NOT_REACHED();
176     return PluginModuleInfo();
177 }
178
179 #endif
180
181 PluginModuleInfo PluginInfoStore::findPlugin(String& mimeType, const URL& url, PluginData::AllowedPluginTypes allowedPluginTypes)
182 {
183     loadPluginsIfNecessary();
184     
185     // First, check if we can get the plug-in based on its MIME type.
186     if (!mimeType.isNull()) {
187         PluginModuleInfo plugin = findPluginForMIMEType(mimeType, allowedPluginTypes);
188         if (!plugin.path.isNull())
189             return plugin;
190     }
191
192     // Next, check if any plug-ins claim to support the URL extension.
193     String extension = pathExtension(url);
194     if (!extension.isNull() && mimeType.isEmpty()) {
195         PluginModuleInfo plugin = findPluginForExtension(extension, mimeType, allowedPluginTypes);
196         if (!plugin.path.isNull())
197             return plugin;
198         
199         // Finally, try to get the MIME type from the extension in a platform specific manner and use that.
200         String extensionMimeType = MIMETypeRegistry::getMIMETypeForExtension(extension);
201         if (!extensionMimeType.isNull()) {
202             PluginModuleInfo plugin = findPluginForMIMEType(extensionMimeType, allowedPluginTypes);
203             if (!plugin.path.isNull()) {
204                 mimeType = extensionMimeType;
205                 return plugin;
206             }
207         }
208     }
209     
210     return PluginModuleInfo();
211 }
212
213 bool PluginInfoStore::isSupportedPlugin(const PluginInfoStore::SupportedPlugin& plugin, const String& mimeType, const URL& pluginURL)
214 {
215     if (!mimeType.isEmpty() && plugin.mimeTypes.contains(mimeType))
216         return true;
217     auto extension = pathExtension(pluginURL);
218     return extension.isEmpty() ? false : plugin.extensions.contains(extension);
219 }
220
221 bool PluginInfoStore::isSupportedPlugin(const String& mimeType, const URL& pluginURL, const String&, const URL& pageURL)
222 {
223     // We check only pageURL for consistency with WebProcess visible plugins.
224     if (!m_supportedPlugins)
225         return true;
226
227     return m_supportedPlugins->findMatching([&] (auto&& plugin) {
228         return pageURL.isMatchingDomain(plugin.matchingDomain) && isSupportedPlugin(plugin, mimeType, pluginURL);
229     }) != notFound;
230 }
231
232 std::optional<Vector<SupportedPluginIdentifier>> PluginInfoStore::supportedPluginIdentifiers()
233 {
234     if (!m_supportedPlugins)
235         return std::nullopt;
236
237     return WTF::map(*m_supportedPlugins, [] (auto&& item) {
238         return SupportedPluginIdentifier { item.matchingDomain, item.identifier };
239     });
240 }
241
242 void PluginInfoStore::addSupportedPlugin(String&& domainName, String&& identifier, HashSet<String>&& mimeTypes, HashSet<String> extensions)
243 {
244     if (!m_supportedPlugins)
245         m_supportedPlugins = Vector<SupportedPlugin> { };
246
247     m_supportedPlugins->append(SupportedPlugin { WTFMove(domainName), WTFMove(identifier), WTFMove(mimeTypes), WTFMove(extensions) });
248 }
249
250 PluginModuleInfo PluginInfoStore::infoForPluginWithPath(const String& pluginPath) const
251 {
252     for (const auto& plugin : m_plugins) {
253         if (plugin.path == pluginPath)
254             return plugin;
255     }
256
257     ASSERT_NOT_REACHED();
258     return PluginModuleInfo();
259 }
260
261 } // namespace WebKit
262
263 #endif // ENABLE(NETSCAPE_PLUGIN_API)