1 | /* |
2 | * Copyright (C) 2014 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 "WebKitUserContentManager.h" |
22 | |
23 | #include "APISerializedScriptValue.h" |
24 | #include "InjectUserScriptImmediately.h" |
25 | #include "WebKitJavascriptResultPrivate.h" |
26 | #include "WebKitUserContentManagerPrivate.h" |
27 | #include "WebKitUserContentPrivate.h" |
28 | #include "WebKitWebContextPrivate.h" |
29 | #include "WebScriptMessageHandler.h" |
30 | #include <wtf/glib/GRefPtr.h> |
31 | #include <wtf/glib/WTFGType.h> |
32 | |
33 | #if PLATFORM(WPE) |
34 | #include "WPEView.h" |
35 | #endif |
36 | |
37 | using namespace WebCore; |
38 | using namespace WebKit; |
39 | |
40 | struct _WebKitUserContentManagerPrivate { |
41 | _WebKitUserContentManagerPrivate() |
42 | : userContentController(adoptRef(new WebUserContentControllerProxy)) |
43 | { |
44 | } |
45 | |
46 | RefPtr<WebUserContentControllerProxy> userContentController; |
47 | }; |
48 | |
49 | /** |
50 | * SECTION:WebKitUserContentManager |
51 | * @short_description: Manages user-defined content which affects web pages. |
52 | * @title: WebKitUserContentManager |
53 | * |
54 | * Using a #WebKitUserContentManager user CSS style sheets can be set to |
55 | * be injected in the web pages loaded by a #WebKitWebView, by |
56 | * webkit_user_content_manager_add_style_sheet(). |
57 | * |
58 | * To use a #WebKitUserContentManager, it must be created using |
59 | * webkit_user_content_manager_new(), and then passed to |
60 | * webkit_web_view_new_with_user_content_manager(). User style |
61 | * sheets can be created with webkit_user_style_sheet_new(). |
62 | * |
63 | * User style sheets can be added and removed at any time, but |
64 | * they will affect the web pages loaded afterwards. |
65 | * |
66 | * Since: 2.6 |
67 | */ |
68 | |
69 | WEBKIT_DEFINE_TYPE(WebKitUserContentManager, webkit_user_content_manager, G_TYPE_OBJECT) |
70 | |
71 | enum { |
72 | SCRIPT_MESSAGE_RECEIVED, |
73 | |
74 | LAST_SIGNAL |
75 | }; |
76 | |
77 | static guint signals[LAST_SIGNAL] = { 0, }; |
78 | |
79 | static void webkit_user_content_manager_class_init(WebKitUserContentManagerClass* klass) |
80 | { |
81 | GObjectClass* gObjectClass = G_OBJECT_CLASS(klass); |
82 | |
83 | /** |
84 | * WebKitUserContentManager::script-message-received: |
85 | * @manager: the #WebKitUserContentManager |
86 | * @js_result: the #WebKitJavascriptResult holding the value received from the JavaScript world. |
87 | * |
88 | * This signal is emitted when JavaScript in a web view calls |
89 | * <code>window.webkit.messageHandlers.<name>.postMessage()</code>, after registering |
90 | * <code><name></code> using |
91 | * webkit_user_content_manager_register_script_message_handler() |
92 | * |
93 | * Since: 2.8 |
94 | */ |
95 | signals[SCRIPT_MESSAGE_RECEIVED] = |
96 | g_signal_new( |
97 | "script-message-received" , |
98 | G_TYPE_FROM_CLASS(gObjectClass), |
99 | static_cast<GSignalFlags>(G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED), |
100 | 0, nullptr, nullptr, |
101 | g_cclosure_marshal_VOID__BOXED, |
102 | G_TYPE_NONE, 1, |
103 | WEBKIT_TYPE_JAVASCRIPT_RESULT); |
104 | } |
105 | |
106 | /** |
107 | * webkit_user_content_manager_new: |
108 | * |
109 | * Creates a new user content manager. |
110 | * |
111 | * Returns: A #WebKitUserContentManager |
112 | * |
113 | * Since: 2.6 |
114 | */ |
115 | WebKitUserContentManager* webkit_user_content_manager_new() |
116 | { |
117 | return WEBKIT_USER_CONTENT_MANAGER(g_object_new(WEBKIT_TYPE_USER_CONTENT_MANAGER, nullptr)); |
118 | } |
119 | |
120 | /** |
121 | * webkit_user_content_manager_add_style_sheet: |
122 | * @manager: A #WebKitUserContentManager |
123 | * @stylesheet: A #WebKitUserStyleSheet |
124 | * |
125 | * Adds a #WebKitUserStyleSheet to the given #WebKitUserContentManager. |
126 | * The same #WebKitUserStyleSheet can be reused with multiple |
127 | * #WebKitUserContentManager instances. |
128 | * |
129 | * Since: 2.6 |
130 | */ |
131 | void webkit_user_content_manager_add_style_sheet(WebKitUserContentManager* manager, WebKitUserStyleSheet* styleSheet) |
132 | { |
133 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager)); |
134 | g_return_if_fail(styleSheet); |
135 | manager->priv->userContentController->addUserStyleSheet(webkitUserStyleSheetGetUserStyleSheet(styleSheet)); |
136 | } |
137 | |
138 | /** |
139 | * webkit_user_content_manager_remove_all_style_sheets: |
140 | * @manager: A #WebKitUserContentManager |
141 | * |
142 | * Removes all user style sheets from the given #WebKitUserContentManager. |
143 | * |
144 | * Since: 2.6 |
145 | */ |
146 | void webkit_user_content_manager_remove_all_style_sheets(WebKitUserContentManager* manager) |
147 | { |
148 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager)); |
149 | manager->priv->userContentController->removeAllUserStyleSheets(); |
150 | } |
151 | |
152 | /** |
153 | * webkit_user_content_manager_add_script: |
154 | * @manager: A #WebKitUserContentManager |
155 | * @script: A #WebKitUserScript |
156 | * |
157 | * Adds a #WebKitUserScript to the given #WebKitUserContentManager. |
158 | * The same #WebKitUserScript can be reused with multiple |
159 | * #WebKitUserContentManager instances. |
160 | * |
161 | * Since: 2.6 |
162 | */ |
163 | void webkit_user_content_manager_add_script(WebKitUserContentManager* manager, WebKitUserScript* script) |
164 | { |
165 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager)); |
166 | g_return_if_fail(script); |
167 | manager->priv->userContentController->addUserScript(webkitUserScriptGetUserScript(script), InjectUserScriptImmediately::No); |
168 | } |
169 | |
170 | /** |
171 | * webkit_user_content_manager_remove_all_scripts: |
172 | * @manager: A #WebKitUserContentManager |
173 | * |
174 | * Removes all user scripts from the given #WebKitUserContentManager |
175 | * |
176 | * Since: 2.6 |
177 | */ |
178 | void webkit_user_content_manager_remove_all_scripts(WebKitUserContentManager* manager) |
179 | { |
180 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager)); |
181 | manager->priv->userContentController->removeAllUserScripts(); |
182 | } |
183 | |
184 | class ScriptMessageClientGtk final : public WebScriptMessageHandler::Client { |
185 | public: |
186 | ScriptMessageClientGtk(WebKitUserContentManager* manager, const char* handlerName) |
187 | : m_handlerName(g_quark_from_string(handlerName)) |
188 | , m_manager(manager) |
189 | { |
190 | } |
191 | |
192 | void didPostMessage(WebPageProxy&, const FrameInfoData&, WebCore::SerializedScriptValue& serializedScriptValue) override |
193 | { |
194 | WebKitJavascriptResult* jsResult = webkitJavascriptResultCreate(serializedScriptValue); |
195 | g_signal_emit(m_manager, signals[SCRIPT_MESSAGE_RECEIVED], m_handlerName, jsResult); |
196 | webkit_javascript_result_unref(jsResult); |
197 | } |
198 | |
199 | virtual ~ScriptMessageClientGtk() { } |
200 | |
201 | private: |
202 | GQuark m_handlerName; |
203 | WebKitUserContentManager* m_manager; |
204 | }; |
205 | |
206 | /** |
207 | * webkit_user_content_manager_register_script_message_handler: |
208 | * @manager: A #WebKitUserContentManager |
209 | * @name: Name of the script message channel |
210 | * |
211 | * Registers a new user script message handler. After it is registered, |
212 | * scripts can use `window.webkit.messageHandlers.<name>.postMessage(value)` |
213 | * to send messages. Those messages are received by connecting handlers |
214 | * to the #WebKitUserContentManager::script-message-received signal. The |
215 | * handler name is used as the detail of the signal. To avoid race |
216 | * conditions between registering the handler name, and starting to |
217 | * receive the signals, it is recommended to connect to the signal |
218 | * *before* registering the handler name: |
219 | * |
220 | * <informalexample><programlisting> |
221 | * WebKitWebView *view = webkit_web_view_new (); |
222 | * WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager (); |
223 | * g_signal_connect (manager, "script-message-received::foobar", |
224 | * G_CALLBACK (handle_script_message), NULL); |
225 | * webkit_user_content_manager_register_script_message_handler (manager, "foobar"); |
226 | * </programlisting></informalexample> |
227 | * |
228 | * Registering a script message handler will fail if the requested |
229 | * name has been already registered before. |
230 | * |
231 | * Returns: %TRUE if message handler was registered successfully, or %FALSE otherwise. |
232 | * |
233 | * Since: 2.8 |
234 | */ |
235 | gboolean webkit_user_content_manager_register_script_message_handler(WebKitUserContentManager* manager, const char* name) |
236 | { |
237 | g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager), FALSE); |
238 | g_return_val_if_fail(name, FALSE); |
239 | |
240 | Ref<WebScriptMessageHandler> handler = |
241 | WebScriptMessageHandler::create(std::make_unique<ScriptMessageClientGtk>(manager, name), String::fromUTF8(name), API::UserContentWorld::normalWorld()); |
242 | return manager->priv->userContentController->addUserScriptMessageHandler(handler.get()); |
243 | } |
244 | |
245 | /** |
246 | * webkit_user_content_manager_unregister_script_message_handler: |
247 | * @manager: A #WebKitUserContentManager |
248 | * @name: Name of the script message channel |
249 | * |
250 | * Unregisters a previously registered message handler. |
251 | * |
252 | * Note that this does *not* disconnect handlers for the |
253 | * #WebKitUserContentManager::script-message-received signal; |
254 | * they will be kept connected, but the signal will not be emitted |
255 | * unless the handler name is registered again. |
256 | * |
257 | * See also webkit_user_content_manager_register_script_message_handler(). |
258 | * |
259 | * Since: 2.8 |
260 | */ |
261 | void webkit_user_content_manager_unregister_script_message_handler(WebKitUserContentManager* manager, const char* name) |
262 | { |
263 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager)); |
264 | g_return_if_fail(name); |
265 | manager->priv->userContentController->removeUserMessageHandlerForName(String::fromUTF8(name), API::UserContentWorld::normalWorld()); |
266 | } |
267 | |
268 | /** |
269 | * webkit_user_content_manager_register_script_message_handler_in_world: |
270 | * @manager: A #WebKitUserContentManager |
271 | * @name: Name of the script message channel |
272 | * @world_name: the name of a #WebKitScriptWorld |
273 | * |
274 | * Registers a new user script message handler in script world with name @world_name. |
275 | * See webkit_user_content_manager_register_script_message_handler() for full description. |
276 | * |
277 | * Registering a script message handler will fail if the requested |
278 | * name has been already registered before. |
279 | * |
280 | * Returns: %TRUE if message handler was registered successfully, or %FALSE otherwise. |
281 | * |
282 | * Since: 2.22 |
283 | */ |
284 | gboolean webkit_user_content_manager_register_script_message_handler_in_world(WebKitUserContentManager* manager, const char* name, const char* worldName) |
285 | { |
286 | g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager), FALSE); |
287 | g_return_val_if_fail(name, FALSE); |
288 | g_return_val_if_fail(worldName, FALSE); |
289 | |
290 | Ref<WebScriptMessageHandler> handler = |
291 | WebScriptMessageHandler::create(std::make_unique<ScriptMessageClientGtk>(manager, name), String::fromUTF8(name), webkitUserContentWorld(worldName)); |
292 | return manager->priv->userContentController->addUserScriptMessageHandler(handler.get()); |
293 | } |
294 | |
295 | /** |
296 | * webkit_user_content_manager_unregister_script_message_handler_in_world: |
297 | * @manager: A #WebKitUserContentManager |
298 | * @name: Name of the script message channel |
299 | * @world_name: the name of a #WebKitScriptWorld |
300 | * |
301 | * Unregisters a previously registered message handler in script world with name @world_name. |
302 | * |
303 | * Note that this does *not* disconnect handlers for the |
304 | * #WebKitUserContentManager::script-message-received signal; |
305 | * they will be kept connected, but the signal will not be emitted |
306 | * unless the handler name is registered again. |
307 | * |
308 | * See also webkit_user_content_manager_register_script_message_handler_in_world(). |
309 | * |
310 | * Since: 2.22 |
311 | */ |
312 | void webkit_user_content_manager_unregister_script_message_handler_in_world(WebKitUserContentManager* manager, const char* name, const char* worldName) |
313 | { |
314 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager)); |
315 | g_return_if_fail(name); |
316 | g_return_if_fail(worldName); |
317 | |
318 | manager->priv->userContentController->removeUserMessageHandlerForName(String::fromUTF8(name), webkitUserContentWorld(worldName)); |
319 | } |
320 | |
321 | /** |
322 | * webkit_user_content_manager_add_filter: |
323 | * @manager: A #WebKitUserContentManager |
324 | * @filter: A #WebKitUserContentFilter |
325 | * |
326 | * Adds a #WebKitUserContentFilter to the given #WebKitUserContentManager. |
327 | * The same #WebKitUserContentFilter can be reused with multiple |
328 | * #WebKitUserContentManager instances. |
329 | * |
330 | * Filters need to be saved and loaded from #WebKitUserContentFilterStore. |
331 | * |
332 | * Since: 2.24 |
333 | */ |
334 | void webkit_user_content_manager_add_filter(WebKitUserContentManager* manager, WebKitUserContentFilter* filter) |
335 | { |
336 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager)); |
337 | g_return_if_fail(filter); |
338 | manager->priv->userContentController->addContentRuleList(webkitUserContentFilterGetContentRuleList(filter)); |
339 | } |
340 | |
341 | /** |
342 | * webkit_user_content_manager_remove_filter: |
343 | * @manager: A #WebKitUserContentManager |
344 | * @filter: A #WebKitUserContentFilter |
345 | * |
346 | * Removes a filter from the given #WebKitUserContentManager. |
347 | * |
348 | * Since 2.24 |
349 | */ |
350 | void webkit_user_content_manager_remove_filter(WebKitUserContentManager* manager, WebKitUserContentFilter* filter) |
351 | { |
352 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager)); |
353 | g_return_if_fail(filter); |
354 | manager->priv->userContentController->removeContentRuleList(webkitUserContentFilterGetContentRuleList(filter).name()); |
355 | } |
356 | |
357 | /** |
358 | * webkit_user_content_manager_remove_all_filters: |
359 | * @manager: A #WebKitUserContentManager |
360 | * |
361 | * Removes all content filters from the given #WebKitUserContentManager. |
362 | * |
363 | * Since: 2.24 |
364 | */ |
365 | void webkit_user_content_manager_remove_all_filters(WebKitUserContentManager* manager) |
366 | { |
367 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager)); |
368 | manager->priv->userContentController->removeAllContentRuleLists(); |
369 | } |
370 | |
371 | WebUserContentControllerProxy* webkitUserContentManagerGetUserContentControllerProxy(WebKitUserContentManager* manager) |
372 | { |
373 | return manager->priv->userContentController.get(); |
374 | } |
375 | |