[GStreamer][MiniBrowser] Honor GStreamer command line parameters in MiniBrowser
[WebKit-https.git] / Tools / MiniBrowser / gtk / main.c
1 /*
2  * Copyright (C) 2006, 2007 Apple Inc.
3  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4  * Copyright (C) 2011 Igalia S.L.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "cmakeconfig.h"
29
30 #include "BrowserWindow.h"
31 #include <JavaScriptCore/JavaScript.h>
32 #include <errno.h>
33 #include <gst/gst.h>
34 #include <gtk/gtk.h>
35 #include <string.h>
36 #include <webkit2/webkit2.h>
37
38 #define MINI_BROWSER_ERROR (miniBrowserErrorQuark())
39
40 static const gchar **uriArguments = NULL;
41 static const gchar **ignoreHosts = NULL;
42 static GdkRGBA *backgroundColor;
43 static gboolean editorMode;
44 static const char *sessionFile;
45 static char *geometry;
46 static gboolean privateMode;
47 static gboolean automationMode;
48 static gboolean fullScreen;
49 static gboolean enableIntelligentTrackingPrevention;
50 static const char *proxy;
51
52 typedef enum {
53     MINI_BROWSER_ERROR_INVALID_ABOUT_PATH
54 } MiniBrowserError;
55
56 static GQuark miniBrowserErrorQuark()
57 {
58     return g_quark_from_string("minibrowser-quark");
59 }
60
61 static gchar *argumentToURL(const char *filename)
62 {
63     GFile *gfile = g_file_new_for_commandline_arg(filename);
64     gchar *fileURL = g_file_get_uri(gfile);
65     g_object_unref(gfile);
66
67     return fileURL;
68 }
69
70 static WebKitWebView *createBrowserTab(BrowserWindow *window, WebKitSettings *webkitSettings, WebKitUserContentManager *userContentManager)
71 {
72     WebKitWebView *webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
73         "web-context", browser_window_get_web_context(window),
74         "settings", webkitSettings,
75         "user-content-manager", userContentManager,
76         "is-controlled-by-automation", automationMode,
77         NULL));
78
79     if (editorMode)
80         webkit_web_view_set_editable(webView, TRUE);
81
82     browser_window_append_view(window, webView);
83     return webView;
84 }
85
86 static gboolean parseBackgroundColor(const char *optionName, const char *value, gpointer data, GError **error)
87 {
88     GdkRGBA rgba;
89     if (gdk_rgba_parse(&rgba, value)) {
90         backgroundColor = gdk_rgba_copy(&rgba);
91         return TRUE;
92     }
93
94     g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Failed to parse '%s' as RGBA color", value);
95     return FALSE;
96 }
97
98 static const GOptionEntry commandLineOptions[] =
99 {
100     { "bg-color", 0, 0, G_OPTION_ARG_CALLBACK, parseBackgroundColor, "Background color", NULL },
101     { "editor-mode", 'e', 0, G_OPTION_ARG_NONE, &editorMode, "Run in editor mode", NULL },
102     { "session-file", 's', 0, G_OPTION_ARG_FILENAME, &sessionFile, "Session file", "FILE" },
103     { "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry, "Set the size and position of the window (WIDTHxHEIGHT+X+Y)", "GEOMETRY" },
104     { "full-screen", 'f', 0, G_OPTION_ARG_NONE, &fullScreen, "Set the window to full-screen mode", NULL },
105     { "private", 'p', 0, G_OPTION_ARG_NONE, &privateMode, "Run in private browsing mode", NULL },
106     { "automation", 0, 0, G_OPTION_ARG_NONE, &automationMode, "Run in automation mode", NULL },
107     { "enable-itp", 0, 0, G_OPTION_ARG_NONE, &enableIntelligentTrackingPrevention, "Enable intelligent tracking prevention", NULL },
108     { "proxy", 0, 0, G_OPTION_ARG_STRING, &proxy, "Set proxy", "PROXY" },
109     { "ignore-host", 0, 0, G_OPTION_ARG_STRING_ARRAY, &ignoreHosts, "Set proxy ignore hosts", "HOSTS" },
110     { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uriArguments, 0, "[URL…]" },
111     { 0, 0, 0, 0, 0, 0, 0 }
112 };
113
114 static gboolean parseOptionEntryCallback(const gchar *optionNameFull, const gchar *value, WebKitSettings *webSettings, GError **error)
115 {
116     if (strlen(optionNameFull) <= 2) {
117         g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Invalid option %s", optionNameFull);
118         return FALSE;
119     }
120
121     /* We have two -- in option name so remove them. */
122     const gchar *optionName = optionNameFull + 2;
123     GParamSpec *spec = g_object_class_find_property(G_OBJECT_GET_CLASS(webSettings), optionName);
124     if (!spec) {
125         g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Cannot find web settings for option %s", optionNameFull);
126         return FALSE;
127     }
128
129     switch (G_PARAM_SPEC_VALUE_TYPE(spec)) {
130     case G_TYPE_BOOLEAN: {
131         gboolean propertyValue = !(value && g_ascii_strcasecmp(value, "true") && strcmp(value, "1"));
132         g_object_set(G_OBJECT(webSettings), optionName, propertyValue, NULL);
133         break;
134     }
135     case G_TYPE_STRING:
136         g_object_set(G_OBJECT(webSettings), optionName, value, NULL);
137         break;
138     case G_TYPE_INT: {
139         glong propertyValue;
140         gchar *end;
141
142         errno = 0;
143         propertyValue = g_ascii_strtoll(value, &end, 0);
144         if (errno == ERANGE || propertyValue > G_MAXINT || propertyValue < G_MININT) {
145             g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Integer value '%s' for %s out of range", value, optionNameFull);
146             return FALSE;
147         }
148         if (errno || value == end) {
149             g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Cannot parse integer value '%s' for %s", value, optionNameFull);
150             return FALSE;
151         }
152         g_object_set(G_OBJECT(webSettings), optionName, propertyValue, NULL);
153         break;
154     }
155     case G_TYPE_FLOAT: {
156         gdouble propertyValue;
157         gchar *end;
158
159         errno = 0;
160         propertyValue = g_ascii_strtod(value, &end);
161         if (errno == ERANGE || propertyValue > G_MAXFLOAT || propertyValue < G_MINFLOAT) {
162             g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Float value '%s' for %s out of range", value, optionNameFull);
163             return FALSE;
164         }
165         if (errno || value == end) {
166             g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Cannot parse float value '%s' for %s", value, optionNameFull);
167             return FALSE;
168         }
169         g_object_set(G_OBJECT(webSettings), optionName, propertyValue, NULL);
170         break;
171     }
172     default:
173         g_assert_not_reached();
174     }
175
176     return TRUE;
177 }
178
179 static gboolean isValidParameterType(GType gParamType)
180 {
181     return (gParamType == G_TYPE_BOOLEAN || gParamType == G_TYPE_STRING || gParamType == G_TYPE_INT
182             || gParamType == G_TYPE_FLOAT);
183 }
184
185 static GOptionEntry* getOptionEntriesFromWebKitSettings(WebKitSettings *webSettings)
186 {
187     GParamSpec **propertySpecs;
188     GOptionEntry *optionEntries;
189     guint numProperties, numEntries, i;
190
191     propertySpecs = g_object_class_list_properties(G_OBJECT_GET_CLASS(webSettings), &numProperties);
192     if (!propertySpecs)
193         return NULL;
194
195     optionEntries = g_new0(GOptionEntry, numProperties + 1);
196     numEntries = 0;
197     for (i = 0; i < numProperties; i++) {
198         GParamSpec *param = propertySpecs[i];
199
200         /* Fill in structures only for writable and not construct-only properties. */
201         if (!param || !(param->flags & G_PARAM_WRITABLE) || (param->flags & G_PARAM_CONSTRUCT_ONLY))
202             continue;
203
204         GType gParamType = G_PARAM_SPEC_VALUE_TYPE(param);
205         if (!isValidParameterType(gParamType))
206             continue;
207
208         GOptionEntry *optionEntry = &optionEntries[numEntries++];
209         optionEntry->long_name = g_param_spec_get_name(param);
210
211         /* There is no easy way to figure our short name for generated option entries.
212            optionEntry.short_name=*/
213         /* For bool arguments "enable" type make option argument not required. */
214         if (gParamType == G_TYPE_BOOLEAN && (strstr(optionEntry->long_name, "enable")))
215             optionEntry->flags = G_OPTION_FLAG_OPTIONAL_ARG;
216         optionEntry->arg = G_OPTION_ARG_CALLBACK;
217         optionEntry->arg_data = parseOptionEntryCallback;
218         optionEntry->description = g_param_spec_get_blurb(param);
219         optionEntry->arg_description = g_type_name(gParamType);
220     }
221     g_free(propertySpecs);
222
223     return optionEntries;
224 }
225
226 static gboolean addSettingsGroupToContext(GOptionContext *context, WebKitSettings* webkitSettings)
227 {
228     GOptionEntry *optionEntries = getOptionEntriesFromWebKitSettings(webkitSettings);
229     if (!optionEntries)
230         return FALSE;
231
232     GOptionGroup *webSettingsGroup = g_option_group_new("websettings",
233                                                         "WebKitSettings writable properties for default WebKitWebView",
234                                                         "WebKitSettings properties",
235                                                         webkitSettings,
236                                                         NULL);
237     g_option_group_add_entries(webSettingsGroup, optionEntries);
238     g_free(optionEntries);
239
240     /* Option context takes ownership of the group. */
241     g_option_context_add_group(context, webSettingsGroup);
242
243     return TRUE;
244 }
245
246 typedef struct {
247     WebKitURISchemeRequest *request;
248     GList *dataList;
249     GHashTable *dataMap;
250 } AboutDataRequest;
251
252 static GHashTable *aboutDataRequestMap;
253
254 static void aboutDataRequestFree(AboutDataRequest *request)
255 {
256     if (!request)
257         return;
258
259     g_list_free_full(request->dataList, g_object_unref);
260
261     if (request->request)
262         g_object_unref(request->request);
263     if (request->dataMap)
264         g_hash_table_destroy(request->dataMap);
265
266     g_slice_free(AboutDataRequest, request);
267 }
268
269 static AboutDataRequest* aboutDataRequestNew(WebKitURISchemeRequest *uriRequest)
270 {
271     if (!aboutDataRequestMap)
272         aboutDataRequestMap = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)aboutDataRequestFree);
273
274     AboutDataRequest *request = g_slice_new0(AboutDataRequest);
275     request->request = g_object_ref(uriRequest);
276     g_hash_table_insert(aboutDataRequestMap, GUINT_TO_POINTER(webkit_web_view_get_page_id(webkit_uri_scheme_request_get_web_view(request->request))), request);
277
278     return request;
279 }
280
281 static AboutDataRequest *aboutDataRequestForView(guint64 pageID)
282 {
283     return aboutDataRequestMap ? g_hash_table_lookup(aboutDataRequestMap, GUINT_TO_POINTER(pageID)) : NULL;
284 }
285
286 static void websiteDataRemovedCallback(WebKitWebsiteDataManager *manager, GAsyncResult *result, AboutDataRequest *dataRequest)
287 {
288     if (webkit_website_data_manager_remove_finish(manager, result, NULL))
289         webkit_web_view_reload(webkit_uri_scheme_request_get_web_view(dataRequest->request));
290 }
291
292 static void websiteDataClearedCallback(WebKitWebsiteDataManager *manager, GAsyncResult *result, AboutDataRequest *dataRequest)
293 {
294     if (webkit_website_data_manager_clear_finish(manager, result, NULL))
295         webkit_web_view_reload(webkit_uri_scheme_request_get_web_view(dataRequest->request));
296 }
297
298 static void aboutDataScriptMessageReceivedCallback(WebKitUserContentManager *userContentManager, WebKitJavascriptResult *message, WebKitWebContext *webContext)
299 {
300     JSValueRef jsValue = webkit_javascript_result_get_value(message);
301     JSStringRef jsString = JSValueToStringCopy(webkit_javascript_result_get_global_context(message), jsValue, NULL);
302     size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString);
303     if (!maxSize) {
304         JSStringRelease(jsString);
305         return;
306     }
307     char *messageString = g_malloc(maxSize);
308     JSStringGetUTF8CString(jsString, messageString, maxSize);
309     JSStringRelease(jsString);
310
311     char **tokens = g_strsplit(messageString, ":", 3);
312     g_free(messageString);
313
314     unsigned tokenCount = g_strv_length(tokens);
315     if (!tokens || tokenCount < 2) {
316         g_strfreev(tokens);
317         return;
318     }
319
320     guint64 pageID = g_ascii_strtoull(tokens[0], NULL, 10);
321     AboutDataRequest *dataRequest = aboutDataRequestForView(pageID);
322     if (!dataRequest) {
323         g_strfreev(tokens);
324         return;
325     }
326
327     WebKitWebsiteDataManager *manager = webkit_web_context_get_website_data_manager(webContext);
328     guint64 types = g_ascii_strtoull(tokens[1], NULL, 10);
329     if (tokenCount == 2)
330         webkit_website_data_manager_clear(manager, types, 0, NULL, (GAsyncReadyCallback)websiteDataClearedCallback, dataRequest);
331     else {
332         guint64 domainID = g_ascii_strtoull(tokens[2], NULL, 10);
333         GList *dataList = g_hash_table_lookup(dataRequest->dataMap, GUINT_TO_POINTER(types));
334         WebKitWebsiteData *data = g_list_nth_data(dataList, domainID);
335         if (data) {
336             GList dataList = { data, NULL, NULL };
337             webkit_website_data_manager_remove(manager, types, &dataList, NULL, (GAsyncReadyCallback)websiteDataRemovedCallback, dataRequest);
338         }
339     }
340     g_strfreev(tokens);
341 }
342
343 static void domainListFree(GList *domains)
344 {
345     g_list_free_full(domains, (GDestroyNotify)webkit_website_data_unref);
346 }
347
348 static void aboutDataFillTable(GString *result, AboutDataRequest *dataRequest, GList* dataList, const char *title, WebKitWebsiteDataTypes types, const char *dataPath, guint64 pageID)
349 {
350     guint64 totalDataSize = 0;
351     GList *domains = NULL;
352     GList *l;
353     for (l = dataList; l; l = g_list_next(l)) {
354         WebKitWebsiteData *data = (WebKitWebsiteData *)l->data;
355
356         if (webkit_website_data_get_types(data) & types) {
357             domains = g_list_prepend(domains, webkit_website_data_ref(data));
358             totalDataSize += webkit_website_data_get_size(data, types);
359         }
360     }
361     if (!domains)
362         return;
363
364     if (!dataRequest->dataMap)
365         dataRequest->dataMap = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)domainListFree);
366     g_hash_table_insert(dataRequest->dataMap, GUINT_TO_POINTER(types), domains);
367
368     if (totalDataSize) {
369         char *totalDataSizeStr = g_format_size(totalDataSize);
370         g_string_append_printf(result, "<h1>%s (%s)</h1>\n<table>\n", title, totalDataSizeStr);
371         g_free(totalDataSizeStr);
372     } else
373         g_string_append_printf(result, "<h1>%s</h1>\n<table>\n", title);
374     if (dataPath)
375         g_string_append_printf(result, "<tr><td colspan=\"2\">Path: %s</td></tr>\n", dataPath);
376
377     unsigned index;
378     for (l = domains, index = 0; l; l = g_list_next(l), index++) {
379         WebKitWebsiteData *data = (WebKitWebsiteData *)l->data;
380         const char *displayName = webkit_website_data_get_name(data);
381         guint64 dataSize = webkit_website_data_get_size(data, types);
382         if (dataSize) {
383             char *dataSizeStr = g_format_size(dataSize);
384             g_string_append_printf(result, "<tr><td>%s (%s)</td>", displayName, dataSizeStr);
385             g_free(dataSizeStr);
386         } else
387             g_string_append_printf(result, "<tr><td>%s</td>", displayName);
388         g_string_append_printf(result, "<td><input type=\"button\" value=\"Remove\" onclick=\"removeData('%"G_GUINT64_FORMAT":%u:%u');\"></td></tr>\n",
389             pageID, types, index);
390     }
391     g_string_append_printf(result, "<tr><td><input type=\"button\" value=\"Clear all\" onclick=\"clearData('%"G_GUINT64_FORMAT":%u');\"></td></tr></table>\n",
392         pageID, types);
393 }
394
395 static void gotWebsiteDataCallback(WebKitWebsiteDataManager *manager, GAsyncResult *asyncResult, AboutDataRequest *dataRequest)
396 {
397     GList *dataList = webkit_website_data_manager_fetch_finish(manager, asyncResult, NULL);
398
399     GString *result = g_string_new(
400         "<html><head>"
401         "<script>"
402         "  function removeData(domain) {"
403         "    window.webkit.messageHandlers.aboutData.postMessage(domain);"
404         "  }"
405         "  function clearData(dataType) {"
406         "    window.webkit.messageHandlers.aboutData.postMessage(dataType);"
407         "  }"
408         "</script></head><body>\n");
409
410     guint64 pageID = webkit_web_view_get_page_id(webkit_uri_scheme_request_get_web_view(dataRequest->request));
411     aboutDataFillTable(result, dataRequest, dataList, "Cookies", WEBKIT_WEBSITE_DATA_COOKIES, NULL, pageID);
412     aboutDataFillTable(result, dataRequest, dataList, "Memory Cache", WEBKIT_WEBSITE_DATA_MEMORY_CACHE, NULL, pageID);
413     aboutDataFillTable(result, dataRequest, dataList, "Disk Cache", WEBKIT_WEBSITE_DATA_DISK_CACHE, webkit_website_data_manager_get_disk_cache_directory(manager), pageID);
414     aboutDataFillTable(result, dataRequest, dataList, "Session Storage", WEBKIT_WEBSITE_DATA_SESSION_STORAGE, NULL, pageID);
415     aboutDataFillTable(result, dataRequest, dataList, "Local Storage", WEBKIT_WEBSITE_DATA_LOCAL_STORAGE, webkit_website_data_manager_get_local_storage_directory(manager), pageID);
416     aboutDataFillTable(result, dataRequest, dataList, "WebSQL Databases", WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES, webkit_website_data_manager_get_websql_directory(manager), pageID);
417     aboutDataFillTable(result, dataRequest, dataList, "IndexedDB Databases", WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES, webkit_website_data_manager_get_indexeddb_directory(manager), pageID);
418     aboutDataFillTable(result, dataRequest, dataList, "Plugins Data", WEBKIT_WEBSITE_DATA_PLUGIN_DATA, NULL, pageID);
419     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);
420
421     result = g_string_append(result, "</body></html>");
422     gsize streamLength = result->len;
423     GInputStream *stream = g_memory_input_stream_new_from_data(g_string_free(result, FALSE), streamLength, g_free);
424     webkit_uri_scheme_request_finish(dataRequest->request, stream, streamLength, "text/html");
425     g_list_free_full(dataList, (GDestroyNotify)webkit_website_data_unref);
426 }
427
428 static void aboutDataHandleRequest(WebKitURISchemeRequest *request, WebKitWebContext *webContext)
429 {
430     AboutDataRequest *dataRequest = aboutDataRequestNew(request);
431     WebKitWebsiteDataManager *manager = webkit_web_context_get_website_data_manager(webContext);
432     webkit_website_data_manager_fetch(manager, WEBKIT_WEBSITE_DATA_ALL, NULL, (GAsyncReadyCallback)gotWebsiteDataCallback, dataRequest);
433 }
434
435 static void aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, WebKitWebContext *webContext)
436 {
437     GInputStream *stream;
438     gsize streamLength;
439     const gchar *path;
440     gchar *contents;
441     GError *error;
442
443     path = webkit_uri_scheme_request_get_path(request);
444     if (!g_strcmp0(path, "minibrowser")) {
445         contents = g_strdup_printf("<html><body><h1>WebKitGTK+ MiniBrowser</h1><p>The WebKit2 test browser of the GTK+ port.</p><p>WebKit version: %d.%d.%d</p></body></html>",
446             webkit_get_major_version(),
447             webkit_get_minor_version(),
448             webkit_get_micro_version());
449         streamLength = strlen(contents);
450         stream = g_memory_input_stream_new_from_data(contents, streamLength, g_free);
451
452         webkit_uri_scheme_request_finish(request, stream, streamLength, "text/html");
453         g_object_unref(stream);
454     } else if (!g_strcmp0(path, "data"))
455         aboutDataHandleRequest(request, webContext);
456     else {
457         error = g_error_new(MINI_BROWSER_ERROR, MINI_BROWSER_ERROR_INVALID_ABOUT_PATH, "Invalid about:%s page.", path);
458         webkit_uri_scheme_request_finish_error(request, error);
459         g_error_free(error);
460     }
461 }
462
463 static GtkWidget *createWebViewForAutomationCallback(WebKitAutomationSession* session)
464 {
465     return GTK_WIDGET(browser_window_get_or_create_web_view_for_automation());
466 }
467
468 static void automationStartedCallback(WebKitWebContext *webContext, WebKitAutomationSession *session)
469 {
470     WebKitApplicationInfo *info = webkit_application_info_new();
471     webkit_application_info_set_version(info, WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION);
472     webkit_automation_session_set_application_info(session, info);
473     webkit_application_info_unref(info);
474
475     g_signal_connect(session, "create-web-view", G_CALLBACK(createWebViewForAutomationCallback), NULL);
476 }
477
478 int main(int argc, char *argv[])
479 {
480     gtk_init(&argc, &argv);
481 #if ENABLE_DEVELOPER_MODE
482     g_setenv("WEBKIT_INJECTED_BUNDLE_PATH", WEBKIT_INJECTED_BUNDLE_PATH, FALSE);
483 #endif
484
485     GOptionContext *context = g_option_context_new(NULL);
486     g_option_context_add_main_entries(context, commandLineOptions, 0);
487     g_option_context_add_group(context, gtk_get_option_group(TRUE));
488     g_option_context_add_group(context, gst_init_get_option_group());
489
490     WebKitSettings *webkitSettings = webkit_settings_new();
491     webkit_settings_set_enable_developer_extras(webkitSettings, TRUE);
492     webkit_settings_set_enable_webgl(webkitSettings, TRUE);
493     webkit_settings_set_enable_media_stream(webkitSettings, TRUE);
494     if (!addSettingsGroupToContext(context, webkitSettings))
495         g_clear_object(&webkitSettings);
496
497     GError *error = 0;
498     if (!g_option_context_parse(context, &argc, &argv, &error)) {
499         g_printerr("Cannot parse arguments: %s\n", error->message);
500         g_error_free(error);
501         g_option_context_free(context);
502
503         return 1;
504     }
505     g_option_context_free (context);
506
507     WebKitWebContext *webContext = (privateMode || automationMode) ? webkit_web_context_new_ephemeral() : webkit_web_context_get_default();
508
509     if (proxy) {
510         WebKitNetworkProxySettings *webkitProxySettings = webkit_network_proxy_settings_new(proxy, ignoreHosts);
511         webkit_web_context_set_network_proxy_settings(webContext, WEBKIT_NETWORK_PROXY_MODE_CUSTOM, webkitProxySettings);
512         webkit_network_proxy_settings_free(webkitProxySettings);
513     }
514
515     const gchar *singleprocess = g_getenv("MINIBROWSER_SINGLEPROCESS");
516     webkit_web_context_set_process_model(webContext, (singleprocess && *singleprocess) ?
517         WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS : WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
518
519     // Enable the favicon database, by specifying the default directory.
520     webkit_web_context_set_favicon_database_directory(webContext, NULL);
521
522     WebKitWebsiteDataManager *manager = webkit_web_context_get_website_data_manager(webContext);
523     webkit_website_data_manager_set_resource_load_statistics_enabled(manager, enableIntelligentTrackingPrevention);
524
525     webkit_web_context_register_uri_scheme(webContext, BROWSER_ABOUT_SCHEME, (WebKitURISchemeRequestCallback)aboutURISchemeRequestCallback, webContext, NULL);
526
527     WebKitUserContentManager *userContentManager = webkit_user_content_manager_new();
528     webkit_user_content_manager_register_script_message_handler(userContentManager, "aboutData");
529     g_signal_connect(userContentManager, "script-message-received::aboutData", G_CALLBACK(aboutDataScriptMessageReceivedCallback), webContext);
530
531     webkit_web_context_set_automation_allowed(webContext, automationMode);
532     g_signal_connect(webContext, "automation-started", G_CALLBACK(automationStartedCallback), NULL);
533
534     BrowserWindow *mainWindow = BROWSER_WINDOW(browser_window_new(NULL, webContext));
535     if (fullScreen)
536         gtk_window_fullscreen(GTK_WINDOW(mainWindow));
537     else if (geometry)
538         gtk_window_parse_geometry(GTK_WINDOW(mainWindow), geometry);
539
540     GtkWidget *firstTab = NULL;
541     if (uriArguments) {
542         int i;
543
544         for (i = 0; uriArguments[i]; i++) {
545             WebKitWebView *webView = createBrowserTab(mainWindow, webkitSettings, userContentManager);
546             if (!i)
547                 firstTab = GTK_WIDGET(webView);
548             gchar *url = argumentToURL(uriArguments[i]);
549             webkit_web_view_load_uri(webView, url);
550             g_free(url);
551         }
552     } else {
553         WebKitWebView *webView = createBrowserTab(mainWindow, webkitSettings, userContentManager);
554         firstTab = GTK_WIDGET(webView);
555
556         if (backgroundColor)
557             browser_window_set_background_color(mainWindow, backgroundColor);
558
559         if (!editorMode) {
560             if (sessionFile)
561                 browser_window_load_session(mainWindow, sessionFile);
562             else if (!automationMode)
563                 webkit_web_view_load_uri(webView, BROWSER_DEFAULT_URL);
564         }
565     }
566
567     gtk_widget_grab_focus(firstTab);
568     gtk_widget_show(GTK_WIDGET(mainWindow));
569
570     g_clear_object(&webkitSettings);
571     g_clear_object(&userContentManager);
572
573     gtk_main();
574
575     if (privateMode)
576         g_object_unref(webContext);
577
578     return 0;
579 }