blob: 994bd2ede8655e566b87e4df07a2d8bb8dea3113 [file] [log] [blame]
/*
* Copyright (C) 2014 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "WebKitUserContentManager.h"
#include "APISerializedScriptValue.h"
#include "InjectUserScriptImmediately.h"
#include "WebKitJavascriptResultPrivate.h"
#include "WebKitUserContentManagerPrivate.h"
#include "WebKitUserContentPrivate.h"
#include "WebKitWebContextPrivate.h"
#include "WebScriptMessageHandler.h"
#include <wtf/glib/GRefPtr.h>
#include <wtf/glib/WTFGType.h>
#if PLATFORM(WPE)
#include "WPEView.h"
#endif
using namespace WebCore;
using namespace WebKit;
struct _WebKitUserContentManagerPrivate {
_WebKitUserContentManagerPrivate()
: userContentController(adoptRef(new WebUserContentControllerProxy))
{
}
RefPtr<WebUserContentControllerProxy> userContentController;
};
/**
* SECTION:WebKitUserContentManager
* @short_description: Manages user-defined content which affects web pages.
* @title: WebKitUserContentManager
*
* Using a #WebKitUserContentManager user CSS style sheets can be set to
* be injected in the web pages loaded by a #WebKitWebView, by
* webkit_user_content_manager_add_style_sheet().
*
* To use a #WebKitUserContentManager, it must be created using
* webkit_user_content_manager_new(), and then passed to
* webkit_web_view_new_with_user_content_manager(). User style
* sheets can be created with webkit_user_style_sheet_new().
*
* User style sheets can be added and removed at any time, but
* they will affect the web pages loaded afterwards.
*
* Since: 2.6
*/
WEBKIT_DEFINE_TYPE(WebKitUserContentManager, webkit_user_content_manager, G_TYPE_OBJECT)
enum {
SCRIPT_MESSAGE_RECEIVED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0, };
static void webkit_user_content_manager_class_init(WebKitUserContentManagerClass* klass)
{
GObjectClass* gObjectClass = G_OBJECT_CLASS(klass);
/**
* WebKitUserContentManager::script-message-received:
* @manager: the #WebKitUserContentManager
* @js_result: the #WebKitJavascriptResult holding the value received from the JavaScript world.
*
* This signal is emitted when JavaScript in a web view calls
* <code>window.webkit.messageHandlers.&lt;name&gt;.postMessage()</code>, after registering
* <code>&lt;name&gt;</code> using
* webkit_user_content_manager_register_script_message_handler()
*
* Since: 2.8
*/
signals[SCRIPT_MESSAGE_RECEIVED] =
g_signal_new(
"script-message-received",
G_TYPE_FROM_CLASS(gObjectClass),
static_cast<GSignalFlags>(G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED),
0, nullptr, nullptr,
g_cclosure_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
WEBKIT_TYPE_JAVASCRIPT_RESULT);
}
/**
* webkit_user_content_manager_new:
*
* Creates a new user content manager.
*
* Returns: A #WebKitUserContentManager
*
* Since: 2.6
*/
WebKitUserContentManager* webkit_user_content_manager_new()
{
return WEBKIT_USER_CONTENT_MANAGER(g_object_new(WEBKIT_TYPE_USER_CONTENT_MANAGER, nullptr));
}
/**
* webkit_user_content_manager_add_style_sheet:
* @manager: A #WebKitUserContentManager
* @stylesheet: A #WebKitUserStyleSheet
*
* Adds a #WebKitUserStyleSheet to the given #WebKitUserContentManager.
* The same #WebKitUserStyleSheet can be reused with multiple
* #WebKitUserContentManager instances.
*
* Since: 2.6
*/
void webkit_user_content_manager_add_style_sheet(WebKitUserContentManager* manager, WebKitUserStyleSheet* styleSheet)
{
g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
g_return_if_fail(styleSheet);
manager->priv->userContentController->addUserStyleSheet(webkitUserStyleSheetGetUserStyleSheet(styleSheet));
}
/**
* webkit_user_content_manager_remove_all_style_sheets:
* @manager: A #WebKitUserContentManager
*
* Removes all user style sheets from the given #WebKitUserContentManager.
*
* Since: 2.6
*/
void webkit_user_content_manager_remove_all_style_sheets(WebKitUserContentManager* manager)
{
g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
manager->priv->userContentController->removeAllUserStyleSheets();
}
/**
* webkit_user_content_manager_add_script:
* @manager: A #WebKitUserContentManager
* @script: A #WebKitUserScript
*
* Adds a #WebKitUserScript to the given #WebKitUserContentManager.
* The same #WebKitUserScript can be reused with multiple
* #WebKitUserContentManager instances.
*
* Since: 2.6
*/
void webkit_user_content_manager_add_script(WebKitUserContentManager* manager, WebKitUserScript* script)
{
g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
g_return_if_fail(script);
manager->priv->userContentController->addUserScript(webkitUserScriptGetUserScript(script), InjectUserScriptImmediately::No);
}
/**
* webkit_user_content_manager_remove_all_scripts:
* @manager: A #WebKitUserContentManager
*
* Removes all user scripts from the given #WebKitUserContentManager
*
* Since: 2.6
*/
void webkit_user_content_manager_remove_all_scripts(WebKitUserContentManager* manager)
{
g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
manager->priv->userContentController->removeAllUserScripts();
}
class ScriptMessageClientGtk final : public WebScriptMessageHandler::Client {
WTF_MAKE_FAST_ALLOCATED;
public:
ScriptMessageClientGtk(WebKitUserContentManager* manager, const char* handlerName)
: m_handlerName(g_quark_from_string(handlerName))
, m_manager(manager)
{
}
void didPostMessage(WebPageProxy&, FrameInfoData&&, WebCore::SerializedScriptValue& serializedScriptValue) override
{
WebKitJavascriptResult* jsResult = webkitJavascriptResultCreate(serializedScriptValue);
g_signal_emit(m_manager, signals[SCRIPT_MESSAGE_RECEIVED], m_handlerName, jsResult);
webkit_javascript_result_unref(jsResult);
}
virtual ~ScriptMessageClientGtk() { }
private:
GQuark m_handlerName;
WebKitUserContentManager* m_manager;
};
/**
* webkit_user_content_manager_register_script_message_handler:
* @manager: A #WebKitUserContentManager
* @name: Name of the script message channel
*
* Registers a new user script message handler. After it is registered,
* scripts can use `window.webkit.messageHandlers.&lt;name&gt;.postMessage(value)`
* to send messages. Those messages are received by connecting handlers
* to the #WebKitUserContentManager::script-message-received signal. The
* handler name is used as the detail of the signal. To avoid race
* conditions between registering the handler name, and starting to
* receive the signals, it is recommended to connect to the signal
* *before* registering the handler name:
*
* <informalexample><programlisting>
* WebKitWebView *view = webkit_web_view_new ();
* WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager ();
* g_signal_connect (manager, "script-message-received::foobar",
* G_CALLBACK (handle_script_message), NULL);
* webkit_user_content_manager_register_script_message_handler (manager, "foobar");
* </programlisting></informalexample>
*
* Registering a script message handler will fail if the requested
* name has been already registered before.
*
* Returns: %TRUE if message handler was registered successfully, or %FALSE otherwise.
*
* Since: 2.8
*/
gboolean webkit_user_content_manager_register_script_message_handler(WebKitUserContentManager* manager, const char* name)
{
g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager), FALSE);
g_return_val_if_fail(name, FALSE);
Ref<WebScriptMessageHandler> handler =
WebScriptMessageHandler::create(makeUnique<ScriptMessageClientGtk>(manager, name), String::fromUTF8(name), API::UserContentWorld::normalWorld());
return manager->priv->userContentController->addUserScriptMessageHandler(handler.get());
}
/**
* webkit_user_content_manager_unregister_script_message_handler:
* @manager: A #WebKitUserContentManager
* @name: Name of the script message channel
*
* Unregisters a previously registered message handler.
*
* Note that this does *not* disconnect handlers for the
* #WebKitUserContentManager::script-message-received signal;
* they will be kept connected, but the signal will not be emitted
* unless the handler name is registered again.
*
* See also webkit_user_content_manager_register_script_message_handler().
*
* Since: 2.8
*/
void webkit_user_content_manager_unregister_script_message_handler(WebKitUserContentManager* manager, const char* name)
{
g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
g_return_if_fail(name);
manager->priv->userContentController->removeUserMessageHandlerForName(String::fromUTF8(name), API::UserContentWorld::normalWorld());
}
/**
* webkit_user_content_manager_register_script_message_handler_in_world:
* @manager: A #WebKitUserContentManager
* @name: Name of the script message channel
* @world_name: the name of a #WebKitScriptWorld
*
* Registers a new user script message handler in script world with name @world_name.
* See webkit_user_content_manager_register_script_message_handler() for full description.
*
* Registering a script message handler will fail if the requested
* name has been already registered before.
*
* Returns: %TRUE if message handler was registered successfully, or %FALSE otherwise.
*
* Since: 2.22
*/
gboolean webkit_user_content_manager_register_script_message_handler_in_world(WebKitUserContentManager* manager, const char* name, const char* worldName)
{
g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager), FALSE);
g_return_val_if_fail(name, FALSE);
g_return_val_if_fail(worldName, FALSE);
Ref<WebScriptMessageHandler> handler =
WebScriptMessageHandler::create(makeUnique<ScriptMessageClientGtk>(manager, name), String::fromUTF8(name), webkitUserContentWorld(worldName));
return manager->priv->userContentController->addUserScriptMessageHandler(handler.get());
}
/**
* webkit_user_content_manager_unregister_script_message_handler_in_world:
* @manager: A #WebKitUserContentManager
* @name: Name of the script message channel
* @world_name: the name of a #WebKitScriptWorld
*
* Unregisters a previously registered message handler in script world with name @world_name.
*
* Note that this does *not* disconnect handlers for the
* #WebKitUserContentManager::script-message-received signal;
* they will be kept connected, but the signal will not be emitted
* unless the handler name is registered again.
*
* See also webkit_user_content_manager_register_script_message_handler_in_world().
*
* Since: 2.22
*/
void webkit_user_content_manager_unregister_script_message_handler_in_world(WebKitUserContentManager* manager, const char* name, const char* worldName)
{
g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
g_return_if_fail(name);
g_return_if_fail(worldName);
manager->priv->userContentController->removeUserMessageHandlerForName(String::fromUTF8(name), webkitUserContentWorld(worldName));
}
/**
* webkit_user_content_manager_add_filter:
* @manager: A #WebKitUserContentManager
* @filter: A #WebKitUserContentFilter
*
* Adds a #WebKitUserContentFilter to the given #WebKitUserContentManager.
* The same #WebKitUserContentFilter can be reused with multiple
* #WebKitUserContentManager instances.
*
* Filters need to be saved and loaded from #WebKitUserContentFilterStore.
*
* Since: 2.24
*/
void webkit_user_content_manager_add_filter(WebKitUserContentManager* manager, WebKitUserContentFilter* filter)
{
g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
g_return_if_fail(filter);
manager->priv->userContentController->addContentRuleList(webkitUserContentFilterGetContentRuleList(filter));
}
/**
* webkit_user_content_manager_remove_filter:
* @manager: A #WebKitUserContentManager
* @filter: A #WebKitUserContentFilter
*
* Removes a filter from the given #WebKitUserContentManager.
*
* Since 2.24
*/
void webkit_user_content_manager_remove_filter(WebKitUserContentManager* manager, WebKitUserContentFilter* filter)
{
g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
g_return_if_fail(filter);
manager->priv->userContentController->removeContentRuleList(webkitUserContentFilterGetContentRuleList(filter).name());
}
/**
* webkit_user_content_manager_remove_filter_by_id:
* @manager: A #WebKitUserContentManager
* @filter_id: Filter identifier
*
* Removes a filter from the given #WebKitUserContentManager given the
* identifier of a #WebKitUserContentFilter as returned by
* webkit_user_content_filter_get_identifier().
*
* Since: 2.26
*/
void webkit_user_content_manager_remove_filter_by_id(WebKitUserContentManager* manager, const char* filterId)
{
g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
g_return_if_fail(filterId);
manager->priv->userContentController->removeContentRuleList(String::fromUTF8(filterId));
}
/**
* webkit_user_content_manager_remove_all_filters:
* @manager: A #WebKitUserContentManager
*
* Removes all content filters from the given #WebKitUserContentManager.
*
* Since: 2.24
*/
void webkit_user_content_manager_remove_all_filters(WebKitUserContentManager* manager)
{
g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
manager->priv->userContentController->removeAllContentRuleLists();
}
WebUserContentControllerProxy* webkitUserContentManagerGetUserContentControllerProxy(WebKitUserContentManager* manager)
{
return manager->priv->userContentController.get();
}