| 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 | |