2 * Copyright (C) 2012 Samsung Electronics
3 * Copyright (C) 2012 Intel Corporation. All rights reserved.
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.
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.
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
21 #include "url_utils.h"
23 #include <Ecore_Evas.h>
25 #include <Elementary.h>
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;
35 #define info(format, args...) \
38 printf(format, ##args); \
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;
46 typedef struct _Browser_Window {
50 Evas_Object *back_button;
51 Evas_Object *forward_button;
54 static const Ecore_Getopt options = {
56 "%prog [options] [url]",
58 "(C)2012 Samsung Electronics\n",
60 "Test Web Browser using the Enlightenment Foundation Libraries of WebKit2",
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),
71 ECORE_GETOPT_COPYRIGHT
79 static Browser_Window *window_create(const char *url);
81 static Browser_Window *browser_window_find(Evas_Object *window)
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;
97 static void window_free(Browser_Window *window)
99 evas_object_del(window->webview);
100 /* The elm_win will take care of freeing its children */
101 evas_object_del(window->window);
105 static void window_close(Browser_Window *window)
107 windows = eina_list_remove(windows, window);
115 on_key_down(void *data, Evas *e, Evas_Object *obj, void *event_info)
117 Evas_Event_Key_Down *ev = (Evas_Event_Key_Down*) event_info;
118 static const char *encodings[] = {
123 static int currentEncoding = -1;
124 Eina_Bool ctrlPressed = evas_key_modifier_is_set(evas_key_modifier_get(e), "Control");
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");
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);
152 view_focus_set(Browser_Window *window, Eina_Bool focus)
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);
162 on_mouse_down(void *user_data, Evas *e, Evas_Object *webview, void *event_info)
164 Browser_Window *window = (Browser_Window *)user_data;
165 Evas_Event_Mouse_Down *ev = (Evas_Event_Mouse_Down *)event_info;
167 /* Clear selection from the URL bar */
168 elm_entry_select_none(window->url_bar);
171 view_focus_set(window, EINA_TRUE);
172 else if (ev->button == 2)
173 view_focus_set(window, !evas_object_focus_get(webview));
177 title_set(Evas_Object *window, const char *title, int progress)
181 if (!title || !*title) {
182 elm_win_title_set(window, APP_NAME);
186 buffer = eina_strbuf_new();
188 eina_strbuf_append_printf(buffer, "%s (%d%%) - %s", title, progress, APP_NAME);
190 eina_strbuf_append_printf(buffer, "%s - %s", title, APP_NAME);
192 elm_win_title_set(window, eina_strbuf_string_get(buffer));
193 eina_strbuf_free(buffer);
197 on_title_changed(void *user_data, Evas_Object *webview, void *event_info)
199 Browser_Window *window = (Browser_Window *)user_data;
200 const char *title = (const char *)event_info;
202 title_set(window->window, title, 100);
206 on_url_changed(void *user_data, Evas_Object *webview, void *event_info)
208 Browser_Window *window = (Browser_Window *)user_data;
209 elm_entry_entry_set(window->url_bar, ewk_view_url_get(window->webview));
213 on_back_forward_list_changed(void *user_data, Evas_Object *webview, void *event_info)
215 Browser_Window *window = (Browser_Window *)user_data;
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));
223 on_new_window(void *user_data, Evas_Object *webview, void *event_info)
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);
232 on_close_window(void *user_data, Evas_Object *webview, void *event_info)
234 window_close((Browser_Window *)user_data);
238 on_progress(void *user_data, Evas_Object *webview, void *event_info)
240 Browser_Window *window = (Browser_Window *)user_data;
241 double progress = *(double *)event_info;
243 title_set(window->window, ewk_view_title_get(window->webview), progress * 100);
247 on_error(void *user_data, Evas_Object *webview, void *event_info)
250 const Ewk_Error *error = (const Ewk_Error *)event_info;
252 /* This is a cancellation, do not display the error page */
253 if (ewk_error_cancellation_get(error))
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));
260 ewk_view_html_string_load(webview, eina_strbuf_string_get(buffer), 0, ewk_error_url_get(error));
261 eina_strbuf_free(buffer);
265 on_download_request(void *user_data, Evas_Object *webview, void *event_info)
267 Ewk_Download_Job *download = (Ewk_Download_Job *)event_info;
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();
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);
276 // Generate a unique file name since no name was suggested.
277 char unique_path[] = "/tmp/downloaded-file.XXXXXX";
279 eina_strbuf_append(destination_path, unique_path);
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);
288 on_download_finished(void *user_data, Evas_Object *webview, void *event_info)
290 Ewk_Download_Job *download = (Ewk_Download_Job *)event_info;
291 info("Download finished: %s\n", ewk_download_job_destination_get(download));
295 on_download_failed(void *user_data, Evas_Object *webview, void *event_info)
297 info("Download failed!\n");
301 quit(Eina_Bool success, const char *msg)
307 fputs(msg, (success) ? stdout : stderr);
316 on_url_bar_activated(void *user_data, Evas_Object *url_bar, void *event_info)
318 Browser_Window *app_data = (Browser_Window *)user_data;
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);
325 /* Give focus back to the view */
326 view_focus_set(app_data, EINA_TRUE);
330 on_url_bar_clicked(void *user_data, Evas_Object *url_bar, void *event_info)
332 Browser_Window *app_data = (Browser_Window *)user_data;
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);
340 on_back_button_clicked(void *user_data, Evas_Object *back_button, void *event_info)
342 Browser_Window *app_data = (Browser_Window *)user_data;
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));
350 on_forward_button_clicked(void *user_data, Evas_Object *forward_button, void *event_info)
352 Browser_Window *app_data = (Browser_Window *)user_data;
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));
360 on_refresh_button_clicked(void *user_data, Evas_Object *refresh_button, void *event_info)
362 Browser_Window *app_data = (Browser_Window *)user_data;
364 Evas *evas = evas_object_evas_get(refresh_button);
365 Eina_Bool ctrlPressed = evas_key_modifier_is_set(evas_key_modifier_get(evas), "Control");
367 info("Reloading and bypassing cache...\n");
368 ewk_view_reload_bypass_cache(app_data->webview);
370 info("Reloading...\n");
371 ewk_view_reload(app_data->webview);
376 on_home_button_clicked(void *user_data, Evas_Object *home_button, void *event_info)
378 Browser_Window *app_data = (Browser_Window *)user_data;
380 ewk_view_url_set(app_data->webview, DEFAULT_URL);
384 on_window_deletion(void *user_data, Evas_Object *window, void *event_info)
386 window_close(browser_window_find(window));
390 create_toolbar_button(Evas_Object *window, const char *icon_name)
392 Evas_Object *button = elm_button_add(window);
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);
405 static Browser_Window *window_create(const char *url)
407 Browser_Window *app_data = malloc(sizeof(Browser_Window));
409 info("ERROR: could not create browser window.\n");
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);
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);
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);
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);
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);
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);
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);
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);
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);
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");
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);
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);
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);
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);
516 ewk_view_url_set(app_data->webview, url);
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);
522 view_focus_set(app_data, EINA_TRUE);
528 elm_main(int argc, char *argv[])
531 unsigned char quitOption = 0;
532 Browser_Window *window;
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
547 ecore_app_args_set(argc, (const char **) argv);
548 args = ecore_getopt_parse(&options, values, argc, argv);
551 return quit(EINA_FALSE, "ERROR: could not parse options.\n");
554 return quit(EINA_TRUE, NULL);
557 char *url = url_from_user_input(argv[args]);
558 window = window_create(url);
561 window = window_create(DEFAULT_URL);
564 return quit(EINA_FALSE, "ERROR: could not create browser window.\n");
566 windows = eina_list_append(windows, window);
570 return quit(EINA_TRUE, NULL);