summaryrefslogtreecommitdiff
path: root/Tools/MiniBrowser/gtk/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/MiniBrowser/gtk/main.c')
-rw-r--r--Tools/MiniBrowser/gtk/main.c307
1 files changed, 278 insertions, 29 deletions
diff --git a/Tools/MiniBrowser/gtk/main.c b/Tools/MiniBrowser/gtk/main.c
index e383fda83..e5e5618cd 100644
--- a/Tools/MiniBrowser/gtk/main.c
+++ b/Tools/MiniBrowser/gtk/main.c
@@ -12,10 +12,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -25,7 +25,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "cmakeconfig.h"
+
#include "BrowserWindow.h"
+#include <JavaScriptCore/JavaScript.h>
#include <errno.h>
#include <gtk/gtk.h>
#include <string.h>
@@ -34,7 +37,11 @@
#define MINI_BROWSER_ERROR (miniBrowserErrorQuark())
static const gchar **uriArguments = NULL;
-static const char *miniBrowserAboutScheme = "minibrowser-about";
+static GdkRGBA *backgroundColor;
+static gboolean editorMode;
+static const char *sessionFile;
+static char *geometry;
+static gboolean privateMode;
typedef enum {
MINI_BROWSER_ERROR_INVALID_ABOUT_PATH
@@ -54,24 +61,40 @@ static gchar *argumentToURL(const char *filename)
return fileURL;
}
-static void createBrowserWindow(const gchar *uri, WebKitSettings *webkitSettings)
+static WebKitWebView *createBrowserTab(BrowserWindow *window, WebKitSettings *webkitSettings, WebKitUserContentManager *userContentManager)
{
- GtkWidget *webView = webkit_web_view_new();
- GtkWidget *mainWindow = browser_window_new(WEBKIT_WEB_VIEW(webView), NULL);
- gchar *url = argumentToURL(uri);
+ WebKitWebView *webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+ "web-context", browser_window_get_web_context(window),
+ "settings", webkitSettings,
+ "user-content-manager", userContentManager,
+ NULL));
+
+ if (editorMode)
+ webkit_web_view_set_editable(webView, TRUE);
- if (webkitSettings)
- webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webView), webkitSettings);
+ browser_window_append_view(window, webView);
+ return webView;
+}
- browser_window_load_uri(BROWSER_WINDOW(mainWindow), url);
- g_free(url);
+static gboolean parseBackgroundColor(const char *optionName, const char *value, gpointer data, GError **error)
+{
+ GdkRGBA rgba;
+ if (gdk_rgba_parse(&rgba, value)) {
+ backgroundColor = gdk_rgba_copy(&rgba);
+ return TRUE;
+ }
- gtk_widget_grab_focus(webView);
- gtk_widget_show(mainWindow);
+ g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Failed to parse '%s' as RGBA color", value);
+ return FALSE;
}
static const GOptionEntry commandLineOptions[] =
{
+ { "bg-color", 0, 0, G_OPTION_ARG_CALLBACK, parseBackgroundColor, "Background color", NULL },
+ { "editor-mode", 'e', 0, G_OPTION_ARG_NONE, &editorMode, "Run in editor mode", NULL },
+ { "session-file", 's', 0, G_OPTION_ARG_FILENAME, &sessionFile, "Session file", "FILE" },
+ { "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry, "Set the size and position of the window (WIDTHxHEIGHT+X+Y)", "GEOMETRY" },
+ { "private", 'p', 0, G_OPTION_ARG_NONE, &privateMode, "Run in private browsing mode", NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uriArguments, 0, "[URL…]" },
{ 0, 0, 0, 0, 0, 0, 0 }
};
@@ -208,8 +231,196 @@ static gboolean addSettingsGroupToContext(GOptionContext *context, WebKitSetting
return TRUE;
}
-static void
-aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, gpointer userData)
+typedef struct {
+ WebKitURISchemeRequest *request;
+ GList *dataList;
+ GHashTable *dataMap;
+} AboutDataRequest;
+
+static GHashTable *aboutDataRequestMap;
+
+static void aboutDataRequestFree(AboutDataRequest *request)
+{
+ if (!request)
+ return;
+
+ g_list_free_full(request->dataList, g_object_unref);
+
+ if (request->request)
+ g_object_unref(request->request);
+ if (request->dataMap)
+ g_hash_table_destroy(request->dataMap);
+
+ g_slice_free(AboutDataRequest, request);
+}
+
+static AboutDataRequest* aboutDataRequestNew(WebKitURISchemeRequest *uriRequest)
+{
+ if (!aboutDataRequestMap)
+ aboutDataRequestMap = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)aboutDataRequestFree);
+
+ AboutDataRequest *request = g_slice_new0(AboutDataRequest);
+ request->request = g_object_ref(uriRequest);
+ g_hash_table_insert(aboutDataRequestMap, GUINT_TO_POINTER(webkit_web_view_get_page_id(webkit_uri_scheme_request_get_web_view(request->request))), request);
+
+ return request;
+}
+
+static AboutDataRequest *aboutDataRequestForView(guint64 pageID)
+{
+ return aboutDataRequestMap ? g_hash_table_lookup(aboutDataRequestMap, GUINT_TO_POINTER(pageID)) : NULL;
+}
+
+static void websiteDataRemovedCallback(WebKitWebsiteDataManager *manager, GAsyncResult *result, AboutDataRequest *dataRequest)
+{
+ if (webkit_website_data_manager_remove_finish(manager, result, NULL))
+ webkit_web_view_reload(webkit_uri_scheme_request_get_web_view(dataRequest->request));
+}
+
+static void websiteDataClearedCallback(WebKitWebsiteDataManager *manager, GAsyncResult *result, AboutDataRequest *dataRequest)
+{
+ if (webkit_website_data_manager_clear_finish(manager, result, NULL))
+ webkit_web_view_reload(webkit_uri_scheme_request_get_web_view(dataRequest->request));
+}
+
+static void aboutDataScriptMessageReceivedCallback(WebKitUserContentManager *userContentManager, WebKitJavascriptResult *message, WebKitWebContext *webContext)
+{
+ JSValueRef jsValue = webkit_javascript_result_get_value(message);
+ JSStringRef jsString = JSValueToStringCopy(webkit_javascript_result_get_global_context(message), jsValue, NULL);
+ size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString);
+ if (!maxSize) {
+ JSStringRelease(jsString);
+ return;
+ }
+ char *messageString = g_malloc(maxSize);
+ JSStringGetUTF8CString(jsString, messageString, maxSize);
+ JSStringRelease(jsString);
+
+ char **tokens = g_strsplit(messageString, ":", 3);
+ g_free(messageString);
+
+ unsigned tokenCount = g_strv_length(tokens);
+ if (!tokens || tokenCount < 2) {
+ g_strfreev(tokens);
+ return;
+ }
+
+ guint64 pageID = g_ascii_strtoull(tokens[0], NULL, 10);
+ AboutDataRequest *dataRequest = aboutDataRequestForView(pageID);
+ if (!dataRequest) {
+ g_strfreev(tokens);
+ return;
+ }
+
+ WebKitWebsiteDataManager *manager = webkit_web_context_get_website_data_manager(webContext);
+ guint64 types = g_ascii_strtoull(tokens[1], NULL, 10);
+ if (tokenCount == 2)
+ webkit_website_data_manager_clear(manager, types, 0, NULL, (GAsyncReadyCallback)websiteDataClearedCallback, dataRequest);
+ else {
+ guint64 domainID = g_ascii_strtoull(tokens[2], NULL, 10);
+ GList *dataList = g_hash_table_lookup(dataRequest->dataMap, GUINT_TO_POINTER(types));
+ WebKitWebsiteData *data = g_list_nth_data(dataList, domainID);
+ if (data) {
+ GList dataList = { data, NULL, NULL };
+ webkit_website_data_manager_remove(manager, types, &dataList, NULL, (GAsyncReadyCallback)websiteDataRemovedCallback, dataRequest);
+ }
+ }
+ g_strfreev(tokens);
+}
+
+static void domainListFree(GList *domains)
+{
+ g_list_free_full(domains, (GDestroyNotify)webkit_website_data_unref);
+}
+
+static void aboutDataFillTable(GString *result, AboutDataRequest *dataRequest, GList* dataList, const char *title, WebKitWebsiteDataTypes types, const char *dataPath, guint64 pageID)
+{
+ guint64 totalDataSize = 0;
+ GList *domains = NULL;
+ GList *l;
+ for (l = dataList; l; l = g_list_next(l)) {
+ WebKitWebsiteData *data = (WebKitWebsiteData *)l->data;
+
+ if (webkit_website_data_get_types(data) & types) {
+ domains = g_list_prepend(domains, webkit_website_data_ref(data));
+ totalDataSize += webkit_website_data_get_size(data, types);
+ }
+ }
+ if (!domains)
+ return;
+
+ if (!dataRequest->dataMap)
+ dataRequest->dataMap = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)domainListFree);
+ g_hash_table_insert(dataRequest->dataMap, GUINT_TO_POINTER(types), domains);
+
+ if (totalDataSize) {
+ char *totalDataSizeStr = g_format_size(totalDataSize);
+ g_string_append_printf(result, "<h1>%s (%s)</h1>\n<table>\n", title, totalDataSizeStr);
+ g_free(totalDataSizeStr);
+ } else
+ g_string_append_printf(result, "<h1>%s</h1>\n<table>\n", title);
+ if (dataPath)
+ g_string_append_printf(result, "<tr><td colspan=\"2\">Path: %s</td></tr>\n", dataPath);
+
+ unsigned index;
+ for (l = domains, index = 0; l; l = g_list_next(l), index++) {
+ WebKitWebsiteData *data = (WebKitWebsiteData *)l->data;
+ const char *displayName = webkit_website_data_get_name(data);
+ guint64 dataSize = webkit_website_data_get_size(data, types);
+ if (dataSize) {
+ char *dataSizeStr = g_format_size(dataSize);
+ g_string_append_printf(result, "<tr><td>%s (%s)</td>", displayName, dataSizeStr);
+ g_free(dataSizeStr);
+ } else
+ g_string_append_printf(result, "<tr><td>%s</td>", displayName);
+ g_string_append_printf(result, "<td><input type=\"button\" value=\"Remove\" onclick=\"removeData('%"G_GUINT64_FORMAT":%u:%u');\"></td></tr>\n",
+ pageID, types, index);
+ }
+ g_string_append_printf(result, "<tr><td><input type=\"button\" value=\"Clear all\" onclick=\"clearData('%"G_GUINT64_FORMAT":%u');\"></td></tr></table>\n",
+ pageID, types);
+}
+
+static void gotWebsiteDataCallback(WebKitWebsiteDataManager *manager, GAsyncResult *asyncResult, AboutDataRequest *dataRequest)
+{
+ GList *dataList = webkit_website_data_manager_fetch_finish(manager, asyncResult, NULL);
+
+ GString *result = g_string_new(
+ "<html><head>"
+ "<script>"
+ " function removeData(domain) {"
+ " window.webkit.messageHandlers.aboutData.postMessage(domain);"
+ " }"
+ " function clearData(dataType) {"
+ " window.webkit.messageHandlers.aboutData.postMessage(dataType);"
+ " }"
+ "</script></head><body>\n");
+
+ guint64 pageID = webkit_web_view_get_page_id(webkit_uri_scheme_request_get_web_view(dataRequest->request));
+ aboutDataFillTable(result, dataRequest, dataList, "Cookies", WEBKIT_WEBSITE_DATA_COOKIES, NULL, pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "Memory Cache", WEBKIT_WEBSITE_DATA_MEMORY_CACHE, NULL, pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "Disk Cache", WEBKIT_WEBSITE_DATA_DISK_CACHE, webkit_website_data_manager_get_disk_cache_directory(manager), pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "Session Storage", WEBKIT_WEBSITE_DATA_SESSION_STORAGE, NULL, pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "Local Storage", WEBKIT_WEBSITE_DATA_LOCAL_STORAGE, webkit_website_data_manager_get_local_storage_directory(manager), pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "WebSQL Databases", WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES, webkit_website_data_manager_get_websql_directory(manager), pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "IndexedDB Databases", WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES, webkit_website_data_manager_get_indexeddb_directory(manager), pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "Plugins Data", WEBKIT_WEBSITE_DATA_PLUGIN_DATA, NULL, pageID);
+ aboutDataFillTable(result, dataRequest, dataList, "Offline Web Applications Cache", WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE, webkit_website_data_manager_get_offline_application_cache_directory(manager), pageID);
+
+ result = g_string_append(result, "</body></html>");
+ gsize streamLength = result->len;
+ GInputStream *stream = g_memory_input_stream_new_from_data(g_string_free(result, FALSE), streamLength, g_free);
+ webkit_uri_scheme_request_finish(dataRequest->request, stream, streamLength, "text/html");
+ g_list_free_full(dataList, (GDestroyNotify)webkit_website_data_unref);
+}
+
+static void aboutDataHandleRequest(WebKitURISchemeRequest *request, WebKitWebContext *webContext)
+{
+ AboutDataRequest *dataRequest = aboutDataRequestNew(request);
+ WebKitWebsiteDataManager *manager = webkit_web_context_get_website_data_manager(webContext);
+ webkit_website_data_manager_fetch(manager, WEBKIT_WEBSITE_DATA_ALL, NULL, (GAsyncReadyCallback)gotWebsiteDataCallback, dataRequest);
+}
+
+static void aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, WebKitWebContext *webContext)
{
GInputStream *stream;
gsize streamLength;
@@ -228,7 +439,9 @@ aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, gpointer userData
webkit_uri_scheme_request_finish(request, stream, streamLength, "text/html");
g_object_unref(stream);
- } else {
+ } else if (!g_strcmp0(path, "data"))
+ aboutDataHandleRequest(request, webContext);
+ else {
error = g_error_new(MINI_BROWSER_ERROR, MINI_BROWSER_ERROR_INVALID_ABOUT_PATH, "Invalid about:%s page.", path);
webkit_uri_scheme_request_finish_error(request, error);
g_error_free(error);
@@ -238,12 +451,9 @@ aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, gpointer userData
int main(int argc, char *argv[])
{
gtk_init(&argc, &argv);
-
- const gchar *multiprocess = g_getenv("MINIBROWSER_MULTIPROCESS");
- if (multiprocess && *multiprocess) {
- webkit_web_context_set_process_model(webkit_web_context_get_default(),
- WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
- }
+#if ENABLE_DEVELOPER_MODE
+ g_setenv("WEBKIT_INJECTED_BUNDLE_PATH", WEBKIT_INJECTED_BUNDLE_PATH, FALSE);
+#endif
GOptionContext *context = g_option_context_new(NULL);
g_option_context_add_main_entries(context, commandLineOptions, 0);
@@ -252,6 +462,7 @@ int main(int argc, char *argv[])
WebKitSettings *webkitSettings = webkit_settings_new();
webkit_settings_set_enable_developer_extras(webkitSettings, TRUE);
webkit_settings_set_enable_webgl(webkitSettings, TRUE);
+ webkit_settings_set_enable_media_stream(webkitSettings, TRUE);
if (!addSettingsGroupToContext(context, webkitSettings))
g_clear_object(&webkitSettings);
@@ -265,24 +476,62 @@ int main(int argc, char *argv[])
}
g_option_context_free (context);
- g_setenv("WEBKIT_INJECTED_BUNDLE_PATH", WEBKIT_INJECTED_BUNDLE_PATH, FALSE);
+ WebKitWebContext *webContext = privateMode ? webkit_web_context_new_ephemeral() : webkit_web_context_get_default();
+
+ const gchar *singleprocess = g_getenv("MINIBROWSER_SINGLEPROCESS");
+ webkit_web_context_set_process_model(webContext, (singleprocess && *singleprocess) ?
+ WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS : WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
// Enable the favicon database, by specifying the default directory.
- webkit_web_context_set_favicon_database_directory(webkit_web_context_get_default(), NULL);
+ webkit_web_context_set_favicon_database_directory(webContext, NULL);
+
+ webkit_web_context_register_uri_scheme(webContext, BROWSER_ABOUT_SCHEME, (WebKitURISchemeRequestCallback)aboutURISchemeRequestCallback, webContext, NULL);
+
+ WebKitUserContentManager *userContentManager = webkit_user_content_manager_new();
+ webkit_user_content_manager_register_script_message_handler(userContentManager, "aboutData");
+ g_signal_connect(userContentManager, "script-message-received::aboutData", G_CALLBACK(aboutDataScriptMessageReceivedCallback), webContext);
- webkit_web_context_register_uri_scheme(webkit_web_context_get_default(), miniBrowserAboutScheme, aboutURISchemeRequestCallback, NULL, NULL);
+ BrowserWindow *mainWindow = BROWSER_WINDOW(browser_window_new(NULL, webContext));
+ if (geometry)
+ gtk_window_parse_geometry(GTK_WINDOW(mainWindow), geometry);
+ GtkWidget *firstTab = NULL;
if (uriArguments) {
int i;
- for (i = 0; uriArguments[i]; i++)
- createBrowserWindow(uriArguments[i], webkitSettings);
- } else
- createBrowserWindow("http://www.webkitgtk.org/", webkitSettings);
+ for (i = 0; uriArguments[i]; i++) {
+ WebKitWebView *webView = createBrowserTab(mainWindow, webkitSettings, userContentManager);
+ if (!i)
+ firstTab = GTK_WIDGET(webView);
+ gchar *url = argumentToURL(uriArguments[i]);
+ webkit_web_view_load_uri(webView, url);
+ g_free(url);
+ }
+ } else {
+ WebKitWebView *webView = createBrowserTab(mainWindow, webkitSettings, userContentManager);
+ firstTab = GTK_WIDGET(webView);
+
+ if (backgroundColor)
+ browser_window_set_background_color(mainWindow, backgroundColor);
+
+ if (!editorMode) {
+ if (sessionFile)
+ browser_window_load_session(mainWindow, sessionFile);
+ else
+ webkit_web_view_load_uri(webView, BROWSER_DEFAULT_URL);
+ }
+ }
+
+ gtk_widget_grab_focus(firstTab);
+ gtk_widget_show(GTK_WIDGET(mainWindow));
g_clear_object(&webkitSettings);
+ g_clear_object(&userContentManager);
gtk_main();
+ if (privateMode)
+ g_object_unref(webContext);
+
return 0;
}