3ddef2656cc4802bfbda71f2b6ac66d64ec02e29
[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 #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
29 #ifdef BUILDING_WITH_CMAKE
30 #include "cmakeconfig.h"
31 #else
32 #include "autotoolsconfig.h"
33 #endif
34 #endif
35
36 #include "BrowserWindow.h"
37 #include <errno.h>
38 #include <gtk/gtk.h>
39 #include <string.h>
40 #include <webkit2/webkit2.h>
41
42 #define MINI_BROWSER_ERROR (miniBrowserErrorQuark())
43
44 static const gchar **uriArguments = NULL;
45 static const char *miniBrowserAboutScheme = "minibrowser-about";
46
47 typedef enum {
48     MINI_BROWSER_ERROR_INVALID_ABOUT_PATH
49 } MiniBrowserError;
50
51 static GQuark miniBrowserErrorQuark()
52 {
53     return g_quark_from_string("minibrowser-quark");
54 }
55
56 static gchar *argumentToURL(const char *filename)
57 {
58     GFile *gfile = g_file_new_for_commandline_arg(filename);
59     gchar *fileURL = g_file_get_uri(gfile);
60     g_object_unref(gfile);
61
62     return fileURL;
63 }
64
65 static void createBrowserWindow(const gchar *uri, WebKitSettings *webkitSettings)
66 {
67     GtkWidget *webView = webkit_web_view_new();
68     GtkWidget *mainWindow = browser_window_new(WEBKIT_WEB_VIEW(webView), NULL);
69     gchar *url = argumentToURL(uri);
70
71     if (webkitSettings)
72         webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webView), webkitSettings);
73
74     browser_window_load_uri(BROWSER_WINDOW(mainWindow), url);
75     g_free(url);
76
77     gtk_widget_grab_focus(webView);
78     gtk_widget_show(mainWindow);
79 }
80
81 static const GOptionEntry commandLineOptions[] =
82 {
83     { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uriArguments, 0, "[URL…]" },
84     { 0, 0, 0, 0, 0, 0, 0 }
85 };
86
87 static gboolean parseOptionEntryCallback(const gchar *optionNameFull, const gchar *value, WebKitSettings *webSettings, GError **error)
88 {
89     if (strlen(optionNameFull) <= 2) {
90         g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Invalid option %s", optionNameFull);
91         return FALSE;
92     }
93
94     /* We have two -- in option name so remove them. */
95     const gchar *optionName = optionNameFull + 2;
96     GParamSpec *spec = g_object_class_find_property(G_OBJECT_GET_CLASS(webSettings), optionName);
97     if (!spec) {
98         g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "Cannot find web settings for option %s", optionNameFull);
99         return FALSE;
100     }
101
102     switch (G_PARAM_SPEC_VALUE_TYPE(spec)) {
103     case G_TYPE_BOOLEAN: {
104         gboolean propertyValue = !(value && g_ascii_strcasecmp(value, "true") && strcmp(value, "1"));
105         g_object_set(G_OBJECT(webSettings), optionName, propertyValue, NULL);
106         break;
107     }
108     case G_TYPE_STRING:
109         g_object_set(G_OBJECT(webSettings), optionName, value, NULL);
110         break;
111     case G_TYPE_INT: {
112         glong propertyValue;
113         gchar *end;
114
115         errno = 0;
116         propertyValue = g_ascii_strtoll(value, &end, 0);
117         if (errno == ERANGE || propertyValue > G_MAXINT || propertyValue < G_MININT) {
118             g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Integer value '%s' for %s out of range", value, optionNameFull);
119             return FALSE;
120         }
121         if (errno || value == end) {
122             g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Cannot parse integer value '%s' for %s", value, optionNameFull);
123             return FALSE;
124         }
125         g_object_set(G_OBJECT(webSettings), optionName, propertyValue, NULL);
126         break;
127     }
128     case G_TYPE_FLOAT: {
129         gdouble propertyValue;
130         gchar *end;
131
132         errno = 0;
133         propertyValue = g_ascii_strtod(value, &end);
134         if (errno == ERANGE || propertyValue > G_MAXFLOAT || propertyValue < G_MINFLOAT) {
135             g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Float value '%s' for %s out of range", value, optionNameFull);
136             return FALSE;
137         }
138         if (errno || value == end) {
139             g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Cannot parse float value '%s' for %s", value, optionNameFull);
140             return FALSE;
141         }
142         g_object_set(G_OBJECT(webSettings), optionName, propertyValue, NULL);
143         break;
144     }
145     default:
146         g_assert_not_reached();
147     }
148
149     return TRUE;
150 }
151
152 static gboolean isValidParameterType(GType gParamType)
153 {
154     return (gParamType == G_TYPE_BOOLEAN || gParamType == G_TYPE_STRING || gParamType == G_TYPE_INT
155             || gParamType == G_TYPE_FLOAT);
156 }
157
158 static GOptionEntry* getOptionEntriesFromWebKitSettings(WebKitSettings *webSettings)
159 {
160     GParamSpec **propertySpecs;
161     GOptionEntry *optionEntries;
162     guint numProperties, numEntries, i;
163
164     propertySpecs = g_object_class_list_properties(G_OBJECT_GET_CLASS(webSettings), &numProperties);
165     if (!propertySpecs)
166         return NULL;
167
168     optionEntries = g_new0(GOptionEntry, numProperties + 1);
169     numEntries = 0;
170     for (i = 0; i < numProperties; i++) {
171         GParamSpec *param = propertySpecs[i];
172
173         /* Fill in structures only for writable and not construct-only properties. */
174         if (!param || !(param->flags & G_PARAM_WRITABLE) || (param->flags & G_PARAM_CONSTRUCT_ONLY))
175             continue;
176
177         GType gParamType = G_PARAM_SPEC_VALUE_TYPE(param);
178         if (!isValidParameterType(gParamType))
179             continue;
180
181         GOptionEntry *optionEntry = &optionEntries[numEntries++];
182         optionEntry->long_name = g_param_spec_get_name(param);
183
184         /* There is no easy way to figure our short name for generated option entries.
185            optionEntry.short_name=*/
186         /* For bool arguments "enable" type make option argument not required. */
187         if (gParamType == G_TYPE_BOOLEAN && (strstr(optionEntry->long_name, "enable")))
188             optionEntry->flags = G_OPTION_FLAG_OPTIONAL_ARG;
189         optionEntry->arg = G_OPTION_ARG_CALLBACK;
190         optionEntry->arg_data = parseOptionEntryCallback;
191         optionEntry->description = g_param_spec_get_blurb(param);
192         optionEntry->arg_description = g_type_name(gParamType);
193     }
194     g_free(propertySpecs);
195
196     return optionEntries;
197 }
198
199 static gboolean addSettingsGroupToContext(GOptionContext *context, WebKitSettings* webkitSettings)
200 {
201     GOptionEntry *optionEntries = getOptionEntriesFromWebKitSettings(webkitSettings);
202     if (!optionEntries)
203         return FALSE;
204
205     GOptionGroup *webSettingsGroup = g_option_group_new("websettings",
206                                                         "WebKitSettings writable properties for default WebKitWebView",
207                                                         "WebKitSettings properties",
208                                                         webkitSettings,
209                                                         NULL);
210     g_option_group_add_entries(webSettingsGroup, optionEntries);
211     g_free(optionEntries);
212
213     /* Option context takes ownership of the group. */
214     g_option_context_add_group(context, webSettingsGroup);
215
216     return TRUE;
217 }
218
219 static void
220 aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, gpointer userData)
221 {
222     GInputStream *stream;
223     gsize streamLength;
224     const gchar *path;
225     gchar *contents;
226     GError *error;
227
228     path = webkit_uri_scheme_request_get_path(request);
229     if (!g_strcmp0(path, "minibrowser")) {
230         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>",
231             webkit_get_major_version(),
232             webkit_get_minor_version(),
233             webkit_get_micro_version());
234         streamLength = strlen(contents);
235         stream = g_memory_input_stream_new_from_data(contents, streamLength, g_free);
236
237         webkit_uri_scheme_request_finish(request, stream, streamLength, "text/html");
238         g_object_unref(stream);
239     } else {
240         error = g_error_new(MINI_BROWSER_ERROR, MINI_BROWSER_ERROR_INVALID_ABOUT_PATH, "Invalid about:%s page.", path);
241         webkit_uri_scheme_request_finish_error(request, error);
242         g_error_free(error);
243     }
244 }
245
246 int main(int argc, char *argv[])
247 {
248     gtk_init(&argc, &argv);
249
250     const gchar *multiprocess = g_getenv("MINIBROWSER_MULTIPROCESS");
251     if (multiprocess && *multiprocess) {
252         webkit_web_context_set_process_model(webkit_web_context_get_default(),
253             WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
254     }
255
256     GOptionContext *context = g_option_context_new(NULL);
257     g_option_context_add_main_entries(context, commandLineOptions, 0);
258     g_option_context_add_group(context, gtk_get_option_group(TRUE));
259
260     WebKitSettings *webkitSettings = webkit_settings_new();
261     webkit_settings_set_enable_developer_extras(webkitSettings, TRUE);
262     webkit_settings_set_enable_webgl(webkitSettings, TRUE);
263     if (!addSettingsGroupToContext(context, webkitSettings))
264         g_clear_object(&webkitSettings);
265
266     GError *error = 0;
267     if (!g_option_context_parse(context, &argc, &argv, &error)) {
268         g_printerr("Cannot parse arguments: %s\n", error->message);
269         g_error_free(error);
270         g_option_context_free(context);
271
272         return 1;
273     }
274     g_option_context_free (context);
275
276     g_setenv("WEBKIT_INJECTED_BUNDLE_PATH", WEBKIT_INJECTED_BUNDLE_PATH, FALSE);
277
278     // Enable the favicon database, by specifying the default directory.
279     webkit_web_context_set_favicon_database_directory(webkit_web_context_get_default(), NULL);
280
281     webkit_web_context_register_uri_scheme(webkit_web_context_get_default(), miniBrowserAboutScheme, aboutURISchemeRequestCallback, NULL, NULL);
282
283     if (uriArguments) {
284         int i;
285
286         for (i = 0; uriArguments[i]; i++)
287             createBrowserWindow(uriArguments[i], webkitSettings);
288     } else
289         createBrowserWindow("http://www.webkitgtk.org/", webkitSettings);
290
291     g_clear_object(&webkitSettings);
292
293     gtk_main();
294
295     return 0;
296 }