1/*
2 * Copyright (C) 2014, 2018-2019 Igalia S.L.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21#include "WebKitUserContent.h"
22
23#include "WebKitUserContentPrivate.h"
24#include <wtf/HashMap.h>
25#include <wtf/NeverDestroyed.h>
26#include <wtf/text/CString.h>
27
28using namespace WebCore;
29
30/**
31 * SECTION:WebKitUserContent
32 * @short_description: Defines user content types which affect web pages.
33 * @title: User content
34 *
35 * See also: #WebKitUserContentManager
36 *
37 * Since: 2.6
38 */
39
40
41API::UserContentWorld& webkitUserContentWorld(const char* worldName)
42{
43 static NeverDestroyed<HashMap<CString, RefPtr<API::UserContentWorld>>> map;
44 return *map.get().ensure(worldName, [worldName = String::fromUTF8(worldName)] { return API::UserContentWorld::worldWithName(worldName); }).iterator->value;
45}
46
47static inline UserContentInjectedFrames toUserContentInjectedFrames(WebKitUserContentInjectedFrames injectedFrames)
48{
49 switch (injectedFrames) {
50 case WEBKIT_USER_CONTENT_INJECT_TOP_FRAME:
51 return InjectInTopFrameOnly;
52 case WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES:
53 return InjectInAllFrames;
54 default:
55 ASSERT_NOT_REACHED();
56 return InjectInAllFrames;
57 }
58}
59
60static inline UserStyleLevel toUserStyleLevel(WebKitUserStyleLevel styleLevel)
61{
62 switch (styleLevel) {
63 case WEBKIT_USER_STYLE_LEVEL_USER:
64 return UserStyleUserLevel;
65 case WEBKIT_USER_STYLE_LEVEL_AUTHOR:
66 return UserStyleAuthorLevel;
67 default:
68 ASSERT_NOT_REACHED();
69 return UserStyleAuthorLevel;
70 }
71}
72
73static inline UserScriptInjectionTime toUserScriptInjectionTime(WebKitUserScriptInjectionTime injectionTime)
74{
75 switch (injectionTime) {
76 case WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START:
77 return InjectAtDocumentStart;
78 case WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END:
79 return InjectAtDocumentEnd;
80 default:
81 ASSERT_NOT_REACHED();
82 return InjectAtDocumentStart;
83 }
84}
85
86static inline Vector<String> toStringVector(const char* const* strv)
87{
88 if (!strv)
89 return Vector<String>();
90
91 Vector<String> result;
92 for (auto str = strv; *str; ++str)
93 result.append(String::fromUTF8(*str));
94 return result;
95}
96
97struct _WebKitUserStyleSheet {
98 _WebKitUserStyleSheet(const gchar* source, WebKitUserContentInjectedFrames injectedFrames, WebKitUserStyleLevel level, const char* const* whitelist, const char* const* blacklist, API::UserContentWorld& world)
99 : userStyleSheet(adoptRef(new API::UserStyleSheet(UserStyleSheet {
100 String::fromUTF8(source), URL { },
101 toStringVector(whitelist), toStringVector(blacklist),
102 toUserContentInjectedFrames(injectedFrames),
103 toUserStyleLevel(level) }, world)))
104 , referenceCount(1)
105 {
106 }
107
108 RefPtr<API::UserStyleSheet> userStyleSheet;
109 int referenceCount;
110};
111
112G_DEFINE_BOXED_TYPE(WebKitUserStyleSheet, webkit_user_style_sheet, webkit_user_style_sheet_ref, webkit_user_style_sheet_unref)
113
114/**
115 * webkit_user_style_sheet_ref:
116 * @user_style_sheet: a #WebKitUserStyleSheet
117 *
118 * Atomically increments the reference count of @user_style_sheet by one.
119 * This function is MT-safe and may be called from any thread.
120 *
121 * Returns: The passed #WebKitUserStyleSheet
122 *
123 * Since: 2.6
124 */
125WebKitUserStyleSheet* webkit_user_style_sheet_ref(WebKitUserStyleSheet* userStyleSheet)
126{
127 g_atomic_int_inc(&userStyleSheet->referenceCount);
128 return userStyleSheet;
129}
130
131/**
132 * webkit_user_style_sheet_unref:
133 * @user_style_sheet: a #WebKitUserStyleSheet
134 *
135 * Atomically decrements the reference count of @user_style_sheet by one.
136 * If the reference count drops to 0, all memory allocated by
137 * #WebKitUserStyleSheet is released. This function is MT-safe and may be
138 * called from any thread.
139 *
140 * Since: 2.6
141 */
142void webkit_user_style_sheet_unref(WebKitUserStyleSheet* userStyleSheet)
143{
144 if (g_atomic_int_dec_and_test(&userStyleSheet->referenceCount)) {
145 userStyleSheet->~WebKitUserStyleSheet();
146 fastFree(userStyleSheet);
147 }
148}
149
150/**
151 * webkit_user_style_sheet_new:
152 * @source: Source code of the user style sheet.
153 * @injected_frames: A #WebKitUserContentInjectedFrames value
154 * @level: A #WebKitUserStyleLevel
155 * @whitelist: (array zero-terminated=1) (allow-none): A whitelist of URI patterns or %NULL
156 * @blacklist: (array zero-terminated=1) (allow-none): A blacklist of URI patterns or %NULL
157 *
158 * Creates a new user style sheet. Style sheets can be applied to some URIs
159 * only by passing non-null values for @whitelist or @blacklist. Passing a
160 * %NULL whitelist implies that all URIs are on the whitelist. The style
161 * sheet is applied if an URI matches the whitelist and not the blacklist.
162 * URI patterns must be of the form `[protocol]://[host]/[path]`, where the
163 * *host* and *path* components can contain the wildcard character (`*`) to
164 * represent zero or more other characters.
165 *
166 * Returns: A new #WebKitUserStyleSheet
167 *
168 * Since: 2.6
169 */
170WebKitUserStyleSheet* webkit_user_style_sheet_new(const gchar* source, WebKitUserContentInjectedFrames injectedFrames, WebKitUserStyleLevel level, const char* const* whitelist, const char* const* blacklist)
171{
172 g_return_val_if_fail(source, nullptr);
173 WebKitUserStyleSheet* userStyleSheet = static_cast<WebKitUserStyleSheet*>(fastMalloc(sizeof(WebKitUserStyleSheet)));
174 new (userStyleSheet) WebKitUserStyleSheet(source, injectedFrames, level, whitelist, blacklist, API::UserContentWorld::normalWorld());
175 return userStyleSheet;
176}
177
178/**
179 * webkit_user_style_sheet_new_for_world:
180 * @source: Source code of the user style sheet.
181 * @injected_frames: A #WebKitUserContentInjectedFrames value
182 * @level: A #WebKitUserStyleLevel
183 * @world_name: the name of a #WebKitScriptWorld
184 * @whitelist: (array zero-terminated=1) (allow-none): A whitelist of URI patterns or %NULL
185 * @blacklist: (array zero-terminated=1) (allow-none): A blacklist of URI patterns or %NULL
186 *
187 * Creates a new user style sheet for script world with name @world_name.
188 * See webkit_user_style_sheet_new() for a full description.
189 *
190 * Returns: A new #WebKitUserStyleSheet
191 *
192 * Since: 2.22
193 */
194WebKitUserStyleSheet* webkit_user_style_sheet_new_for_world(const gchar* source, WebKitUserContentInjectedFrames injectedFrames, WebKitUserStyleLevel level, const char* worldName, const char* const* whitelist, const char* const* blacklist)
195{
196 g_return_val_if_fail(source, nullptr);
197 g_return_val_if_fail(worldName, nullptr);
198
199 WebKitUserStyleSheet* userStyleSheet = static_cast<WebKitUserStyleSheet*>(fastMalloc(sizeof(WebKitUserStyleSheet)));
200 new (userStyleSheet) WebKitUserStyleSheet(source, injectedFrames, level, whitelist, blacklist, webkitUserContentWorld(worldName));
201 return userStyleSheet;
202}
203
204API::UserStyleSheet& webkitUserStyleSheetGetUserStyleSheet(WebKitUserStyleSheet* userStyleSheet)
205{
206 return *userStyleSheet->userStyleSheet;
207}
208
209struct _WebKitUserScript {
210 _WebKitUserScript(const gchar* source, WebKitUserContentInjectedFrames injectedFrames, WebKitUserScriptInjectionTime injectionTime, const gchar* const* whitelist, const gchar* const* blacklist, API::UserContentWorld& world)
211 : userScript(adoptRef(new API::UserScript(UserScript {
212 String::fromUTF8(source), URL { },
213 toStringVector(whitelist), toStringVector(blacklist),
214 toUserScriptInjectionTime(injectionTime),
215 toUserContentInjectedFrames(injectedFrames) }, world)))
216 , referenceCount(1)
217 {
218 }
219
220 RefPtr<API::UserScript> userScript;
221 int referenceCount;
222};
223
224G_DEFINE_BOXED_TYPE(WebKitUserScript, webkit_user_script, webkit_user_script_ref, webkit_user_script_unref)
225
226/**
227 * webkit_user_script_ref:
228 * @user_script: a #WebKitUserScript
229 *
230 * Atomically increments the reference count of @user_script by one.
231 * This function is MT-safe and may be called from any thread.
232 *
233 * Returns: The passed #WebKitUserScript
234 *
235 * Since: 2.6
236 */
237WebKitUserScript* webkit_user_script_ref(WebKitUserScript* userScript)
238{
239 g_atomic_int_inc(&userScript->referenceCount);
240 return userScript;
241}
242
243/**
244 * webkit_user_script_unref:
245 * @user_script: a #WebKitUserScript
246 *
247 * Atomically decrements the reference count of @user_script by one.
248 * If the reference count drops to 0, all memory allocated by
249 * #WebKitUserScript is released. This function is MT-safe and may be called
250 * from any thread.
251 *
252 * Since: 2.6
253 */
254void webkit_user_script_unref(WebKitUserScript* userScript)
255{
256 if (g_atomic_int_dec_and_test(&userScript->referenceCount)) {
257 userScript->~WebKitUserScript();
258 fastFree(userScript);
259 }
260}
261
262/**
263 * webkit_user_script_new:
264 * @source: Source code of the user script.
265 * @injected_frames: A #WebKitUserContentInjectedFrames value
266 * @injection_time: A #WebKitUserScriptInjectionTime value
267 * @whitelist: (array zero-terminated=1) (allow-none): A whitelist of URI patterns or %NULL
268 * @blacklist: (array zero-terminated=1) (allow-none): A blacklist of URI patterns or %NULL
269 *
270 * Creates a new user script. Scripts can be applied to some URIs
271 * only by passing non-null values for @whitelist or @blacklist. Passing a
272 * %NULL whitelist implies that all URIs are on the whitelist. The script
273 * is applied if an URI matches the whitelist and not the blacklist.
274 * URI patterns must be of the form `[protocol]://[host]/[path]`, where the
275 * *host* and *path* components can contain the wildcard character (`*`) to
276 * represent zero or more other characters.
277 *
278 * Returns: A new #WebKitUserScript
279 *
280 * Since: 2.6
281 */
282WebKitUserScript* webkit_user_script_new(const gchar* source, WebKitUserContentInjectedFrames injectedFrames, WebKitUserScriptInjectionTime injectionTime, const gchar* const* whitelist, const gchar* const* blacklist)
283{
284 g_return_val_if_fail(source, nullptr);
285 WebKitUserScript* userScript = static_cast<WebKitUserScript*>(fastMalloc(sizeof(WebKitUserScript)));
286 new (userScript) WebKitUserScript(source, injectedFrames, injectionTime, whitelist, blacklist, API::UserContentWorld::normalWorld());
287 return userScript;
288}
289
290/**
291 * webkit_user_script_new_for_world:
292 * @source: Source code of the user script.
293 * @injected_frames: A #WebKitUserContentInjectedFrames value
294 * @injection_time: A #WebKitUserScriptInjectionTime value
295 * @world_name: the name of a #WebKitScriptWorld
296 * @whitelist: (array zero-terminated=1) (allow-none): A whitelist of URI patterns or %NULL
297 * @blacklist: (array zero-terminated=1) (allow-none): A blacklist of URI patterns or %NULL
298 *
299 * Creates a new user script for script world with name @world_name.
300 * See webkit_user_script_new() for a full description.
301 *
302 * Returns: A new #WebKitUserScript
303 *
304 * Since: 2.22
305 */
306WebKitUserScript* webkit_user_script_new_for_world(const gchar* source, WebKitUserContentInjectedFrames injectedFrames, WebKitUserScriptInjectionTime injectionTime, const char* worldName, const gchar* const* whitelist, const gchar* const* blacklist)
307{
308 g_return_val_if_fail(source, nullptr);
309 g_return_val_if_fail(worldName, nullptr);
310
311 WebKitUserScript* userScript = static_cast<WebKitUserScript*>(fastMalloc(sizeof(WebKitUserScript)));
312 new (userScript) WebKitUserScript(source, injectedFrames, injectionTime, whitelist, blacklist, webkitUserContentWorld(worldName));
313 return userScript;
314}
315
316API::UserScript& webkitUserScriptGetUserScript(WebKitUserScript* userScript)
317{
318 return *userScript->userScript;
319}
320
321
322struct _WebKitUserContentFilter {
323 _WebKitUserContentFilter(RefPtr<API::ContentRuleList>&& contentRuleList)
324 : identifier(contentRuleList->name().utf8())
325 , contentRuleList(WTFMove(contentRuleList))
326 , referenceCount(1)
327 {
328 }
329
330 CString identifier;
331 RefPtr<API::ContentRuleList> contentRuleList;
332 int referenceCount;
333};
334
335G_DEFINE_BOXED_TYPE(WebKitUserContentFilter, webkit_user_content_filter, webkit_user_content_filter_ref, webkit_user_content_filter_unref)
336
337/**
338 * webkit_user_content_filter_ref:
339 * @user_content_filter: A #WebKitUserContentFilter
340 *
341 * Atomically increments the reference count of @user_content_filter by one.
342 * This function is MT-safe and may be called from any thread.
343 *
344 * Since: 2.24
345 */
346WebKitUserContentFilter* webkit_user_content_filter_ref(WebKitUserContentFilter* userContentFilter)
347{
348 g_return_val_if_fail(userContentFilter, nullptr);
349 g_atomic_int_inc(&userContentFilter->referenceCount);
350 return userContentFilter;
351}
352
353/**
354 * webkit_user_content_filter_unref:
355 * @user_content_filter: A #WebKitUserContentFilter
356 *
357 * Atomically decrements the reference count of @user_content_filter by one.
358 * If the reference count drops to 0, all the memory allocated by the
359 * #WebKitUserContentFilter is released. This function is MT-safe and may
360 * be called from any thread.
361 *
362 * Since: 2.24
363 */
364void webkit_user_content_filter_unref(WebKitUserContentFilter* userContentFilter)
365{
366 g_return_if_fail(userContentFilter);
367 if (g_atomic_int_dec_and_test(&userContentFilter->referenceCount)) {
368 userContentFilter->~WebKitUserContentFilter();
369 fastFree(userContentFilter);
370 }
371}
372
373/**
374 * webkit_user_content_filter_get_identifier:
375 * @user_content_filter: A #WebKitUserContentFilter
376 *
377 * Obtain the identifier previously used to save the @user_content_filter in the
378 * #WebKitUserContentFilterStore.
379 *
380 * Returns: (transfer none): the identifier for the filter
381 *
382 * Since: 2.24
383 */
384const char* webkit_user_content_filter_get_identifier(WebKitUserContentFilter* userContentFilter)
385{
386 g_return_val_if_fail(userContentFilter, nullptr);
387 return userContentFilter->identifier.data();
388}
389
390WebKitUserContentFilter* webkitUserContentFilterCreate(RefPtr<API::ContentRuleList>&& contentRuleList)
391{
392 WebKitUserContentFilter* userContentFilter = static_cast<WebKitUserContentFilter*>(fastMalloc(sizeof(WebKitUserContentFilter)));
393 new (userContentFilter) WebKitUserContentFilter(WTFMove(contentRuleList));
394 return userContentFilter;
395}
396
397API::ContentRuleList& webkitUserContentFilterGetContentRuleList(WebKitUserContentFilter* userContentFilter)
398{
399 ASSERT(userContentFilter);
400 return *userContentFilter->contentRuleList;
401}
402