Merge "[Cherry-pick][Text Autosizing] Combine narrow descendants of a cluster into...
[platform/framework/web/webkit-efl.git] / Tools / MiniBrowser / efl / main.c
1 /*
2  * Copyright (C) 2012 Samsung Electronics
3  * Copyright (C) 2012 Intel Corporation. All rights reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "EWebKit2.h"
21 #include "url_utils.h"
22 #include <Ecore.h>
23 #include <Ecore_Evas.h>
24 #include <Eina.h>
25 #include <Elementary.h>
26 #include <Evas.h>
27
28 static const int DEFAULT_WIDTH = 800;
29 static const int DEFAULT_HEIGHT = 600;
30 static const char DEFAULT_URL[] = "http://www.google.com/";
31 static const char APP_NAME[] = "EFL MiniBrowser";
32 static const int TOOL_BAR_ICON_SIZE = 24;
33 static const int TOOL_BAR_BUTTON_SIZE = 32;
34
35 #define info(format, args...)       \
36     do {                            \
37         if (verbose)                \
38             printf(format, ##args); \
39     } while (0)
40
41 static int verbose = 1;
42 static Eina_List *windows = NULL;
43 static char *evas_engine_name = NULL;
44 static Eina_Bool frame_flattening_enabled = EINA_FALSE;
45
46 typedef struct _Browser_Window {
47     Evas_Object *window;
48     Evas_Object *webview;
49     Evas_Object *url_bar;
50     Evas_Object *back_button;
51     Evas_Object *forward_button;
52 } Browser_Window;
53
54 static const Ecore_Getopt options = {
55     "MiniBrowser",
56     "%prog [options] [url]",
57     "0.0.1",
58     "(C)2012 Samsung Electronics\n",
59     "",
60     "Test Web Browser using the Enlightenment Foundation Libraries of WebKit2",
61     EINA_TRUE, {
62         ECORE_GETOPT_STORE_STR
63             ('e', "engine", "ecore-evas engine to use."),
64         ECORE_GETOPT_CALLBACK_NOARGS
65             ('E', "list-engines", "list ecore-evas engines.",
66              ecore_getopt_callback_ecore_evas_list_engines, NULL),
67         ECORE_GETOPT_STORE_DEF_BOOL
68             ('f', "flattening", "frame flattening.", EINA_FALSE),
69         ECORE_GETOPT_VERSION
70             ('V', "version"),
71         ECORE_GETOPT_COPYRIGHT
72             ('R', "copyright"),
73         ECORE_GETOPT_HELP
74             ('h', "help"),
75         ECORE_GETOPT_SENTINEL
76     }
77 };
78
79 static Browser_Window *window_create(const char *url);
80
81 static Browser_Window *browser_window_find(Evas_Object *window)
82 {
83     Eina_List *l;
84     void *data;
85
86     if (!window)
87         return NULL;
88
89     EINA_LIST_FOREACH(windows, l, data) {
90         Browser_Window *browser_window = (Browser_Window *)data;
91         if (browser_window->window == window)
92             return browser_window;
93     }
94     return NULL;
95 }
96
97 static void window_free(Browser_Window *window)
98 {
99     evas_object_del(window->webview);
100     /* The elm_win will take care of freeing its children */
101     evas_object_del(window->window);
102     free(window);
103 }
104
105 static void window_close(Browser_Window *window)
106 {
107     windows = eina_list_remove(windows, window);
108     window_free(window);
109
110     if (!windows)
111         elm_exit();
112 }
113
114 static void
115 on_key_down(void *data, Evas *e, Evas_Object *obj, void *event_info)
116 {
117     Evas_Event_Key_Down *ev = (Evas_Event_Key_Down*) event_info;
118     static const char *encodings[] = {
119         "ISO-8859-1",
120         "UTF-8",
121         NULL
122     };
123     static int currentEncoding = -1;
124     Eina_Bool ctrlPressed = evas_key_modifier_is_set(evas_key_modifier_get(e), "Control");
125
126     if (!strcmp(ev->key, "F1")) {
127         info("Back (F1) was pressed\n");
128         if (!ewk_view_back(obj))
129             info("Back ignored: No back history\n");
130     } else if (!strcmp(ev->key, "F2")) {
131         info("Forward (F2) was pressed\n");
132         if (!ewk_view_forward(obj))
133             info("Forward ignored: No forward history\n");
134     } else if (!strcmp(ev->key, "F3")) {
135         currentEncoding = (currentEncoding + 1) % (sizeof(encodings) / sizeof(encodings[0]));
136         info("Set encoding (F3) pressed. New encoding to %s", encodings[currentEncoding]);
137         ewk_view_custom_encoding_set(obj, encodings[currentEncoding]);
138     } else if (!strcmp(ev->key, "F5")) {
139         info("Reload (F5) was pressed, reloading.\n");
140         ewk_view_reload(obj);
141     } else if (!strcmp(ev->key, "F6")) {
142         info("Stop (F6) was pressed, stop loading.\n");
143         ewk_view_stop(obj);
144     } else if (!strcmp(ev->key, "n") && ctrlPressed) {
145         info("Create new window (Ctrl+n) was pressed.\n");
146         Browser_Window *window = window_create(DEFAULT_URL);
147         windows = eina_list_append(windows, window);
148     }
149 }
150
151 static void
152 view_focus_set(Browser_Window *window, Eina_Bool focus)
153 {
154     /* We steal focus away from elm's focus model and start to do things
155      * manually here, so elm now has no clue what's up. Tell elm that its
156      * toplevel widget is to be unfocused so elm gives up the focus */
157     elm_object_focus_set(elm_object_top_widget_get(window->window), EINA_FALSE);
158     evas_object_focus_set(window->webview, focus);
159 }
160
161 static void
162 on_mouse_down(void *user_data, Evas *e, Evas_Object *webview, void *event_info)
163 {
164     Browser_Window *window = (Browser_Window *)user_data;
165     Evas_Event_Mouse_Down *ev = (Evas_Event_Mouse_Down *)event_info;
166
167     /* Clear selection from the URL bar */
168     elm_entry_select_none(window->url_bar);
169
170     if (ev->button == 1)
171         view_focus_set(window, EINA_TRUE);
172     else if (ev->button == 2)
173         view_focus_set(window, !evas_object_focus_get(webview));
174 }
175
176 static void
177 title_set(Evas_Object *window, const char *title, int progress)
178 {
179     Eina_Strbuf *buffer;
180
181     if (!title || !*title) {
182         elm_win_title_set(window, APP_NAME);
183         return;
184     }
185
186     buffer = eina_strbuf_new();
187     if (progress < 100)
188         eina_strbuf_append_printf(buffer, "%s (%d%%) - %s", title, progress, APP_NAME);
189     else
190         eina_strbuf_append_printf(buffer, "%s - %s", title, APP_NAME);
191
192     elm_win_title_set(window, eina_strbuf_string_get(buffer));
193     eina_strbuf_free(buffer);
194 }
195
196 static void
197 on_title_changed(void *user_data, Evas_Object *webview, void *event_info)
198 {
199     Browser_Window *window = (Browser_Window *)user_data;
200     const char *title = (const char *)event_info;
201
202     title_set(window->window, title, 100);
203 }
204
205 static void
206 on_url_changed(void *user_data, Evas_Object *webview, void *event_info)
207 {
208     Browser_Window *window = (Browser_Window *)user_data;
209     elm_entry_entry_set(window->url_bar, ewk_view_url_get(window->webview));
210 }
211
212 static void
213 on_back_forward_list_changed(void *user_data, Evas_Object *webview, void *event_info)
214 {
215     Browser_Window *window = (Browser_Window *)user_data;
216
217     /* Update navigation buttons state */
218     elm_object_disabled_set(window->back_button, !ewk_view_back_possible(webview));
219     elm_object_disabled_set(window->forward_button, !ewk_view_forward_possible(webview));
220 }
221
222 static void
223 on_new_window(void *user_data, Evas_Object *webview, void *event_info)
224 {
225     Evas_Object **new_view = (Evas_Object **)event_info;
226     Browser_Window *window = window_create(NULL);
227     *new_view = window->webview;
228     windows = eina_list_append(windows, window);
229 }
230
231 static void
232 on_close_window(void *user_data, Evas_Object *webview, void *event_info)
233 {
234     window_close((Browser_Window *)user_data);
235 }
236
237 static void
238 on_progress(void *user_data, Evas_Object *webview, void *event_info)
239 {
240     Browser_Window *window = (Browser_Window *)user_data;
241     double progress = *(double *)event_info;
242
243     title_set(window->window, ewk_view_title_get(window->webview), progress * 100);
244 }
245
246 static void
247 on_error(void *user_data, Evas_Object *webview, void *event_info)
248 {
249     Eina_Strbuf* buffer;
250     const Ewk_Error *error = (const Ewk_Error *)event_info;
251
252     /* This is a cancellation, do not display the error page */
253     if (ewk_error_cancellation_get(error))
254         return;
255
256     buffer = eina_strbuf_new();
257     eina_strbuf_append_printf(buffer, "<html><body><div style=\"color:#ff0000\">ERROR!</div><br><div>Code: %d<br>Description: %s<br>URL: %s</div></body</html>",
258         ewk_error_code_get(error), ewk_error_description_get(error), ewk_error_url_get(error));
259
260     ewk_view_html_string_load(webview, eina_strbuf_string_get(buffer), 0, ewk_error_url_get(error));
261     eina_strbuf_free(buffer);
262 }
263
264 static void
265 on_download_request(void *user_data, Evas_Object *webview, void *event_info)
266 {
267     Ewk_Download_Job *download = (Ewk_Download_Job *)event_info;
268
269     // FIXME: The destination folder should be selected by the user but we set it to '/tmp' for now.
270     Eina_Strbuf *destination_path = eina_strbuf_new();
271
272     const char *suggested_name = ewk_download_job_suggested_filename_get(download);
273     if (suggested_name && *suggested_name)
274         eina_strbuf_append_printf(destination_path, "/tmp/%s", suggested_name);
275     else {
276         // Generate a unique file name since no name was suggested.
277         char unique_path[] = "/tmp/downloaded-file.XXXXXX";
278         // change from org distribution
279         eina_strbuf_append(destination_path, mktemp(unique_path));
280     }
281
282     ewk_download_job_destination_set(download, eina_strbuf_string_get(destination_path));
283     info("Downloading: %s\n", eina_strbuf_string_get(destination_path));
284     eina_strbuf_free(destination_path);
285 }
286
287 static void
288 on_download_finished(void *user_data, Evas_Object *webview, void *event_info)
289 {
290     Ewk_Download_Job *download = (Ewk_Download_Job *)event_info;
291     info("Download finished: %s\n",  ewk_download_job_destination_get(download));
292 }
293
294 static void
295 on_download_failed(void *user_data, Evas_Object *webview, void *event_info)
296 {
297     info("Download failed!\n");
298 }
299
300 static int
301 quit(Eina_Bool success, const char *msg)
302 {
303     ewk_shutdown();
304     elm_shutdown();
305
306     if (msg)
307         fputs(msg, (success) ? stdout : stderr);
308
309     if (!success)
310         return EXIT_FAILURE;
311
312     return EXIT_SUCCESS;
313 }
314
315 static void
316 on_url_bar_activated(void *user_data, Evas_Object *url_bar, void *event_info)
317 {
318     Browser_Window *app_data = (Browser_Window *)user_data;
319
320     const char *user_url = elm_entry_entry_get(url_bar);
321     char *url = url_from_user_input(user_url);
322     ewk_view_url_set(app_data->webview, url);
323     free(url);
324
325     /* Give focus back to the view */
326     view_focus_set(app_data, EINA_TRUE);
327 }
328
329 static void
330 on_url_bar_clicked(void *user_data, Evas_Object *url_bar, void *event_info)
331 {
332     Browser_Window *app_data = (Browser_Window *)user_data;
333
334     /* Grab focus from the view */
335     evas_object_focus_set(app_data->webview, EINA_FALSE);
336     elm_object_focus_set(url_bar, EINA_TRUE);
337 }
338
339 static void
340 on_back_button_clicked(void *user_data, Evas_Object *back_button, void *event_info)
341 {
342     Browser_Window *app_data = (Browser_Window *)user_data;
343
344     ewk_view_back(app_data->webview);
345     /* Update back button state */
346     elm_object_disabled_set(back_button, !ewk_view_back_possible(app_data->webview));
347 }
348
349 static void
350 on_forward_button_clicked(void *user_data, Evas_Object *forward_button, void *event_info)
351 {
352     Browser_Window *app_data = (Browser_Window *)user_data;
353
354     ewk_view_forward(app_data->webview);
355     /* Update forward button state */
356     elm_object_disabled_set(forward_button, !ewk_view_forward_possible(app_data->webview));
357 }
358
359 static void
360 on_refresh_button_clicked(void *user_data, Evas_Object *refresh_button, void *event_info)
361 {
362     Browser_Window *app_data = (Browser_Window *)user_data;
363
364     Evas *evas = evas_object_evas_get(refresh_button);
365     Eina_Bool ctrlPressed = evas_key_modifier_is_set(evas_key_modifier_get(evas), "Control");
366     if (ctrlPressed) {
367         info("Reloading and bypassing cache...\n");
368         ewk_view_reload_bypass_cache(app_data->webview);
369     } else {
370         info("Reloading...\n");
371         ewk_view_reload(app_data->webview);
372     }
373 }
374
375 static void
376 on_home_button_clicked(void *user_data, Evas_Object *home_button, void *event_info)
377 {
378     Browser_Window *app_data = (Browser_Window *)user_data;
379
380     ewk_view_url_set(app_data->webview, DEFAULT_URL);
381 }
382
383 static void
384 on_window_deletion(void *user_data, Evas_Object *window, void *event_info)
385 {
386     window_close(browser_window_find(window));
387 }
388
389 static Evas_Object *
390 create_toolbar_button(Evas_Object *window, const char *icon_name)
391 {
392     Evas_Object *button = elm_button_add(window);
393
394     Evas_Object *icon = elm_icon_add(window);
395     elm_icon_standard_set(icon, icon_name);
396     evas_object_size_hint_max_set(icon, TOOL_BAR_ICON_SIZE, TOOL_BAR_ICON_SIZE);
397     evas_object_color_set(icon, 44, 44, 102, 128);
398     evas_object_show(icon);
399     elm_object_part_content_set(button, "icon", icon);
400     evas_object_size_hint_min_set(button, TOOL_BAR_BUTTON_SIZE, TOOL_BAR_BUTTON_SIZE);
401
402     return button;
403 }
404
405 static Browser_Window *window_create(const char *url)
406 {
407     Browser_Window *app_data = malloc(sizeof(Browser_Window));
408     if (!app_data) {
409         info("ERROR: could not create browser window.\n");
410         return NULL;
411     }
412
413     /* Create window */
414     app_data->window = elm_win_add(NULL, "minibrowser-window", ELM_WIN_BASIC);
415     elm_win_title_set(app_data->window, APP_NAME);
416     evas_object_smart_callback_add(app_data->window, "delete,request", on_window_deletion, &app_data);
417
418     /* Create window background */
419     Evas_Object *bg = elm_bg_add(app_data->window);
420     elm_bg_color_set(bg, 193, 192, 191);
421     evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
422     elm_win_resize_object_add(app_data->window, bg);
423     evas_object_show(bg);
424
425     /* Create vertical layout */
426     Evas_Object *vertical_layout = elm_box_add(app_data->window);
427     elm_box_padding_set(vertical_layout, 0, 2);
428     evas_object_size_hint_weight_set(vertical_layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
429     elm_win_resize_object_add(app_data->window, vertical_layout);
430     evas_object_show(vertical_layout);
431
432     /* Create horizontal layout for top bar */
433     Evas_Object *horizontal_layout = elm_box_add(app_data->window);
434     elm_box_horizontal_set(horizontal_layout, EINA_TRUE);
435     evas_object_size_hint_weight_set(horizontal_layout, EVAS_HINT_EXPAND, 0.0);
436     evas_object_size_hint_align_set(horizontal_layout, EVAS_HINT_FILL, 0.0);
437     elm_box_pack_end(vertical_layout, horizontal_layout);
438     evas_object_show(horizontal_layout);
439
440     /* Create Back button */
441     app_data->back_button = create_toolbar_button(app_data->window, "arrow_left");
442     evas_object_smart_callback_add(app_data->back_button, "clicked", on_back_button_clicked, app_data);
443     elm_object_disabled_set(app_data->back_button, EINA_TRUE);
444     evas_object_size_hint_weight_set(app_data->back_button, 0.0, EVAS_HINT_EXPAND);
445     evas_object_size_hint_align_set(app_data->back_button, 0.0, 0.5);
446     elm_box_pack_end(horizontal_layout, app_data->back_button);
447     evas_object_show(app_data->back_button);
448
449     /* Create Forward button */
450     app_data->forward_button = create_toolbar_button(app_data->window, "arrow_right");
451     evas_object_smart_callback_add(app_data->forward_button, "clicked", on_forward_button_clicked, app_data);
452     elm_object_disabled_set(app_data->forward_button, EINA_TRUE);
453     evas_object_size_hint_weight_set(app_data->forward_button, 0.0, EVAS_HINT_EXPAND);
454     evas_object_size_hint_align_set(app_data->forward_button, 0.0, 0.5);
455     elm_box_pack_end(horizontal_layout, app_data->forward_button);
456     evas_object_show(app_data->forward_button);
457
458     /* Create URL bar */
459     app_data->url_bar = elm_entry_add(app_data->window);
460     elm_entry_scrollable_set(app_data->url_bar, EINA_TRUE);
461     elm_entry_single_line_set(app_data->url_bar, EINA_TRUE);
462     elm_entry_cnp_mode_set(app_data->url_bar, ELM_CNP_MODE_PLAINTEXT);
463     elm_entry_text_style_user_push(app_data->url_bar, "DEFAULT='font_size=18'");
464     evas_object_smart_callback_add(app_data->url_bar, "activated", on_url_bar_activated, app_data);
465     evas_object_smart_callback_add(app_data->url_bar, "clicked", on_url_bar_clicked, app_data);
466     evas_object_size_hint_weight_set(app_data->url_bar, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
467     evas_object_size_hint_align_set(app_data->url_bar, EVAS_HINT_FILL, EVAS_HINT_FILL);
468     elm_box_pack_end(horizontal_layout, app_data->url_bar);
469     evas_object_show(app_data->url_bar);
470
471     /* Create Refresh button */
472     Evas_Object *refresh_button = create_toolbar_button(app_data->window, "refresh");
473     evas_object_smart_callback_add(refresh_button, "clicked", on_refresh_button_clicked, app_data);
474     evas_object_size_hint_weight_set(refresh_button, 0.0, EVAS_HINT_EXPAND);
475     evas_object_size_hint_align_set(refresh_button, 1.0, 0.5);
476     elm_box_pack_end(horizontal_layout, refresh_button);
477     evas_object_show(refresh_button);
478
479     /* Create Home button */
480     Evas_Object *home_button = create_toolbar_button(app_data->window, "home");
481     evas_object_smart_callback_add(home_button, "clicked", on_home_button_clicked, app_data);
482     evas_object_size_hint_weight_set(home_button, 0.0, EVAS_HINT_EXPAND);
483     evas_object_size_hint_align_set(home_button, 1.0, 0.5);
484     elm_box_pack_end(horizontal_layout, home_button);
485     evas_object_show(home_button);
486
487     /* Create webview */
488     Evas *evas = evas_object_evas_get(app_data->window);
489     app_data->webview = ewk_view_add(evas);
490     ewk_view_theme_set(app_data->webview, THEME_DIR "/default.edj");
491
492 //    Ewk_Settings *settings = ewk_view_settings_get(app_data->webview);
493 //    ewk_settings_file_access_from_file_urls_allowed_set(settings, EINA_TRUE);
494 //    ewk_settings_enable_frame_flattening_set(settings, frame_flattening_enabled);
495
496     evas_object_smart_callback_add(app_data->webview, "close,window", on_close_window, app_data);
497     evas_object_smart_callback_add(app_data->webview, "create,window", on_new_window, app_data);
498     evas_object_smart_callback_add(app_data->webview, "download,failed", on_download_failed, app_data);
499     evas_object_smart_callback_add(app_data->webview, "download,finished", on_download_finished, app_data);
500     evas_object_smart_callback_add(app_data->webview, "download,request", on_download_request, app_data);
501     evas_object_smart_callback_add(app_data->webview, "load,error", on_error, app_data);
502     evas_object_smart_callback_add(app_data->webview, "load,progress", on_progress, app_data);
503     evas_object_smart_callback_add(app_data->webview, "title,changed", on_title_changed, app_data);
504     evas_object_smart_callback_add(app_data->webview, "url,changed", on_url_changed, app_data);
505     evas_object_smart_callback_add(app_data->webview, "back,forward,list,changed", on_back_forward_list_changed, app_data);
506
507     evas_object_event_callback_add(app_data->webview, EVAS_CALLBACK_KEY_DOWN, on_key_down, app_data);
508     evas_object_event_callback_add(app_data->webview, EVAS_CALLBACK_MOUSE_DOWN, on_mouse_down, app_data);
509
510     evas_object_size_hint_weight_set(app_data->webview, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
511     evas_object_size_hint_align_set(app_data->webview, EVAS_HINT_FILL, EVAS_HINT_FILL);
512     elm_box_pack_end(vertical_layout, app_data->webview);
513     evas_object_show(app_data->webview);
514
515     if (url)
516         ewk_view_url_set(app_data->webview, url);
517
518     evas_object_resize(app_data->window, DEFAULT_WIDTH, DEFAULT_HEIGHT);
519     elm_win_center(app_data->window, EINA_TRUE, EINA_TRUE);
520     evas_object_show(app_data->window);
521
522     view_focus_set(app_data, EINA_TRUE);
523
524     return app_data;
525 }
526
527 EAPI_MAIN int
528 elm_main(int argc, char *argv[])
529 {
530     int args = 1;
531     unsigned char quitOption = 0;
532     Browser_Window *window;
533
534     Ecore_Getopt_Value values[] = {
535         ECORE_GETOPT_VALUE_STR(evas_engine_name),
536         ECORE_GETOPT_VALUE_BOOL(quitOption),
537         ECORE_GETOPT_VALUE_BOOL(frame_flattening_enabled),
538         ECORE_GETOPT_VALUE_BOOL(quitOption),
539         ECORE_GETOPT_VALUE_BOOL(quitOption),
540         ECORE_GETOPT_VALUE_BOOL(quitOption),
541         ECORE_GETOPT_VALUE_NONE
542     };
543
544     if (!ewk_init())
545         return EXIT_FAILURE;
546
547     ecore_app_args_set(argc, (const char **) argv);
548     args = ecore_getopt_parse(&options, values, argc, argv);
549
550     if (args < 0)
551         return quit(EINA_FALSE, "ERROR: could not parse options.\n");
552
553     if (quitOption)
554         return quit(EINA_TRUE, NULL);
555
556     if (args < argc) {
557         char *url = url_from_user_input(argv[args]);
558         window = window_create(url);
559         free(url);
560     } else
561         window = window_create(DEFAULT_URL);
562
563     if (!window)
564         return quit(EINA_FALSE, "ERROR: could not create browser window.\n");
565
566     windows = eina_list_append(windows, window);
567
568     elm_run();
569
570     return quit(EINA_TRUE, NULL);
571 }
572 ELM_MAIN()