diff -r 000000000000 -r 4f2f89ce4247 WebKit/efl/ewk/ewk_view.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebKit/efl/ewk/ewk_view.cpp Fri Sep 17 09:02:29 2010 +0300 @@ -0,0 +1,3905 @@ +/* + Copyright (C) 2009-2010 ProFUSION embedded systems + Copyright (C) 2009-2010 Samsung Electronics + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#define __STDC_FORMAT_MACROS +#include "config.h" +#include "ewk_view.h" + +#include "ChromeClientEfl.h" +#include "ContextMenuClientEfl.h" +#include "ContextMenuController.h" +#include "DocumentLoader.h" +#include "DragClientEfl.h" +#include "EWebKit.h" +#include "EditorClientEfl.h" +#include "EventHandler.h" +#include "FocusController.h" +#include "FrameLoaderClientEfl.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "InspectorClientEfl.h" +#include "PlatformMouseEvent.h" +#include "PopupMenuClient.h" +#include "ProgressTracker.h" +#include "ewk_private.h" + +#include +#include +#include +#include +#include +#include + +#define ZOOM_MIN (0.05) +#define ZOOM_MAX (4.0) + +static const char EWK_VIEW_TYPE_STR[] = "EWK_View"; + +static const size_t EWK_VIEW_REPAINTS_SIZE_INITIAL = 32; +static const size_t EWK_VIEW_REPAINTS_SIZE_STEP = 8; +static const size_t EWK_VIEW_REPAINTS_SIZE_MAX_FREE = 64; + +static const size_t EWK_VIEW_SCROLLS_SIZE_INITIAL = 8; +static const size_t EWK_VIEW_SCROLLS_SIZE_STEP = 2; +static const size_t EWK_VIEW_SCROLLS_SIZE_MAX_FREE = 32; + +struct _Ewk_View_Private_Data { + WebCore::Page* page; + WebCore::Settings* page_settings; + WebCore::Frame* main_frame; + Ewk_History* history; + struct { + Ewk_Menu menu; + WebCore::PopupMenuClient* menu_client; + } popup; + struct { + Eina_Rectangle* array; + size_t count; + size_t allocated; + } repaints; + struct { + Ewk_Scroll_Request* array; + size_t count; + size_t allocated; + } scrolls; + struct { + const char* user_agent; + const char* user_stylesheet; + const char* encoding_default; + const char* encoding_custom; + int font_minimum_size; + int font_minimum_logical_size; + int font_default_size; + int font_monospace_size; + const char* font_standard; + const char* font_cursive; + const char* font_monospace; + const char* font_fantasy; + const char* font_serif; + const char* font_sans_serif; + Eina_Bool auto_load_images:1; + Eina_Bool auto_shrink_images:1; + Eina_Bool enable_scripts:1; + Eina_Bool enable_plugins:1; + Eina_Bool scripts_window_open:1; + Eina_Bool resizable_textareas:1; + Eina_Bool private_browsing:1; + Eina_Bool caret_browsing:1; + struct { + float w; + float h; + float init_scale; + float min_scale; + float max_scale; + float user_scalable; + } viewport; + struct { + float min_scale; + float max_scale; + Eina_Bool user_scalable:1; + } zoom_range; + } settings; + struct { + struct { + double start; + double end; + double duration; + } time; + struct { + float start; + float end; + float range; + } zoom; + struct { + Evas_Coord x, y; + } center; + Ecore_Animator* animator; + } animated_zoom; + struct { + Evas_Coord w, h; + Eina_Bool use:1; + } fixed_layout; +}; + +#ifndef EWK_TYPE_CHECK +#define EWK_VIEW_TYPE_CHECK(o, ...) do { } while (0) +#else +#define EWK_VIEW_TYPE_CHECK(o, ...) \ + do { \ + const char* _tmp_otype = evas_object_type_get(o); \ + const Evas_Smart* _tmp_s = evas_object_smart_smart_get(o); \ + if (EINA_UNLIKELY(!_tmp_s)) { \ + EINA_LOG_CRIT \ + ("%p (%s) is not a smart object!", o, \ + _tmp_otype ? _tmp_otype : "(null)"); \ + return __VA_ARGS__; \ + } \ + const Evas_Smart_Class* _tmp_sc = evas_smart_class_get(_tmp_s); \ + if (EINA_UNLIKELY(!_tmp_sc)) { \ + EINA_LOG_CRIT \ + ("%p (%s) is not a smart object!", o, \ + _tmp_otype ? _tmp_otype : "(null)"); \ + return __VA_ARGS__; \ + } \ + if (EINA_UNLIKELY(_tmp_sc->data != EWK_VIEW_TYPE_STR)) { \ + EINA_LOG_CRIT \ + ("%p (%s) is not of an ewk_view (need %p, got %p)!", \ + o, _tmp_otype ? _tmp_otype : "(null)", \ + EWK_VIEW_TYPE_STR, _tmp_sc->data); \ + return __VA_ARGS__; \ + } \ + } while (0) +#endif + +#define EWK_VIEW_SD_GET(o, ptr) \ + Ewk_View_Smart_Data* ptr = (Ewk_View_Smart_Data*)evas_object_smart_data_get(o) + +#define EWK_VIEW_SD_GET_OR_RETURN(o, ptr, ...) \ + EWK_VIEW_TYPE_CHECK(o, __VA_ARGS__); \ + EWK_VIEW_SD_GET(o, ptr); \ + if (!ptr) { \ + CRITICAL("no smart data for object %p (%s)", \ + o, evas_object_type_get(o)); \ + return __VA_ARGS__; \ + } + +#define EWK_VIEW_PRIV_GET(sd, ptr) \ + Ewk_View_Private_Data* ptr = sd->_priv + +#define EWK_VIEW_PRIV_GET_OR_RETURN(sd, ptr, ...) \ + EWK_VIEW_PRIV_GET(sd, ptr); \ + if (!ptr) { \ + CRITICAL("no private data for object %p (%s)", \ + sd->self, evas_object_type_get(sd->self)); \ + return __VA_ARGS__; \ + } + +static void _ewk_view_smart_changed(Ewk_View_Smart_Data* sd) +{ + if (sd->changed.any) + return; + sd->changed.any = EINA_TRUE; + evas_object_smart_changed(sd->self); +} + +static Eina_Bool _ewk_view_repaints_resize(Ewk_View_Private_Data* priv, size_t size) +{ + void* tmp = realloc(priv->repaints.array, size * sizeof(Eina_Rectangle)); + if (!tmp) { + CRITICAL("could not realloc repaints array to %zu elements.", size); + return EINA_FALSE; + } + priv->repaints.allocated = size; + priv->repaints.array = (Eina_Rectangle*)tmp; + return EINA_TRUE; +} + +static void _ewk_view_repaint_add(Ewk_View_Private_Data* priv, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) +{ + Eina_Rectangle* r; + + // fprintf(stderr, ">>> repaint requested: %d,%d+%dx%d\n", x, y, w, h); + if (priv->repaints.allocated == priv->repaints.count) { + size_t size; + if (!priv->repaints.allocated) + size = EWK_VIEW_REPAINTS_SIZE_INITIAL; + else + size = priv->repaints.allocated + EWK_VIEW_REPAINTS_SIZE_STEP; + if (!_ewk_view_repaints_resize(priv, size)) + return; + } + + r = priv->repaints.array + priv->repaints.count; + priv->repaints.count++; + + r->x = x; + r->y = y; + r->w = w; + r->h = h; + + DBG("add repaint %d,%d+%dx%d", x, y, w, h); +} + +static void _ewk_view_repaints_flush(Ewk_View_Private_Data* priv) +{ + priv->repaints.count = 0; + if (priv->repaints.allocated <= EWK_VIEW_REPAINTS_SIZE_MAX_FREE) + return; + _ewk_view_repaints_resize(priv, EWK_VIEW_REPAINTS_SIZE_MAX_FREE); +} + +static Eina_Bool _ewk_view_scrolls_resize(Ewk_View_Private_Data* priv, size_t size) +{ + void* tmp = realloc(priv->scrolls.array, size * sizeof(Ewk_Scroll_Request)); + if (!tmp) { + CRITICAL("could not realloc scrolls array to %zu elements.", size); + return EINA_FALSE; + } + priv->scrolls.allocated = size; + priv->scrolls.array = (Ewk_Scroll_Request*)tmp; + return EINA_TRUE; +} + +static void _ewk_view_scroll_add(Ewk_View_Private_Data* priv, Evas_Coord dx, Evas_Coord dy, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, Eina_Bool main_scroll) +{ + Ewk_Scroll_Request* r; + Ewk_Scroll_Request* r_end; + Evas_Coord x2 = x + w, y2 = y + h; + + r = priv->scrolls.array; + r_end = r + priv->scrolls.count; + for (; r < r_end; r++) { + if (r->x == x && r->y == y && r->w == w && r->h == h) { + DBG("region already scrolled %d,%d+%dx%d %+03d,%+03d add " + "%+03d,%+03d", + r->x, r->y, r->w, r->h, r->dx, r->dy, dx, dy); + r->dx += dx; + r->dy += dy; + return; + } + if ((x <= r->x && x2 >= r->x2) && (y <= r->y && y2 >= r->y2)) { + DBG("old viewport (%d,%d+%dx%d %+03d,%+03d) was scrolled itself, " + "add %+03d,%+03d", + r->x, r->y, r->w, r->h, r->dx, r->dy, dx, dy); + r->x += dx; + r->y += dy; + } + } + + if (priv->scrolls.allocated == priv->scrolls.count) { + size_t size; + if (!priv->scrolls.allocated) + size = EWK_VIEW_SCROLLS_SIZE_INITIAL; + else + size = priv->scrolls.allocated + EWK_VIEW_SCROLLS_SIZE_STEP; + if (!_ewk_view_scrolls_resize(priv, size)) + return; + } + + r = priv->scrolls.array + priv->scrolls.count; + priv->scrolls.count++; + + r->x = x; + r->y = y; + r->w = w; + r->h = h; + r->x2 = x2; + r->y2 = y2; + r->dx = dx; + r->dy = dy; + r->main_scroll = main_scroll; + DBG("add scroll in region: %d,%d+%dx%d %+03d,%+03d", x, y, w, h, dx, dy); + + Eina_Rectangle* pr; + Eina_Rectangle* pr_end; + size_t count; + pr = priv->repaints.array; + count = priv->repaints.count; + pr_end = pr + count; + for (; pr < pr_end; pr++) { + pr->x += dx; + pr->y += dy; + } +} + +static void _ewk_view_scrolls_flush(Ewk_View_Private_Data* priv) +{ + priv->scrolls.count = 0; + if (priv->scrolls.allocated <= EWK_VIEW_SCROLLS_SIZE_MAX_FREE) + return; + _ewk_view_scrolls_resize(priv, EWK_VIEW_SCROLLS_SIZE_MAX_FREE); +} + +// Default Event Handling ////////////////////////////////////////////// +static Eina_Bool _ewk_view_smart_focus_in(Ewk_View_Smart_Data* sd) +{ + EWK_VIEW_PRIV_GET(sd, priv); + WebCore::FocusController* fc = priv->page->focusController(); + DBG("o=%p, fc=%p", sd->self, fc); + EINA_SAFETY_ON_NULL_RETURN_VAL(fc, EINA_FALSE); + + fc->setActive(true); + fc->setFocused(true); + return EINA_TRUE; +} + +static Eina_Bool _ewk_view_smart_focus_out(Ewk_View_Smart_Data* sd) +{ + EWK_VIEW_PRIV_GET(sd, priv); + WebCore::FocusController* fc = priv->page->focusController(); + DBG("o=%p, fc=%p", sd->self, fc); + EINA_SAFETY_ON_NULL_RETURN_VAL(fc, EINA_FALSE); + + fc->setActive(false); + fc->setFocused(false); + return EINA_TRUE; +} + +static Eina_Bool _ewk_view_smart_mouse_wheel(Ewk_View_Smart_Data* sd, const Evas_Event_Mouse_Wheel* ev) +{ + return ewk_frame_feed_mouse_wheel(sd->main_frame, ev); +} + +static Eina_Bool _ewk_view_smart_mouse_down(Ewk_View_Smart_Data* sd, const Evas_Event_Mouse_Down* ev) +{ + return ewk_frame_feed_mouse_down(sd->main_frame, ev); +} + +static Eina_Bool _ewk_view_smart_mouse_up(Ewk_View_Smart_Data* sd, const Evas_Event_Mouse_Up* ev) +{ + return ewk_frame_feed_mouse_up(sd->main_frame, ev); +} + +static Eina_Bool _ewk_view_smart_mouse_move(Ewk_View_Smart_Data* sd, const Evas_Event_Mouse_Move* ev) +{ + return ewk_frame_feed_mouse_move(sd->main_frame, ev); +} + +static Eina_Bool _ewk_view_smart_key_down(Ewk_View_Smart_Data* sd, const Evas_Event_Key_Down* ev) +{ + Evas_Object* frame = ewk_view_frame_focused_get(sd->self); + + if (!frame) + frame = sd->main_frame; + + return ewk_frame_feed_key_down(frame, ev); +} + +static Eina_Bool _ewk_view_smart_key_up(Ewk_View_Smart_Data* sd, const Evas_Event_Key_Up* ev) +{ + Evas_Object* frame = ewk_view_frame_focused_get(sd->self); + + if (!frame) + frame = sd->main_frame; + + return ewk_frame_feed_key_up(frame, ev); +} + +static void _ewk_view_smart_add_console_message(Ewk_View_Smart_Data* sd, const char* message, unsigned int lineNumber, const char* sourceID) +{ + INF("console message: %s @%d: %s\n", sourceID, lineNumber, message); +} + +static void _ewk_view_smart_run_javascript_alert(Ewk_View_Smart_Data* sd, Evas_Object* frame, const char* message) +{ + INF("javascript alert: %s\n", message); +} + +static Eina_Bool _ewk_view_smart_run_javascript_confirm(Ewk_View_Smart_Data* sd, Evas_Object* frame, const char* message) +{ + INF("javascript confirm: %s", message); + INF("javascript confirm (HARD CODED)? YES"); + return EINA_TRUE; +} + +static Eina_Bool _ewk_view_smart_should_interrupt_javascript(Ewk_View_Smart_Data* sd) +{ + INF("should interrupt javascript?\n" + "\t(HARD CODED) NO"); + return EINA_FALSE; +} + +static Eina_Bool _ewk_view_smart_run_javascript_prompt(Ewk_View_Smart_Data* sd, Evas_Object* frame, const char* message, const char* defaultValue, char** value) +{ + *value = strdup("test"); + Eina_Bool ret = EINA_TRUE; + INF("javascript prompt:\n" + "\t message: %s\n" + "\tdefault value: %s\n" + "\tgiving answer: %s\n" + "\t button: %s", message, defaultValue, *value, ret?"ok":"cancel"); + + return ret; +} + +// Event Handling ////////////////////////////////////////////////////// +static void _ewk_view_on_focus_in(void* data, Evas* e, Evas_Object* o, void* event_info) +{ + Ewk_View_Smart_Data* sd = (Ewk_View_Smart_Data*)data; + EINA_SAFETY_ON_NULL_RETURN(sd->api); + EINA_SAFETY_ON_NULL_RETURN(sd->api->focus_in); + sd->api->focus_in(sd); +} + +static void _ewk_view_on_focus_out(void* data, Evas* e, Evas_Object* o, void* event_info) +{ + Ewk_View_Smart_Data* sd = (Ewk_View_Smart_Data*)data; + EINA_SAFETY_ON_NULL_RETURN(sd->api); + EINA_SAFETY_ON_NULL_RETURN(sd->api->focus_out); + sd->api->focus_out(sd); +} + +static void _ewk_view_on_mouse_wheel(void* data, Evas* e, Evas_Object* o, void* event_info) +{ + Evas_Event_Mouse_Wheel* ev = (Evas_Event_Mouse_Wheel*)event_info; + Ewk_View_Smart_Data* sd = (Ewk_View_Smart_Data*)data; + EINA_SAFETY_ON_NULL_RETURN(sd->api); + EINA_SAFETY_ON_NULL_RETURN(sd->api->mouse_wheel); + sd->api->mouse_wheel(sd, ev); +} + +static void _ewk_view_on_mouse_down(void* data, Evas* e, Evas_Object* o, void* event_info) +{ + Evas_Event_Mouse_Down* ev = (Evas_Event_Mouse_Down*)event_info; + Ewk_View_Smart_Data* sd = (Ewk_View_Smart_Data*)data; + EINA_SAFETY_ON_NULL_RETURN(sd->api); + EINA_SAFETY_ON_NULL_RETURN(sd->api->mouse_down); + sd->api->mouse_down(sd, ev); +} + +static void _ewk_view_on_mouse_up(void* data, Evas* e, Evas_Object* o, void* event_info) +{ + Evas_Event_Mouse_Up* ev = (Evas_Event_Mouse_Up*)event_info; + Ewk_View_Smart_Data* sd = (Ewk_View_Smart_Data*)data; + EINA_SAFETY_ON_NULL_RETURN(sd->api); + EINA_SAFETY_ON_NULL_RETURN(sd->api->mouse_up); + sd->api->mouse_up(sd, ev); +} + +static void _ewk_view_on_mouse_move(void* data, Evas* e, Evas_Object* o, void* event_info) +{ + Evas_Event_Mouse_Move* ev = (Evas_Event_Mouse_Move*)event_info; + Ewk_View_Smart_Data* sd = (Ewk_View_Smart_Data*)data; + EINA_SAFETY_ON_NULL_RETURN(sd->api); + EINA_SAFETY_ON_NULL_RETURN(sd->api->mouse_move); + sd->api->mouse_move(sd, ev); +} + +static void _ewk_view_on_key_down(void* data, Evas* e, Evas_Object* o, void* event_info) +{ + Evas_Event_Key_Down* ev = (Evas_Event_Key_Down*)event_info; + Ewk_View_Smart_Data* sd = (Ewk_View_Smart_Data*)data; + EINA_SAFETY_ON_NULL_RETURN(sd->api); + EINA_SAFETY_ON_NULL_RETURN(sd->api->key_down); + sd->api->key_down(sd, ev); +} + +static void _ewk_view_on_key_up(void* data, Evas* e, Evas_Object* o, void* event_info) +{ + Evas_Event_Key_Up* ev = (Evas_Event_Key_Up*)event_info; + Ewk_View_Smart_Data* sd = (Ewk_View_Smart_Data*)data; + EINA_SAFETY_ON_NULL_RETURN(sd->api); + EINA_SAFETY_ON_NULL_RETURN(sd->api->key_up); + sd->api->key_up(sd, ev); +} + +static WTF::PassRefPtr _ewk_view_core_frame_new(Ewk_View_Smart_Data* sd, Ewk_View_Private_Data* priv, WebCore::HTMLFrameOwnerElement* owner) +{ + WebCore::FrameLoaderClientEfl* flc = new WebCore::FrameLoaderClientEfl(sd->self); + if (!flc) { + CRITICAL("Could not create frame loader client."); + return 0; + } + flc->setCustomUserAgent(WebCore::String::fromUTF8(priv->settings.user_agent)); + + return WebCore::Frame::create(priv->page, owner, flc); +} + +static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL; + +static Ewk_View_Private_Data* _ewk_view_priv_new(Ewk_View_Smart_Data* sd) +{ + Ewk_View_Private_Data* priv = + (Ewk_View_Private_Data*)calloc(1, sizeof(Ewk_View_Private_Data)); + WebCore::AtomicString s; + WebCore::KURL url; + + if (!priv) { + CRITICAL("could not allocate Ewk_View_Private_Data"); + return 0; + } + priv->page = new WebCore::Page( + static_cast(new WebCore::ChromeClientEfl(sd->self)), + static_cast(new WebCore::ContextMenuClientEfl(sd->self)), + static_cast(new WebCore::EditorClientEfl(sd->self)), + static_cast(new WebCore::DragClientEfl), + static_cast(new WebCore::InspectorClientEfl), + 0, + 0, + 0, + 0); + if (!priv->page) { + CRITICAL("Could not create WebKit Page"); + goto error_page; + } + + priv->page_settings = priv->page->settings(); + if (!priv->page_settings) { + CRITICAL("Could not get page settings."); + goto error_settings; + } + + priv->page_settings->setLoadsImagesAutomatically(true); + priv->page_settings->setDefaultFixedFontSize(12); + priv->page_settings->setDefaultFontSize(16); + priv->page_settings->setSerifFontFamily("serif"); + priv->page_settings->setFixedFontFamily("monotype"); + priv->page_settings->setSansSerifFontFamily("sans"); + priv->page_settings->setStandardFontFamily("sans"); + priv->page_settings->setJavaScriptEnabled(true); + priv->page_settings->setPluginsEnabled(true); + + url = priv->page_settings->userStyleSheetLocation(); + priv->settings.user_stylesheet = eina_stringshare_add(url.prettyURL().utf8().data()); + + priv->settings.encoding_default = eina_stringshare_add + (priv->page_settings->defaultTextEncodingName().utf8().data()); + priv->settings.encoding_custom = 0; + + priv->settings.font_minimum_size = priv->page_settings->minimumFontSize(); + priv->settings.font_minimum_logical_size = priv->page_settings->minimumLogicalFontSize(); + priv->settings.font_default_size = priv->page_settings->defaultFontSize(); + priv->settings.font_monospace_size = priv->page_settings->defaultFixedFontSize(); + + s = priv->page_settings->standardFontFamily(); + priv->settings.font_standard = eina_stringshare_add(s.string().utf8().data()); + s = priv->page_settings->cursiveFontFamily(); + priv->settings.font_cursive = eina_stringshare_add(s.string().utf8().data()); + s = priv->page_settings->fixedFontFamily(); + priv->settings.font_monospace = eina_stringshare_add(s.string().utf8().data()); + s = priv->page_settings->fantasyFontFamily(); + priv->settings.font_fantasy = eina_stringshare_add(s.string().utf8().data()); + s = priv->page_settings->serifFontFamily(); + priv->settings.font_serif = eina_stringshare_add(s.string().utf8().data()); + s = priv->page_settings->sansSerifFontFamily(); + priv->settings.font_sans_serif = eina_stringshare_add(s.string().utf8().data()); + + priv->settings.auto_load_images = priv->page_settings->loadsImagesAutomatically(); + priv->settings.auto_shrink_images = priv->page_settings->shrinksStandaloneImagesToFit(); + priv->settings.enable_scripts = priv->page_settings->isJavaScriptEnabled(); + priv->settings.enable_plugins = priv->page_settings->arePluginsEnabled(); + priv->settings.scripts_window_open = priv->page_settings->allowScriptsToCloseWindows(); + priv->settings.resizable_textareas = priv->page_settings->textAreasAreResizable(); + priv->settings.private_browsing = priv->page_settings->privateBrowsingEnabled(); + priv->settings.caret_browsing = priv->page_settings->caretBrowsingEnabled(); + + // Since there's no scale separated from zooming in webkit-efl, this functionality of + // viewport meta tag is implemented using zoom. When scale zoom is supported by webkit-efl, + // this functionality will be modified by the scale zoom patch. + priv->settings.zoom_range.min_scale = ZOOM_MIN; + priv->settings.zoom_range.max_scale = ZOOM_MAX; + priv->settings.zoom_range.user_scalable = EINA_TRUE; + + priv->main_frame = _ewk_view_core_frame_new(sd, priv, 0).get(); + if (!priv->main_frame) { + CRITICAL("Could not create main frame."); + goto error_main_frame; + } + + priv->history = ewk_history_new(priv->page->backForwardList()); + if (!priv->history) { + CRITICAL("Could not create history instance for view."); + goto error_history; + } + + return priv; + +error_history: + // delete priv->main_frame; /* do not delete priv->main_frame */ +error_main_frame: +error_settings: + delete priv->page; +error_page: + free(priv); + return 0; +} + +static void _ewk_view_priv_del(Ewk_View_Private_Data* priv) +{ + if (!priv) + return; + + /* do not delete priv->main_frame */ + + free(priv->repaints.array); + free(priv->scrolls.array); + + eina_stringshare_del(priv->settings.user_agent); + eina_stringshare_del(priv->settings.user_stylesheet); + eina_stringshare_del(priv->settings.encoding_default); + eina_stringshare_del(priv->settings.encoding_custom); + eina_stringshare_del(priv->settings.font_standard); + eina_stringshare_del(priv->settings.font_cursive); + eina_stringshare_del(priv->settings.font_monospace); + eina_stringshare_del(priv->settings.font_fantasy); + eina_stringshare_del(priv->settings.font_serif); + eina_stringshare_del(priv->settings.font_sans_serif); + + if (priv->animated_zoom.animator) + ecore_animator_del(priv->animated_zoom.animator); + + ewk_history_free(priv->history); + + delete priv->page; + free(priv); +} + +static void _ewk_view_smart_add(Evas_Object* o) +{ + const Evas_Smart* smart = evas_object_smart_smart_get(o); + const Evas_Smart_Class* sc = evas_smart_class_get(smart); + const Ewk_View_Smart_Class* api = (const Ewk_View_Smart_Class*)sc; + EINA_SAFETY_ON_NULL_RETURN(api->backing_store_add); + EWK_VIEW_SD_GET(o, sd); + + if (!sd) { + sd = (Ewk_View_Smart_Data*)calloc(1, sizeof(Ewk_View_Smart_Data)); + if (!sd) + CRITICAL("could not allocate Ewk_View_Smart_Data"); + else + evas_object_smart_data_set(o, sd); + } + + sd->bg_color.r = 255; + sd->bg_color.g = 255; + sd->bg_color.b = 255; + sd->bg_color.a = 255; + + sd->self = o; + sd->_priv = _ewk_view_priv_new(sd); + sd->api = api; + + _parent_sc.add(o); + + if (!sd->_priv) + return; + + EWK_VIEW_PRIV_GET(sd, priv); + + sd->backing_store = api->backing_store_add(sd); + if (!sd->backing_store) { + ERR("Could not create backing store object."); + return; + } + + evas_object_smart_member_add(sd->backing_store, o); + evas_object_show(sd->backing_store); + + sd->main_frame = ewk_frame_add(sd->base.evas); + if (!sd->main_frame) { + ERR("Could not create main frame object."); + return; + } + + if (!ewk_frame_init(sd->main_frame, o, priv->main_frame)) { + ERR("Could not initialize main frme object."); + evas_object_del(sd->main_frame); + sd->main_frame = 0; + + delete priv->main_frame; + priv->main_frame = 0; + return; + } + + evas_object_name_set(sd->main_frame, "EWK_Frame:main"); + evas_object_smart_member_add(sd->main_frame, o); + evas_object_show(sd->main_frame); + +#define CONNECT(s, c) evas_object_event_callback_add(o, s, c, sd) + CONNECT(EVAS_CALLBACK_FOCUS_IN, _ewk_view_on_focus_in); + CONNECT(EVAS_CALLBACK_FOCUS_OUT, _ewk_view_on_focus_out); + CONNECT(EVAS_CALLBACK_MOUSE_WHEEL, _ewk_view_on_mouse_wheel); + CONNECT(EVAS_CALLBACK_MOUSE_DOWN, _ewk_view_on_mouse_down); + CONNECT(EVAS_CALLBACK_MOUSE_UP, _ewk_view_on_mouse_up); + CONNECT(EVAS_CALLBACK_MOUSE_MOVE, _ewk_view_on_mouse_move); + CONNECT(EVAS_CALLBACK_KEY_DOWN, _ewk_view_on_key_down); + CONNECT(EVAS_CALLBACK_KEY_UP, _ewk_view_on_key_up); +#undef CONNECT +} + +static void _ewk_view_smart_del(Evas_Object* o) +{ + EWK_VIEW_SD_GET(o, sd); + Ewk_View_Private_Data* priv = sd ? sd->_priv : 0; + + ewk_view_stop(o); + _parent_sc.del(o); + _ewk_view_priv_del(priv); +} + +static void _ewk_view_smart_resize(Evas_Object* o, Evas_Coord w, Evas_Coord h) +{ + EWK_VIEW_SD_GET(o, sd); + + // these should be queued and processed in calculate as well! + evas_object_resize(sd->backing_store, w, h); + + sd->changed.size = EINA_TRUE; + _ewk_view_smart_changed(sd); +} + +static void _ewk_view_smart_move(Evas_Object* o, Evas_Coord x, Evas_Coord y) +{ + EWK_VIEW_SD_GET(o, sd); + sd->changed.position = EINA_TRUE; + _ewk_view_smart_changed(sd); +} + +static void _ewk_view_smart_calculate(Evas_Object* o) +{ + EWK_VIEW_SD_GET(o, sd); + EWK_VIEW_PRIV_GET(sd, priv); + EINA_SAFETY_ON_NULL_RETURN(sd->api->contents_resize); + EINA_SAFETY_ON_NULL_RETURN(sd->api->scrolls_process); + EINA_SAFETY_ON_NULL_RETURN(sd->api->repaints_process); + Evas_Coord x, y, w, h; + + sd->changed.any = EINA_FALSE; + + if (!sd->main_frame || !priv->main_frame) + return; + + evas_object_geometry_get(o, &x, &y, &w, &h); + + DBG("o=%p geo=[%d, %d + %dx%d], changed: size=%hhu, " + "scrolls=%zu, repaints=%zu", + o, x, y, w, h, sd->changed.size, + priv->scrolls.count, priv->repaints.count); + + if (sd->changed.size && ((w != sd->view.w) || (h != sd->view.h))) { + WebCore::FrameView* view = priv->main_frame->view(); + if (view) { + view->resize(w, h); + view->forceLayout(); + view->adjustViewSize(); + IntSize size = view->contentsSize(); + if (!sd->api->contents_resize(sd, size.width(), size.height())) + ERR("failed to resize contents to %dx%d", + size.width(), size.height()); + } + evas_object_resize(sd->main_frame, w, h); + sd->changed.frame_rect = EINA_TRUE; + sd->view.w = w; + sd->view.h = h; + } + sd->changed.size = EINA_FALSE; + + if (sd->changed.position && ((x != sd->view.x) || (y != sd->view.y))) { + evas_object_move(sd->main_frame, x, y); + evas_object_move(sd->backing_store, x, y); + sd->changed.frame_rect = EINA_TRUE; + sd->view.x = x; + sd->view.y = y; + } + sd->changed.position = EINA_FALSE; + + ewk_view_layout_if_needed_recursive(sd->_priv); + + if (!sd->api->scrolls_process(sd)) + ERR("failed to process scrolls."); + _ewk_view_scrolls_flush(priv); + + if (!sd->api->repaints_process(sd)) + ERR("failed to process repaints."); + _ewk_view_repaints_flush(priv); + + if (sd->changed.frame_rect) { + WebCore::FrameView* view = priv->main_frame->view(); + view->frameRectsChanged(); /* force tree to get position from root */ + sd->changed.frame_rect = EINA_FALSE; + } +} + +static Eina_Bool _ewk_view_smart_contents_resize(Ewk_View_Smart_Data* sd, int w, int h) +{ + return EINA_TRUE; +} + +static Eina_Bool _ewk_view_smart_zoom_set(Ewk_View_Smart_Data* sd, float zoom, Evas_Coord cx, Evas_Coord cy) +{ + double px, py; + Evas_Coord x, y, w, h; + Eina_Bool ret; + + ewk_frame_scroll_size_get(sd->main_frame, &w, &h); + ewk_frame_scroll_pos_get(sd->main_frame, &x, &y); + + if (w + sd->view.w > 0) + px = (double)(x + cx) / (w + sd->view.w); + else + px = 0.0; + + if (h + sd->view.h > 0) + py = (double)(y + cy) / (h + sd->view.h); + else + py = 0.0; + + ret = ewk_frame_zoom_set(sd->main_frame, zoom); + + ewk_frame_scroll_size_get(sd->main_frame, &w, &h); + x = (w + sd->view.w) * px - cx; + y = (h + sd->view.h) * py - cy; + ewk_frame_scroll_set(sd->main_frame, x, y); + return ret; +} + +static void _ewk_view_smart_flush(Ewk_View_Smart_Data* sd) +{ + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv); + _ewk_view_repaints_flush(priv); + _ewk_view_scrolls_flush(priv); +} + +static Eina_Bool _ewk_view_smart_pre_render_region(Ewk_View_Smart_Data* sd, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom) +{ + WRN("not supported by engine. sd=%p area=%d,%d+%dx%d, zoom=%f", + sd, x, y, w, h, zoom); + return EINA_FALSE; +} + +static void _ewk_view_smart_pre_render_cancel(Ewk_View_Smart_Data* sd) +{ + WRN("not supported by engine. sd=%p", sd); +} + +static void _ewk_view_zoom_animated_mark_stop(Ewk_View_Smart_Data* sd) +{ + sd->animated_zoom.zoom.start = 0.0; + sd->animated_zoom.zoom.end = 0.0; + sd->animated_zoom.zoom.current = 0.0; +} + +static void _ewk_view_zoom_animated_finish(Ewk_View_Smart_Data* sd) +{ + EWK_VIEW_PRIV_GET(sd, priv); + ecore_animator_del(priv->animated_zoom.animator); + priv->animated_zoom.animator = 0; + _ewk_view_zoom_animated_mark_stop(sd); + evas_object_smart_callback_call(sd->self, "zoom,animated,end", 0); +} + +static float _ewk_view_zoom_animated_current(Ewk_View_Private_Data* priv) +{ + double now = ecore_loop_time_get(); + double delta = now - priv->animated_zoom.time.start; + + if (delta > priv->animated_zoom.time.duration) + delta = priv->animated_zoom.time.duration; + if (delta < 0.0) // time went back, clock adjusted? + delta = 0.0; + + delta /= priv->animated_zoom.time.duration; + + return ((priv->animated_zoom.zoom.range * delta) + + priv->animated_zoom.zoom.start); +} + +static Eina_Bool _ewk_view_zoom_animator_cb(void* data) +{ + Ewk_View_Smart_Data* sd = (Ewk_View_Smart_Data*)data; + Evas_Coord cx, cy; + EWK_VIEW_PRIV_GET(sd, priv); + double now = ecore_loop_time_get(); + + cx = priv->animated_zoom.center.x; + cy = priv->animated_zoom.center.y; + + // TODO: progressively center (cx, cy) -> (view.x + view.h/2, view.y + view.h/2) + if (cx >= sd->view.w) + cx = sd->view.w - 1; + if (cy >= sd->view.h) + cy = sd->view.h - 1; + + if ((now >= priv->animated_zoom.time.end) + || (now < priv->animated_zoom.time.start)) { + _ewk_view_zoom_animated_finish(sd); + ewk_view_zoom_set(sd->self, priv->animated_zoom.zoom.end, cx, cy); + return EINA_FALSE; + } + + sd->animated_zoom.zoom.current = _ewk_view_zoom_animated_current(priv); + sd->api->zoom_weak_set(sd, sd->animated_zoom.zoom.current, cx, cy); + return EINA_TRUE; +} + +static void _ewk_view_zoom_animation_start(Ewk_View_Smart_Data* sd) +{ + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv); + if (priv->animated_zoom.animator) + return; + priv->animated_zoom.animator = ecore_animator_add + (_ewk_view_zoom_animator_cb, sd); +} + +/** + * Sets the smart class api without any backing store, enabling view + * to be inherited. + * + * @param api class definition to be set, all members with the + * exception of Evas_Smart_Class->data may be overridden. Must + * @b not be @c 0. + * + * @note Evas_Smart_Class->data is used to implement type checking and + * is not supposed to be changed/overridden. If you need extra + * data for your smart class to work, just extend + * Ewk_View_Smart_Class instead. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE on failure (probably + * version mismatch). + * + * @see ewk_view_single_smart_set() + * @see ewk_view_tiled_smart_set() + */ +Eina_Bool ewk_view_base_smart_set(Ewk_View_Smart_Class* api) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(api, EINA_FALSE); + + if (api->version != EWK_VIEW_SMART_CLASS_VERSION) { + EINA_LOG_CRIT + ("Ewk_View_Smart_Class %p is version %lu while %lu was expected.", + api, api->version, EWK_VIEW_SMART_CLASS_VERSION); + return EINA_FALSE; + } + + if (EINA_UNLIKELY(!_parent_sc.add)) + evas_object_smart_clipped_smart_set(&_parent_sc); + + evas_object_smart_clipped_smart_set(&api->sc); + api->sc.add = _ewk_view_smart_add; + api->sc.del = _ewk_view_smart_del; + api->sc.resize = _ewk_view_smart_resize; + api->sc.move = _ewk_view_smart_move; + api->sc.calculate = _ewk_view_smart_calculate; + api->sc.data = EWK_VIEW_TYPE_STR; /* used by type checking */ + + api->contents_resize = _ewk_view_smart_contents_resize; + api->zoom_set = _ewk_view_smart_zoom_set; + api->flush = _ewk_view_smart_flush; + api->pre_render_region = _ewk_view_smart_pre_render_region; + api->pre_render_cancel = _ewk_view_smart_pre_render_cancel; + + api->focus_in = _ewk_view_smart_focus_in; + api->focus_out = _ewk_view_smart_focus_out; + api->mouse_wheel = _ewk_view_smart_mouse_wheel; + api->mouse_down = _ewk_view_smart_mouse_down; + api->mouse_up = _ewk_view_smart_mouse_up; + api->mouse_move = _ewk_view_smart_mouse_move; + api->key_down = _ewk_view_smart_key_down; + api->key_up = _ewk_view_smart_key_up; + + api->add_console_message = _ewk_view_smart_add_console_message; + api->run_javascript_alert = _ewk_view_smart_run_javascript_alert; + api->run_javascript_confirm = _ewk_view_smart_run_javascript_confirm; + api->run_javascript_prompt = _ewk_view_smart_run_javascript_prompt; + api->should_interrupt_javascript = _ewk_view_smart_should_interrupt_javascript; + + return EINA_TRUE; +} + +/** + * Set a fixed layout size to be used, dissociating it from viewport size. + * + * Setting a width different than zero enables fixed layout on that + * size. It's automatically scaled based on zoom, but will not change + * if viewport changes. + * + * Setting both @a w and @a h to zero will disable fixed layout. + * + * @param o view object to change fixed layout. + * @param w fixed width to use. This size will be automatically scaled + * based on zoom level. + * @param h fixed height to use. This size will be automatically scaled + * based on zoom level. + */ +void ewk_view_fixed_layout_size_set(Evas_Object* o, Evas_Coord w, Evas_Coord h) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv); + + WebCore::FrameLoaderClientEfl* client = static_cast(priv->main_frame->loader()->client()); + if (!client->getInitLayoutCompleted()) + return; + + WebCore::FrameView* view = sd->_priv->main_frame->view(); + if (w <= 0 && h <= 0) { + if (!priv->fixed_layout.use) + return; + priv->fixed_layout.w = 0; + priv->fixed_layout.h = 0; + priv->fixed_layout.use = EINA_FALSE; + } else { + if (priv->fixed_layout.use + && priv->fixed_layout.w == w && priv->fixed_layout.h == h) + return; + priv->fixed_layout.w = w; + priv->fixed_layout.h = h; + priv->fixed_layout.use = EINA_TRUE; + + if (view) + view->setFixedLayoutSize(WebCore::IntSize(w, h)); + } + + if (!view) + return; + view->setUseFixedLayout(priv->fixed_layout.use); + view->forceLayout(); +} + +/** + * Get fixed layout size in use. + * + * @param o view object to query fixed layout size. + * @param w where to return width. Returns 0 on error or if no fixed + * layout in use. + * @param h where to return height. Returns 0 on error or if no fixed + * layout in use. + */ +void ewk_view_fixed_layout_size_get(Evas_Object* o, Evas_Coord* w, Evas_Coord* h) +{ + if (w) + *w = 0; + if (h) + *h = 0; + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv); + if (priv->fixed_layout.use) { + if (w) + *w = priv->fixed_layout.w; + if (h) + *h = priv->fixed_layout.h; + } +} + +/** + * Set the theme path to be used by this view. + * + * This also sets the theme on the main frame. As frames inherit theme + * from their parent, this will have all frames with unset theme to + * use this one. + * + * @param o view object to change theme. + * @param path theme path, may be @c 0 to reset to default. + */ +void ewk_view_theme_set(Evas_Object* o, const char* path) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + ewk_frame_theme_set(sd->main_frame, path); +} + +/** + * Gets the theme set on this frame. + * + * This returns the value set by ewk_view_theme_set(). + * + * @param o view object to get theme path. + * + * @return theme path, may be @c 0 if not set. + */ +const char* ewk_view_theme_get(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + return ewk_frame_theme_get(sd->main_frame); +} + +/** + * Get the object that represents the main frame. + * + * @param o view object to get main frame. + * + * @return ewk_frame object or @c 0 if none yet. + */ +Evas_Object* ewk_view_frame_main_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + return sd->main_frame; +} + +/** + * Get the currently focused frame object. + * + * @param o view object to get focused frame. + * + * @return ewk_frame object or @c 0 if none yet. + */ +Evas_Object* ewk_view_frame_focused_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + + WebCore::Frame* core = priv->page->focusController()->focusedFrame(); + if (!core) + return 0; + + WebCore::FrameLoaderClientEfl* client = static_cast(core->loader()->client()); + if (!client) + return 0; + return client->webFrame(); +} + +/** + * Ask main frame to load the given URI. + * + * @param o view object to load uri. + * @param uri uniform resource identifier to load. + * + * @return @c EINA_TRUE on successful request, @c EINA_FALSE on failure. + * Note that it means the request was done, not that it was + * satisfied. + */ +Eina_Bool ewk_view_uri_set(Evas_Object* o, const char* uri) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_uri_set(sd->main_frame, uri); +} + +/** + * Get the current uri loaded by main frame. + * + * @param o view object to get current uri. + * + * @return current uri reference or @c 0. It's internal, don't change. + */ +const char* ewk_view_uri_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + return ewk_frame_uri_get(sd->main_frame); +} + +/** + * Get the current title of main frame. + * + * @param o view object to get current title. + * + * @return current title reference or @c 0. It's internal, don't change. + */ +const char* ewk_view_title_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + return ewk_frame_title_get(sd->main_frame); +} + +/** + * Gets if main frame is editable. + * + * @param o view object to get editable state. + * + * @return @c EINA_TRUE if editable, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_editable_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_editable_get(sd->main_frame); +} + +/** + * Set background color and transparency + * + * Just as in Evas, colors are pre-multiplied, so 50% red is + * (128, 0, 0, 128) and not (255, 0, 0, 128)! + * + * @warning Watch out performance issues with transparency! Object + * will be handled as transparent image by evas even if the + * webpage specifies a background color. That mean you'll pay + * a price even if it's not really transparent, thus + * scrolling/panning and zooming will be likely slower than + * if transparency is off. + * + * @param o view object to change. + * @param r red component. + * @param g green component. + * @param b blue component. + * @param a transparency. + */ +void ewk_view_bg_color_set(Evas_Object* o, int r, int g, int b, int a) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + EINA_SAFETY_ON_NULL_RETURN(sd->api); + EINA_SAFETY_ON_NULL_RETURN(sd->api->bg_color_set); + + if (a < 0) { + WRN("Alpha less than zero (%d).", a); + a = 0; + } else if (a > 255) { + WRN("Alpha is larger than 255 (%d).", a); + a = 255; + } + +#define CHECK_PREMUL_COLOR(c, a) \ + if (c < 0) { \ + WRN("Color component "#c" is less than zero (%d).", c); \ + c = 0; \ + } else if (c > a) { \ + WRN("Color component "#c" is greater than alpha (%d, alpha=%d).", \ + c, a); \ + c = a; \ + } + CHECK_PREMUL_COLOR(r, a); + CHECK_PREMUL_COLOR(g, a); + CHECK_PREMUL_COLOR(b, a); +#undef CHECK_PREMUL_COLOR + + sd->bg_color.r = r; + sd->bg_color.g = g; + sd->bg_color.b = b; + sd->bg_color.a = a; + + sd->api->bg_color_set(sd, r, g, b, a); + + WebCore::FrameView* view = sd->_priv->main_frame->view(); + if (view) { + WebCore::Color color; + + if (!a) + color = WebCore::Color(0, 0, 0, 0); + else if (a == 255) + color = WebCore::Color(r, g, b, a); + else + color = WebCore::Color(r * 255 / a, g * 255 / a, b * 255 / a, a); + + view->updateBackgroundRecursively(color, !a); + } +} + +/** + * Query if view object background color. + * + * Just as in Evas, colors are pre-multiplied, so 50% red is + * (128, 0, 0, 128) and not (255, 0, 0, 128)! + * + * @param o view object to query. + * @param r where to return red color component. + * @param g where to return green color component. + * @param b where to return blue color component. + * @param a where to return alpha value. + */ +void ewk_view_bg_color_get(const Evas_Object* o, int* r, int* g, int* b, int* a) +{ + if (r) + *r = 0; + if (g) + *g = 0; + if (b) + *b = 0; + if (a) + *a = 0; + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + if (r) + *r = sd->bg_color.r; + if (g) + *g = sd->bg_color.g; + if (b) + *b = sd->bg_color.b; + if (a) + *a = sd->bg_color.a; +} + +/** + * Search the given text string in document. + * + * @param o view object where to search text. + * @param string reference string to search. + * @param case_sensitive if search should be case sensitive or not. + * @param forward if search is from cursor and on or backwards. + * @param wrap if search should wrap at end. + * + * @return @c EINA_TRUE if found, @c EINA_FALSE if not or failure. + */ +Eina_Bool ewk_view_text_search(const Evas_Object* o, const char* string, Eina_Bool case_sensitive, Eina_Bool forward, Eina_Bool wrap) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(string, EINA_FALSE); + WebCore::TextCaseSensitivity sensitive; + WebCore::FindDirection direction; + + if (case_sensitive) + sensitive = WebCore::TextCaseSensitive; + else + sensitive = WebCore::TextCaseInsensitive; + + if (forward) + direction = WebCore::FindDirectionForward; + else + direction = WebCore::FindDirectionBackward; + + return priv->page->findString(WebCore::String::fromUTF8(string), sensitive, direction, wrap); +} + +/** + * Mark matches the given text string in document. + * + * @param o view object where to search text. + * @param string reference string to match. + * @param case_sensitive if match should be case sensitive or not. + * @param highlight if matches should be highlighted. + * @param limit maximum amount of matches, or zero to unlimited. + * + * @return number of matches. + */ +unsigned int ewk_view_text_matches_mark(Evas_Object* o, const char* string, Eina_Bool case_sensitive, Eina_Bool highlight, unsigned int limit) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + EINA_SAFETY_ON_NULL_RETURN_VAL(string, 0); + WebCore::TextCaseSensitivity sensitive; + + if (case_sensitive) + sensitive = WebCore::TextCaseSensitive; + else + sensitive = WebCore::TextCaseInsensitive; + + return priv->page->markAllMatchesForText(WebCore::String::fromUTF8(string), sensitive, highlight, limit); +} + +/** + * Reverses the effect of ewk_view_text_matches_mark() + * + * @param o view object where to search text. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE for failure. + */ +Eina_Bool ewk_view_text_matches_unmark_all(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + priv->page->unmarkAllTextMatches(); + return EINA_TRUE; +} + +/** + * Set if should highlight matches marked with ewk_view_text_matches_mark(). + * + * @param o view object where to set if matches are highlighted or not. + * @param highlight if @c EINA_TRUE, matches will be highlighted. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE for failure. + */ +Eina_Bool ewk_view_text_matches_highlight_set(Evas_Object* o, Eina_Bool highlight) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_text_matches_highlight_set(sd->main_frame, highlight); +} + +/** + * Get if should highlight matches marked with ewk_view_text_matches_mark(). + * + * @param o view object to query if matches are highlighted or not. + * + * @return @c EINA_TRUE if they are highlighted, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_text_matches_highlight_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_text_matches_highlight_get(sd->main_frame); +} + +/** + * Sets if main frame is editable. + * + * @param o view object to set editable state. + * @param editable new state. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_editable_set(Evas_Object* o, Eina_Bool editable) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_editable_set(sd->main_frame, editable); +} + +/** + * Get the copy of the selection text. + * + * @param o view object to get selection text. + * + * @return newly allocated string or @c 0 if nothing is selected or failure. + */ +char* ewk_view_selection_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + WTF::CString s = priv->page->focusController()->focusedOrMainFrame()->selectedText().utf8(); + if (s.isNull()) + return 0; + return strdup(s.data()); +} + +static Eina_Bool _ewk_view_editor_command(Ewk_View_Private_Data* priv, const char* command) +{ + return priv->page->focusController()->focusedOrMainFrame()->editor()->command(WebCore::String::fromUTF8(command)).execute(); +} + +/** + * Unselects whatever was selected. + * + * @return @c EINA_TRUE if operation was executed, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_select_none(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return _ewk_view_editor_command(priv, "Unselect"); +} + +/** + * Selects everything. + * + * @return @c EINA_TRUE if operation was executed, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_select_all(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return _ewk_view_editor_command(priv, "SelectAll"); +} + +/** + * Selects the current paragrah. + * + * @return @c EINA_TRUE if operation was executed, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_select_paragraph(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return _ewk_view_editor_command(priv, "SelectParagraph"); +} + +/** + * Selects the current sentence. + * + * @return @c EINA_TRUE if operation was executed, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_select_sentence(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return _ewk_view_editor_command(priv, "SelectSentence"); +} + +/** + * Selects the current line. + * + * @return @c EINA_TRUE if operation was executed, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_select_line(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return _ewk_view_editor_command(priv, "SelectLine"); +} + +/** + * Selects the current word. + * + * @return @c EINA_TRUE if operation was executed, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_select_word(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return _ewk_view_editor_command(priv, "SelectWord"); +} + +/** + * Forwards a request of new Context Menu to WebCore. + * + * @param o View. + * @param ev Event data. + * + * @return @c EINA_TRUE if operation was executed, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_context_menu_forward_event(Evas_Object* o, const Evas_Event_Mouse_Down* ev) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + Eina_Bool mouse_press_handled = EINA_FALSE; + + priv->page->contextMenuController()->clearContextMenu(); + WebCore::Frame* main_frame = priv->page->mainFrame(); + Evas_Coord x, y; + evas_object_geometry_get(sd->self, &x, &y, 0, 0); + + WebCore::PlatformMouseEvent event(ev, WebCore::IntPoint(x, y)); + + if (main_frame->view()) { + mouse_press_handled = + main_frame->eventHandler()->handleMousePressEvent(event); + } + + if (main_frame->eventHandler()->sendContextMenuEvent(event)) + return EINA_FALSE; + + WebCore::ContextMenu* coreMenu = + priv->page->contextMenuController()->contextMenu(); + if (!coreMenu) { + // WebCore decided not to create a context menu, return true if event + // was handled by handleMouseReleaseEvent + return mouse_press_handled; + } + + return EINA_TRUE; +} + +/** + * Get current load progress estimate from 0.0 to 1.0. + * + * @param o view object to get current progress. + * + * @return progres value from 0.0 to 1.0 or -1.0 on error. + */ +double ewk_view_load_progress_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, -1.0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, -1.0); + return priv->page->progress()->estimatedProgress(); +} + +/** + * Ask main frame to stop loading. + * + * @param o view object to stop loading. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see ewk_frame_stop() + */ +Eina_Bool ewk_view_stop(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_stop(sd->main_frame); +} + +/** + * Ask main frame to reload current document. + * + * @param o view object to reload. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see ewk_frame_reload() + */ +Eina_Bool ewk_view_reload(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_reload(sd->main_frame); +} + +/** + * Ask main frame to fully reload current document, using no caches. + * + * @param o view object to reload. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see ewk_frame_reload_full() + */ +Eina_Bool ewk_view_reload_full(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_reload_full(sd->main_frame); +} + +/** + * Ask main frame to navigate back in history. + * + * @param o view object to navigate back. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see ewk_frame_back() + */ +Eina_Bool ewk_view_back(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_back(sd->main_frame); +} + +/** + * Ask main frame to navigate forward in history. + * + * @param o view object to navigate forward. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see ewk_frame_forward() + */ +Eina_Bool ewk_view_forward(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_forward(sd->main_frame); +} + +/** + * Navigate back or forward in history. + * + * @param o view object to navigate. + * @param steps if positive navigates that amount forwards, if negative + * does backwards. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see ewk_frame_navigate() + */ +Eina_Bool ewk_view_navigate(Evas_Object* o, int steps) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_navigate(sd->main_frame, steps); +} + +/** + * Check if it is possible to navigate backwards one item in history. + * + * @param o view object to check if backward navigation is possible. + * + * @return @c EINA_TRUE if possible, @c EINA_FALSE otherwise. + * + * @see ewk_view_navigate_possible() + */ +Eina_Bool ewk_view_back_possible(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_back_possible(sd->main_frame); +} + +/** + * Check if it is possible to navigate forwards one item in history. + * + * @param o view object to check if forward navigation is possible. + * + * @return @c EINA_TRUE if possible, @c EINA_FALSE otherwise. + * + * @see ewk_view_navigate_possible() + */ +Eina_Bool ewk_view_forward_possible(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_forward_possible(sd->main_frame); +} + +/** + * Check if it is possible to navigate given @a steps in history. + * + * @param o view object to navigate. + * @param steps if positive navigates that amount forwards, if negative + * does backwards. + * + * @return @c EINA_TRUE if possible, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_navigate_possible(Evas_Object* o, int steps) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_navigate_possible(sd->main_frame, steps); +} + +/** + * Check if navigation history (back-forward lists) is enabled. + * + * @param o view object to check if navigation history is enabled. + * + * @return @c EINA_TRUE if view keeps history, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_history_enable_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return priv->page->backForwardList()->enabled(); +} + +/** + * Sets if navigation history (back-forward lists) is enabled. + * + * @param o view object to set if navigation history is enabled. + * @param enable @c EINA_TRUE if we want to enable navigation history; + * @c EINA_FALSE otherwise. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_history_enable_set(Evas_Object* o, Eina_Bool enable) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + priv->page->backForwardList()->setEnabled(enable); + return EINA_TRUE; +} + +/** + * Gets the history (back-forward list) associated with this view. + * + * @param o view object to get navigation history from. + * + * @return returns the history instance handle associated with this + * view or @c 0 on errors (including when history is not + * enabled with ewk_view_history_enable_set()). This instance + * is unique for this view and thus multiple calls to this + * function with the same view as parameter returns the same + * handle. This handle is alive while view is alive, thus one + * might want to listen for EVAS_CALLBACK_DEL on given view + * (@a o) to know when to stop using returned handle. + * + * @see ewk_view_history_enable_set() + */ +Ewk_History* ewk_view_history_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + if (!priv->page->backForwardList()->enabled()) { + ERR("asked history, but it's disabled! Returning 0!"); + return 0; + } + return priv->history; +} + +/** + * Get the current zoom level of main frame. + * + * @param o view object to query zoom level. + * + * @return current zoom level in use or -1.0 on error. + */ +float ewk_view_zoom_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, -1.0); + return ewk_frame_zoom_get(sd->main_frame); +} + +/** + * Set the current zoom level of main frame. + * + * @param o view object to set zoom level. + * @param zoom new level to use. + * @param cx x of center coordinate + * @param cy y of center coordinate + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_zoom_set(Evas_Object* o, float zoom, Evas_Coord cx, Evas_Coord cy) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET(sd, priv); + + EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api->zoom_set, EINA_FALSE); + + if (!priv->settings.zoom_range.user_scalable) { + WRN("userScalable is false"); + return EINA_FALSE; + } + + if (zoom < priv->settings.zoom_range.min_scale) { + WRN("zoom level is < %f : %f", (double)priv->settings.zoom_range.min_scale, (double)zoom); + return EINA_FALSE; + } + if (zoom > priv->settings.zoom_range.max_scale) { + WRN("zoom level is > %f : %f", (double)priv->settings.zoom_range.max_scale, (double)zoom); + return EINA_FALSE; + } + + if (cx >= sd->view.w) + cx = sd->view.w - 1; + if (cy >= sd->view.h) + cy = sd->view.h - 1; + if (cx < 0) + cx = 0; + if (cy < 0) + cy = 0; + _ewk_view_zoom_animated_mark_stop(sd); + return sd->api->zoom_set(sd, zoom, cx, cy); +} + +Eina_Bool ewk_view_zoom_weak_smooth_scale_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return sd->zoom_weak_smooth_scale; +} + +void ewk_view_zoom_weak_smooth_scale_set(Evas_Object* o, Eina_Bool smooth_scale) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + smooth_scale = !!smooth_scale; + if (sd->zoom_weak_smooth_scale == smooth_scale) + return; + sd->zoom_weak_smooth_scale = smooth_scale; + EINA_SAFETY_ON_NULL_RETURN(sd->api); + EINA_SAFETY_ON_NULL_RETURN(sd->api->zoom_weak_smooth_scale_set); + sd->api->zoom_weak_smooth_scale_set(sd, smooth_scale); +} + +/** + * Set the current zoom level of backing store, centered at given point. + * + * Unlike ewk_view_zoom_set(), this call do not ask WebKit to render + * at new size, but scale what is already rendered, being much faster + * but worse quality. + * + * Often one should use ewk_view_zoom_animated_set(), it will call the + * same machinery internally. + * + * @note this will set variables used by ewk_view_zoom_animated_set() + * so sub-classes will not reset internal state on their + * "calculate" phase. To unset those and enable sub-classes to + * reset their internal state, call + * ewk_view_zoom_animated_mark_stop(). Namely, this call will + * set ewk_view_zoom_animated_mark_start() to actual webkit zoom + * level, ewk_view_zoom_animated_mark_end() and + * ewk_view_zoom_animated_mark_current() to given zoom level. + * + * @param o view object to set weak zoom level. + * @param zoom level to scale backing store. + * @param cx horizontal center offset, relative to object (w/2 is middle). + * @param cy vertical center offset, relative to object (h/2 is middle). + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_zoom_weak_set(Evas_Object* o, float zoom, Evas_Coord cx, Evas_Coord cy) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET(sd, priv); + + EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api->zoom_weak_set, EINA_FALSE); + + if (!priv->settings.zoom_range.user_scalable) { + WRN("userScalable is false"); + return EINA_FALSE; + } + + if (zoom < priv->settings.zoom_range.min_scale) { + WRN("zoom level is < %f : %f", (double)priv->settings.zoom_range.min_scale, (double)zoom); + return EINA_FALSE; + } + if (zoom > priv->settings.zoom_range.max_scale) { + WRN("zoom level is > %f : %f", (double)priv->settings.zoom_range.max_scale, (double)zoom); + return EINA_FALSE; + } + + if (cx >= sd->view.w) + cx = sd->view.w - 1; + if (cy >= sd->view.h) + cy = sd->view.h - 1; + if (cx < 0) + cx = 0; + if (cy < 0) + cy = 0; + + sd->animated_zoom.zoom.start = ewk_frame_zoom_get(sd->main_frame); + sd->animated_zoom.zoom.end = zoom; + sd->animated_zoom.zoom.current = zoom; + return sd->api->zoom_weak_set(sd, zoom, cx, cy); +} + +/** + * Mark internal zoom animation state to given zoom. + * + * This does not modify any actual zoom in WebKit or backing store, + * just set the Ewk_View_Smart_Data->animated_zoom.zoom.start so + * sub-classes will know they should not reset their internal state. + * + * @param o view object to change value. + * @param zoom new start value. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see ewk_view_zoom_animated_set() + * @see ewk_view_zoom_weak_set() + * @see ewk_view_zoom_animated_mark_stop() + * @see ewk_view_zoom_animated_mark_end() + * @see ewk_view_zoom_animated_mark_current() + */ +Eina_Bool ewk_view_zoom_animated_mark_start(Evas_Object* o, float zoom) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + sd->animated_zoom.zoom.start = zoom; + return EINA_TRUE; +} + +/** + * Mark internal zoom animation state to given zoom. + * + * This does not modify any actual zoom in WebKit or backing store, + * just set the Ewk_View_Smart_Data->animated_zoom.zoom.end so + * sub-classes will know they should not reset their internal state. + * + * @param o view object to change value. + * @param zoom new end value. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see ewk_view_zoom_animated_set() + * @see ewk_view_zoom_weak_set() + * @see ewk_view_zoom_animated_mark_stop() + * @see ewk_view_zoom_animated_mark_start() + * @see ewk_view_zoom_animated_mark_current() + */ +Eina_Bool ewk_view_zoom_animated_mark_end(Evas_Object* o, float zoom) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + sd->animated_zoom.zoom.end = zoom; + return EINA_TRUE; +} + +/** + * Mark internal zoom animation state to given zoom. + * + * This does not modify any actual zoom in WebKit or backing store, + * just set the Ewk_View_Smart_Data->animated_zoom.zoom.current so + * sub-classes will know they should not reset their internal state. + * + * @param o view object to change value. + * @param zoom new current value. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + * + * @see ewk_view_zoom_animated_set() + * @see ewk_view_zoom_weak_set() + * @see ewk_view_zoom_animated_mark_stop() + * @see ewk_view_zoom_animated_mark_start() + * @see ewk_view_zoom_animated_mark_end() + */ +Eina_Bool ewk_view_zoom_animated_mark_current(Evas_Object* o, float zoom) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + sd->animated_zoom.zoom.current = zoom; + return EINA_TRUE; +} + +/** + * Unmark internal zoom animation state. + * + * This zero all start, end and current values. + * + * @param o view object to mark as animated is stopped. + * + * @see ewk_view_zoom_animated_mark_start() + * @see ewk_view_zoom_animated_mark_end() + * @see ewk_view_zoom_animated_mark_current() + * @see ewk_view_zoom_weak_set() + */ +Eina_Bool ewk_view_zoom_animated_mark_stop(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + _ewk_view_zoom_animated_mark_stop(sd); + return EINA_TRUE; +} + +/** + * Set the current zoom level while animating. + * + * If the view was already animating to another zoom, it will start + * from current point to the next provided zoom (@a zoom parameter) + * and duration (@a duration parameter). + * + * This is the recommended way to do transitions from one level to + * another. However, one may wish to do those from outside, in that + * case use ewk_view_zoom_weak_set() and later control intermediate + * states with ewk_view_zoom_animated_mark_current(), + * ewk_view_zoom_animated_mark_end() and + * ewk_view_zoom_animated_mark_stop(). + * + * @param o view object to animate. + * @param zoom final zoom level to use. + * @param duration time in seconds the animation should take. + * @param cx offset inside object that defines zoom center. 0 is left side. + * @param cy offset inside object that defines zoom center. 0 is top side. + * @return @c EINA_TRUE if animation will be started, @c EINA_FALSE if not + * because zoom is too small/big. + */ +Eina_Bool ewk_view_zoom_animated_set(Evas_Object* o, float zoom, float duration, Evas_Coord cx, Evas_Coord cy) +{ + double now; + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api->zoom_weak_set, EINA_FALSE); + + if (!priv->settings.zoom_range.user_scalable) { + WRN("userScalable is false"); + return EINA_FALSE; + } + + if (zoom < priv->settings.zoom_range.min_scale) { + WRN("zoom level is < %f : %f", (double)priv->settings.zoom_range.min_scale, (double)zoom); + return EINA_FALSE; + } + if (zoom > priv->settings.zoom_range.max_scale) { + WRN("zoom level is > %f : %f", (double)priv->settings.zoom_range.max_scale, (double)zoom); + return EINA_FALSE; + } + + if (priv->animated_zoom.animator) + priv->animated_zoom.zoom.start = _ewk_view_zoom_animated_current(priv); + else { + priv->animated_zoom.zoom.start = ewk_frame_zoom_get(sd->main_frame); + _ewk_view_zoom_animation_start(sd); + } + + if (cx < 0) + cx = 0; + if (cy < 0) + cy = 0; + + now = ecore_loop_time_get(); + priv->animated_zoom.time.start = now; + priv->animated_zoom.time.end = now + duration; + priv->animated_zoom.time.duration = duration; + priv->animated_zoom.zoom.end = zoom; + priv->animated_zoom.zoom.range = (priv->animated_zoom.zoom.end - priv->animated_zoom.zoom.start); + priv->animated_zoom.center.x = cx; + priv->animated_zoom.center.y = cy; + sd->animated_zoom.zoom.current = priv->animated_zoom.zoom.start; + sd->animated_zoom.zoom.start = priv->animated_zoom.zoom.start; + sd->animated_zoom.zoom.end = priv->animated_zoom.zoom.end; + + return EINA_TRUE; +} + +/** + * Query if zoom level just applies to text and not other elements. + * + * @param o view to query setting. + * + * @return @c EINA_TRUE if just text are scaled, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_zoom_text_only_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_zoom_text_only_get(sd->main_frame); +} + +/** + * Set if zoom level just applies to text and not other elements. + * + * @param o view to change setting. + * @param setting @c EINA_TRUE if zoom should just be applied to text. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_zoom_text_only_set(Evas_Object* o, Eina_Bool setting) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + return ewk_frame_zoom_text_only_set(sd->main_frame, setting); +} + +/** + * Hint engine to pre-render region. + * + * Engines and backing store might be able to pre-render regions in + * order to speed up zooming or scrolling to that region. Not all + * engines might implement that and they will return @c EINA_FALSE + * in that case. + * + * The given region is a hint. Engines might do bigger or smaller area + * that covers that region. Pre-render might not be immediate, it may + * be postponed to a thread, operated cooperatively in the main loop + * and may be even ignored or cancelled afterwards. + * + * Multiple requests might be queued by engines. One can clear/forget + * about them with ewk_view_pre_render_cancel(). + * + * @param o view to ask pre-render of given region. + * @param x absolute coordinate (0=left) to pre-render at zoom. + * @param y absolute coordinate (0=top) to pre-render at zoom. + * @param w width to pre-render starting from @a x at zoom. + * @param h height to pre-render starting from @a y at zoom. + * @param zoom desired zoom. + * + * @return @c EINA_TRUE if request was accepted, @c EINA_FALSE + * otherwise (errors, pre-render not supported, etc). + * + * @see ewk_view_pre_render_cancel() + */ +Eina_Bool ewk_view_pre_render_region(Evas_Object* o, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api->pre_render_region, EINA_FALSE); + float cur_zoom = ewk_frame_zoom_get(sd->main_frame); + Evas_Coord cw, ch; + + if (cur_zoom < 0.00001) + return EINA_FALSE; + if (!ewk_frame_contents_size_get(sd->main_frame, &cw, &ch)) + return EINA_FALSE; + + cw *= zoom / cur_zoom; + ch *= zoom / cur_zoom; + DBG("region %d,%d+%dx%d @ %f contents=%dx%d", x, y, w, h, zoom, cw, ch); + + if (x + w > cw) + w = cw - x; + + if (y + h > ch) + h = ch - y; + + if (x < 0) { + w += x; + x = 0; + } + if (y < 0) { + h += y; + y = 0; + } + + return sd->api->pre_render_region(sd, x, y, w, h, zoom); +} + +/** + * Cancel (clear) previous pre-render requests. + * + * @param o view to clear pre-render requests. + */ +void ewk_view_pre_render_cancel(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + EINA_SAFETY_ON_NULL_RETURN(sd->api->pre_render_cancel); + sd->api->pre_render_cancel(sd); +} + +const char* ewk_view_setting_user_agent_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.user_agent; +} + +Eina_Bool ewk_view_setting_user_agent_set(Evas_Object* o, const char* user_agent) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (eina_stringshare_replace(&priv->settings.user_agent, user_agent)) { + WebCore::FrameLoaderClientEfl* client = static_cast(priv->main_frame->loader()->client()); + client->setCustomUserAgent(WebCore::String::fromUTF8(user_agent)); + } + return EINA_TRUE; +} + +const char* ewk_view_setting_user_stylesheet_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.user_stylesheet; +} + +Eina_Bool ewk_view_setting_user_stylesheet_set(Evas_Object* o, const char* uri) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (eina_stringshare_replace(&priv->settings.user_stylesheet, uri)) { + WebCore::KURL kurl(WebCore::KURL(), WebCore::String::fromUTF8(uri)); + priv->page_settings->setUserStyleSheetLocation(kurl); + } + return EINA_TRUE; +} + +Eina_Bool ewk_view_setting_auto_load_images_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return priv->settings.auto_load_images; +} + +Eina_Bool ewk_view_setting_auto_load_images_set(Evas_Object* o, Eina_Bool automatic) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + automatic = !!automatic; + if (priv->settings.auto_load_images != automatic) { + priv->page_settings->setLoadsImagesAutomatically(automatic); + priv->settings.auto_load_images = automatic; + } + return EINA_TRUE; +} + +Eina_Bool ewk_view_setting_auto_shrink_images_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return priv->settings.auto_shrink_images; +} + +Eina_Bool ewk_view_setting_auto_shrink_images_set(Evas_Object* o, Eina_Bool automatic) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + automatic = !!automatic; + if (priv->settings.auto_shrink_images != automatic) { + priv->page_settings->setShrinksStandaloneImagesToFit(automatic); + priv->settings.auto_shrink_images = automatic; + } + return EINA_TRUE; +} + +Eina_Bool ewk_view_setting_enable_scripts_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return priv->settings.enable_scripts; +} + +Eina_Bool ewk_view_setting_enable_scripts_set(Evas_Object* o, Eina_Bool enable) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + enable = !!enable; + if (priv->settings.enable_scripts != enable) { + priv->page_settings->setJavaScriptEnabled(enable); + priv->settings.enable_scripts = enable; + } + return EINA_TRUE; +} + +Eina_Bool ewk_view_setting_enable_plugins_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return priv->settings.enable_plugins; +} + +Eina_Bool ewk_view_setting_enable_plugins_set(Evas_Object* o, Eina_Bool enable) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + enable = !!enable; + if (priv->settings.enable_plugins != enable) { + priv->page_settings->setPluginsEnabled(enable); + priv->settings.enable_plugins = enable; + } + return EINA_TRUE; +} + +Eina_Bool ewk_view_setting_scripts_window_open_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return priv->settings.scripts_window_open; +} + +Eina_Bool ewk_view_setting_scripts_window_open_set(Evas_Object* o, Eina_Bool allow) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + allow = !!allow; + if (priv->settings.scripts_window_open != allow) { + priv->page_settings->setJavaScriptCanOpenWindowsAutomatically(allow); + priv->settings.scripts_window_open = allow; + } + return EINA_TRUE; +} + +Eina_Bool ewk_view_setting_resizable_textareas_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return priv->settings.resizable_textareas; +} + +Eina_Bool ewk_view_setting_resizable_textareas_set(Evas_Object* o, Eina_Bool enable) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + enable = !!enable; + if (priv->settings.resizable_textareas != enable) { + priv->page_settings->setTextAreasAreResizable(enable); + priv->settings.resizable_textareas = enable; + } + return EINA_TRUE; +} + +Eina_Bool ewk_view_setting_private_browsing_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return priv->settings.private_browsing; +} + +Eina_Bool ewk_view_setting_private_browsing_set(Evas_Object* o, Eina_Bool enable) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + enable = !!enable; + if (priv->settings.private_browsing != enable) { + priv->page_settings->setPrivateBrowsingEnabled(enable); + priv->settings.private_browsing = enable; + } + return EINA_TRUE; +} + +Eina_Bool ewk_view_setting_caret_browsing_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + return priv->settings.caret_browsing; +} + +Eina_Bool ewk_view_setting_caret_browsing_set(Evas_Object* o, Eina_Bool enable) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + enable = !!enable; + if (priv->settings.caret_browsing != enable) { + priv->page_settings->setCaretBrowsingEnabled(enable); + priv->settings.caret_browsing = enable; + } + return EINA_TRUE; +} + +/** + * Get current encoding of this View. + * + * @param o View. + * + * @return A pointer to an eina_strinshare containing the current custom + * encoding for View object @param o, or @c 0 if it's not set. + */ +const char* ewk_view_setting_encoding_custom_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + Evas_Object* main_frame = ewk_view_frame_main_get(o); + WebCore::Frame* core_frame = ewk_frame_core_get(main_frame); + + WebCore::String overrideEncoding = core_frame->loader()->documentLoader()->overrideEncoding(); + + if (overrideEncoding.isEmpty()) + return 0; + + eina_stringshare_replace(&priv->settings.encoding_custom, overrideEncoding.utf8().data()); + return priv->settings.encoding_custom; +} + +/** + * Set encoding of this View and reload page. + * + * @param o View. + * @param encoding The new encoding or @c 0 to restore the default encoding. + * + * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise. + */ +Eina_Bool ewk_view_setting_encoding_custom_set(Evas_Object* o, const char *encoding) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + Evas_Object* main_frame = ewk_view_frame_main_get(o); + WebCore::Frame* core_frame = ewk_frame_core_get(main_frame); +DBG("%s", encoding); + eina_stringshare_replace(&priv->settings.encoding_custom, encoding); + core_frame->loader()->reloadWithOverrideEncoding(WebCore::String::fromUTF8(encoding)); + + return EINA_TRUE; +} + +const char* ewk_view_setting_encoding_default_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.encoding_default; +} + +Eina_Bool ewk_view_setting_encoding_default_set(Evas_Object* o, const char* encoding) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (eina_stringshare_replace(&priv->settings.encoding_default, encoding)) + priv->page_settings->setDefaultTextEncodingName(WebCore::String::fromUTF8(encoding)); + return EINA_TRUE; +} + +int ewk_view_setting_font_minimum_size_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.font_minimum_size; +} + +Eina_Bool ewk_view_setting_font_minimum_size_set(Evas_Object* o, int size) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (priv->settings.font_minimum_size != size) { + priv->page_settings->setMinimumFontSize(size); + priv->settings.font_minimum_size = size; + } + return EINA_TRUE; +} + +int ewk_view_setting_font_minimum_logical_size_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.font_minimum_logical_size; +} + +Eina_Bool ewk_view_setting_font_minimum_logical_size_set(Evas_Object* o, int size) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (priv->settings.font_minimum_logical_size != size) { + priv->page_settings->setMinimumLogicalFontSize(size); + priv->settings.font_minimum_logical_size = size; + } + return EINA_TRUE; +} + +int ewk_view_setting_font_default_size_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.font_default_size; +} + +Eina_Bool ewk_view_setting_font_default_size_set(Evas_Object* o, int size) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (priv->settings.font_default_size != size) { + priv->page_settings->setDefaultFontSize(size); + priv->settings.font_default_size = size; + } + return EINA_TRUE; +} + +int ewk_view_setting_font_monospace_size_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.font_monospace_size; +} + +Eina_Bool ewk_view_setting_font_monospace_size_set(Evas_Object* o, int size) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (priv->settings.font_monospace_size != size) { + priv->page_settings->setDefaultFixedFontSize(size); + priv->settings.font_monospace_size = size; + } + return EINA_TRUE; +} + +const char* ewk_view_setting_font_standard_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.font_standard; +} + +Eina_Bool ewk_view_setting_font_standard_set(Evas_Object* o, const char* family) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (eina_stringshare_replace(&priv->settings.font_standard, family)) { + WebCore::AtomicString s = WebCore::String::fromUTF8(family); + priv->page_settings->setStandardFontFamily(s); + } + return EINA_TRUE; +} + +const char* ewk_view_setting_font_cursive_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.font_cursive; +} + +Eina_Bool ewk_view_setting_font_cursive_set(Evas_Object* o, const char* family) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (eina_stringshare_replace(&priv->settings.font_cursive, family)) { + WebCore::AtomicString s = WebCore::String::fromUTF8(family); + priv->page_settings->setCursiveFontFamily(s); + } + return EINA_TRUE; +} + +const char* ewk_view_setting_font_fantasy_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.font_fantasy; +} + +Eina_Bool ewk_view_setting_font_fantasy_set(Evas_Object* o, const char* family) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (eina_stringshare_replace(&priv->settings.font_fantasy, family)) { + WebCore::AtomicString s = WebCore::String::fromUTF8(family); + priv->page_settings->setFantasyFontFamily(s); + } + return EINA_TRUE; +} + +const char* ewk_view_setting_font_monospace_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.font_monospace; +} + +Eina_Bool ewk_view_setting_font_monospace_set(Evas_Object* o, const char* family) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (eina_stringshare_replace(&priv->settings.font_monospace, family)) { + WebCore::AtomicString s = WebCore::String::fromUTF8(family); + priv->page_settings->setFixedFontFamily(s); + } + return EINA_TRUE; +} + +const char* ewk_view_setting_font_serif_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.font_serif; +} + +Eina_Bool ewk_view_setting_font_serif_set(Evas_Object* o, const char* family) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (eina_stringshare_replace(&priv->settings.font_serif, family)) { + WebCore::AtomicString s = WebCore::String::fromUTF8(family); + priv->page_settings->setSerifFontFamily(s); + } + return EINA_TRUE; +} + +const char* ewk_view_setting_font_sans_serif_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->settings.font_sans_serif; +} + +Eina_Bool ewk_view_setting_font_sans_serif_set(Evas_Object* o, const char* family) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + if (eina_stringshare_replace(&priv->settings.font_sans_serif, family)) { + WebCore::AtomicString s = WebCore::String::fromUTF8(family); + priv->page_settings->setSansSerifFontFamily(s); + } + return EINA_TRUE; +} + +/** + * Similar to evas_object_smart_data_get(), but does type checking. + * + * @param o view object to query internal data. + * @return internal data or @c 0 on errors (ie: incorrect type of @a o). + */ +Ewk_View_Smart_Data* ewk_view_smart_data_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + return sd; +} + +/** + * Gets the internal array of repaint requests. + * + * This array should not be modified anyhow. It should be processed + * immediately as any further ewk_view call might change it, like + * those that add repaints or flush them, so be sure that your code + * does not call any of those while you process the repaints, + * otherwise copy the array. + * + * @param priv private handle pointer of the view to get repaints. + * @param count where to return the number of elements of returned array. + * + * @return reference to array of requested repaints. + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +const Eina_Rectangle* ewk_view_repaints_get(const Ewk_View_Private_Data* priv, size_t* count) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(count, 0); + if (count) + *count = 0; + EINA_SAFETY_ON_NULL_RETURN_VAL(priv, 0); + if (count) + *count = priv->repaints.count; + return priv->repaints.array; +} + +/** + * Gets the internal array of scroll requests. + * + * This array should not be modified anyhow. It should be processed + * immediately as any further ewk_view call might change it, like + * those that add scrolls or flush them, so be sure that your code + * does not call any of those while you process the scrolls, + * otherwise copy the array. + * + * @param priv private handle pointer of the view to get scrolls. + * @param count where to return the number of elements of returned array. + * + * @return reference to array of requested scrolls. + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +const Ewk_Scroll_Request* ewk_view_scroll_requests_get(const Ewk_View_Private_Data* priv, size_t* count) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(count, 0); + if (count) + *count = 0; + EINA_SAFETY_ON_NULL_RETURN_VAL(priv, 0); + if (count) + *count = priv->scrolls.count; + return priv->scrolls.array; +} + +/** + * Add a new repaint request to queue. + * + * The repaints are assumed to be relative to current viewport. + * + * @param priv private handle pointer of the view to add repaint request. + * @param x horizontal position relative to current view port (scrolled). + * @param y vertical position relative to current view port (scrolled). + * @param w width of area to be repainted + * @param h height of area to be repainted + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +void ewk_view_repaint_add(Ewk_View_Private_Data* priv, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) +{ + EINA_SAFETY_ON_NULL_RETURN(priv); + _ewk_view_repaint_add(priv, x, y, w, h); +} + +/** + * Do layout if required, applied recursively. + * + * @param priv private handle pointer of the view to layout. + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +void ewk_view_layout_if_needed_recursive(Ewk_View_Private_Data* priv) +{ + EINA_SAFETY_ON_NULL_RETURN(priv); + + WebCore::FrameView* v = priv->main_frame->view(); + if (!v) { + ERR("no main frame view"); + return; + } + v->layoutIfNeededRecursive(); +} + +void ewk_view_scrolls_process(Ewk_View_Smart_Data* sd) +{ + EINA_SAFETY_ON_NULL_RETURN(sd); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv); + if (!sd->api->scrolls_process(sd)) + ERR("failed to process scrolls."); + _ewk_view_scrolls_flush(priv); +} + +struct _Ewk_View_Paint_Context { + WebCore::GraphicsContext* gc; + WebCore::FrameView* view; + cairo_t* cr; +}; + +/** + * Create a new paint context using the view as source and cairo as output. + * + * @param priv private handle pointer of the view to use as paint source. + * @param cr cairo context to use as paint destination. A new + * reference is taken, so it's safe to call cairo_destroy() + * after this function returns. + * + * @return newly allocated instance or @c 0 on errors. + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +Ewk_View_Paint_Context* ewk_view_paint_context_new(Ewk_View_Private_Data* priv, cairo_t* cr) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(priv, 0); + EINA_SAFETY_ON_NULL_RETURN_VAL(cr, 0); + EINA_SAFETY_ON_NULL_RETURN_VAL(priv->main_frame, 0); + WebCore::FrameView* view = priv->main_frame->view(); + EINA_SAFETY_ON_NULL_RETURN_VAL(view, 0); + Ewk_View_Paint_Context* ctxt = (Ewk_View_Paint_Context*)malloc(sizeof(*ctxt)); + EINA_SAFETY_ON_NULL_RETURN_VAL(ctxt, 0); + + ctxt->gc = new WebCore::GraphicsContext(cr); + if (!ctxt->gc) { + free(ctxt); + return 0; + } + ctxt->view = view; + ctxt->cr = cairo_reference(cr); + return ctxt; +} + +/** + * Destroy previously created paint context. + * + * @param ctxt paint context to destroy. Must @b not be @c 0. + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +void ewk_view_paint_context_free(Ewk_View_Paint_Context* ctxt) +{ + EINA_SAFETY_ON_NULL_RETURN(ctxt); + delete ctxt->gc; + cairo_destroy(ctxt->cr); + free(ctxt); +} + +/** + * Save (push to stack) paint context status. + * + * @param ctxt paint context to save. Must @b not be @c 0. + * + * @see ewk_view_paint_context_restore() + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +void ewk_view_paint_context_save(Ewk_View_Paint_Context* ctxt) +{ + EINA_SAFETY_ON_NULL_RETURN(ctxt); + cairo_save(ctxt->cr); + ctxt->gc->save(); +} + +/** + * Restore (pop from stack) paint context status. + * + * @param ctxt paint context to restore. Must @b not be @c 0. + * + * @see ewk_view_paint_context_save() + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +void ewk_view_paint_context_restore(Ewk_View_Paint_Context* ctxt) +{ + EINA_SAFETY_ON_NULL_RETURN(ctxt); + ctxt->gc->restore(); + cairo_restore(ctxt->cr); +} + +/** + * Clip paint context drawings to given area. + * + * @param ctxt paint context to clip. Must @b not be @c 0. + * @param area clip area to use. + * + * @see ewk_view_paint_context_save() + * @see ewk_view_paint_context_restore() + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +void ewk_view_paint_context_clip(Ewk_View_Paint_Context* ctxt, const Eina_Rectangle* area) +{ + EINA_SAFETY_ON_NULL_RETURN(ctxt); + EINA_SAFETY_ON_NULL_RETURN(area); + ctxt->gc->clip(WebCore::IntRect(area->x, area->y, area->w, area->h)); +} + +/** + * Paint using context using given area. + * + * @param ctxt paint context to paint. Must @b not be @c 0. + * @param area paint area to use. Coordinates are relative to current viewport, + * thus "scrolled". + * + * @note one may use cairo functions on the cairo context to + * translate, scale or any modification that may fit his desires. + * + * @see ewk_view_paint_context_clip() + * @see ewk_view_paint_context_paint_contents() + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +void ewk_view_paint_context_paint(Ewk_View_Paint_Context* ctxt, const Eina_Rectangle* area) +{ + EINA_SAFETY_ON_NULL_RETURN(ctxt); + EINA_SAFETY_ON_NULL_RETURN(area); + + WebCore::IntRect rect(area->x, area->y, area->w, area->h); + + if (ctxt->view->isTransparent()) + ctxt->gc->clearRect(rect); + ctxt->view->paint(ctxt->gc, rect); +} + +/** + * Paint just contents using context using given area. + * + * Unlike ewk_view_paint_context_paint(), this function paint just + * bare contents and ignores any scrolling, scrollbars and extras. It + * will walk the rendering tree and paint contents inside the given + * area to the cairo context specified in @a ctxt. + * + * @param ctxt paint context to paint. Must @b not be @c 0. + * @param area paint area to use. Coordinates are absolute to page. + * + * @note one may use cairo functions on the cairo context to + * translate, scale or any modification that may fit his desires. + * + * @see ewk_view_paint_context_clip() + * @see ewk_view_paint_context_paint() + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +void ewk_view_paint_context_paint_contents(Ewk_View_Paint_Context* ctxt, const Eina_Rectangle* area) +{ + EINA_SAFETY_ON_NULL_RETURN(ctxt); + EINA_SAFETY_ON_NULL_RETURN(area); + + WebCore::IntRect rect(area->x, area->y, area->w, area->h); + + if (ctxt->view->isTransparent()) + ctxt->gc->clearRect(rect); + + ctxt->view->paintContents(ctxt->gc, rect); +} + +/** + * Scale the contents by the given factors. + * + * This function applies a scaling transformation using Cairo. + * + * @param ctxt paint context to paint. Must @b not be @c 0. + * @param scale_x scale factor for the X dimension. + * @param scale_y scale factor for the Y dimension. + */ +void ewk_view_paint_context_scale(Ewk_View_Paint_Context* ctxt, float scale_x, float scale_y) +{ + EINA_SAFETY_ON_NULL_RETURN(ctxt); + + ctxt->gc->scale(WebCore::FloatSize(scale_x, scale_y)); +} + +/** + * Performs a translation of the origin coordinates. + * + * This function moves the origin coordinates by @p x and @p y pixels. + * + * @param ctxt paint context to paint. Must @b not be @c 0. + * @param x amount of pixels to translate in the X dimension. + * @param y amount of pixels to translate in the Y dimension. + */ +void ewk_view_paint_context_translate(Ewk_View_Paint_Context* ctxt, float x, float y) +{ + EINA_SAFETY_ON_NULL_RETURN(ctxt); + + ctxt->gc->translate(x, y); +} + +/** + * Paint using given graphics context the given area. + * + * This uses viewport relative area and will also handle scrollbars + * and other extra elements. See ewk_view_paint_contents() for the + * alternative function. + * + * @param priv private handle pointer of view to use as paint source. + * @param cr cairo context to use as paint destination. Its state will + * be saved before operation and restored afterwards. + * @param area viewport relative geometry to paint. + * + * @return @c EINA_TRUE on success and @c EINA_FALSE on failure, like + * incorrect parameters. + * + * @note this is an easy to use version, but internal structures are + * always created, then graphics context is clipped, then + * painted, restored and destroyed. This might not be optimum, + * so using #Ewk_View_Paint_Context may be a better solutions + * for large number of operations. + * + * @see ewk_view_paint_contents() + * @see ewk_view_paint_context_paint() + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +Eina_Bool ewk_view_paint(Ewk_View_Private_Data* priv, cairo_t* cr, const Eina_Rectangle* area) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(priv, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(cr, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(area, EINA_FALSE); + WebCore::FrameView* view = priv->main_frame->view(); + EINA_SAFETY_ON_NULL_RETURN_VAL(view, EINA_FALSE); + + if (view->needsLayout()) + view->forceLayout(); + WebCore::GraphicsContext gc(cr); + WebCore::IntRect rect(area->x, area->y, area->w, area->h); + + cairo_save(cr); + gc.save(); + gc.clip(rect); + if (view->isTransparent()) + gc.clearRect(rect); + view->paint(&gc, rect); + gc.restore(); + cairo_restore(cr); + + return EINA_TRUE; +} + +/** + * Paint just contents using given graphics context the given area. + * + * This uses absolute coordinates for area and will just handle + * contents, no scrollbars or extras. See ewk_view_paint() for the + * alternative solution. + * + * @param priv private handle pointer of view to use as paint source. + * @param cr cairo context to use as paint destination. Its state will + * be saved before operation and restored afterwards. + * @param area absolute geometry to paint. + * + * @return @c EINA_TRUE on success and @c EINA_FALSE on failure, like + * incorrect parameters. + * + * @note this is an easy to use version, but internal structures are + * always created, then graphics context is clipped, then + * painted, restored and destroyed. This might not be optimum, + * so using #Ewk_View_Paint_Context may be a better solutions + * for large number of operations. + * + * @see ewk_view_paint() + * @see ewk_view_paint_context_paint_contents() + * + * @note this is not for general use but just for subclasses that want + * to define their own backing store. + */ +Eina_Bool ewk_view_paint_contents(Ewk_View_Private_Data* priv, cairo_t* cr, const Eina_Rectangle* area) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(priv, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(cr, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(area, EINA_FALSE); + WebCore::FrameView* view = priv->main_frame->view(); + EINA_SAFETY_ON_NULL_RETURN_VAL(view, EINA_FALSE); + + WebCore::GraphicsContext gc(cr); + WebCore::IntRect rect(area->x, area->y, area->w, area->h); + + cairo_save(cr); + gc.save(); + gc.clip(rect); + if (view->isTransparent()) + gc.clearRect(rect); + view->paintContents(&gc, rect); + gc.restore(); + cairo_restore(cr); + + return EINA_TRUE; +} + + +/* internal methods ****************************************************/ +/** + * @internal + * Reports the view is ready to be displayed as all elements are aready. + * + * Emits signal: "ready" with no parameters. + */ +void ewk_view_ready(Evas_Object* o) +{ + DBG("o=%p", o); + evas_object_smart_callback_call(o, "ready", 0); +} + +/** + * @internal + * The view title was changed by the frame loader. + * + * Emits signal: "title,changed" with pointer to new title string. + */ +void ewk_view_title_set(Evas_Object* o, const char* title) +{ + DBG("o=%p, title=%s", o, title ? title : "(null)"); + evas_object_smart_callback_call(o, "title,changed", (void*)title); +} + +/** + * @internal + * Reports that main frame's uri changed. + * + * Emits signal: "uri,changed" with pointer to the new uri string. + */ +void ewk_view_uri_changed(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + const char* uri = ewk_frame_uri_get(sd->main_frame); + DBG("o=%p, uri=%s", o, uri ? uri : "(null)"); + evas_object_smart_callback_call(o, "uri,changed", (void*)uri); +} + +/** + * @internal + * Reports the view started loading something. + * + * @param o View. + * + * Emits signal: "load,started" with no parameters. + */ +void ewk_view_load_started(Evas_Object* o) +{ + DBG("o=%p", o); + evas_object_smart_callback_call(o, "load,started", 0); +} + +/** + * Reports the frame started loading something. + * + * @param o View. + * + * Emits signal: "load,started" on main frame with no parameters. + */ +void ewk_view_frame_main_load_started(Evas_Object* o) +{ + DBG("o=%p", o); + Evas_Object* frame = ewk_view_frame_main_get(o); + evas_object_smart_callback_call(frame, "load,started", 0); +} + +/** + * @internal + * Reports the main frame started provisional load. + * + * @param o View. + * + * Emits signal: "load,provisional" on View with no parameters. + */ +void ewk_view_load_provisional(Evas_Object* o) +{ + DBG("o=%p", o); + evas_object_smart_callback_call(o, "load,provisional", 0); +} + +/** + * @internal + * Reports view can be shown after a new window is created. + * + * @param o Frame. + * + * Emits signal: "load,newwindow,show" on view with no parameters. + */ +void ewk_view_load_show(Evas_Object* o) +{ + DBG("o=%p", o); + evas_object_smart_callback_call(o, "load,newwindow,show", 0); +} + + +/** + * @internal + * Reports the main frame was cleared. + * + * @param o View. + */ +void ewk_view_frame_main_cleared(Evas_Object* o) +{ + DBG("o=%p", o); + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + EINA_SAFETY_ON_NULL_RETURN(sd->api->flush); + sd->api->flush(sd); +} + +/** + * @internal + * Reports the main frame received an icon. + * + * @param o View. + * + * Emits signal: "icon,received" with no parameters. + */ +void ewk_view_frame_main_icon_received(Evas_Object* o) +{ + DBG("o=%p", o); + Evas_Object* frame = ewk_view_frame_main_get(o); + evas_object_smart_callback_call(frame, "icon,received", 0); +} + +/** + * @internal + * Reports load finished, optionally with error information. + * + * Emits signal: "load,finished" with pointer to #Ewk_Frame_Load_Error + * if any error, or @c 0 if successful load. + * + * @note there should not be any error stuff here, but trying to be + * compatible with previous WebKit. + */ +void ewk_view_load_finished(Evas_Object* o, const Ewk_Frame_Load_Error* error) +{ + DBG("o=%p, error=%p", o, error); + evas_object_smart_callback_call(o, "load,finished", (void*)error); +} + +/** + * @internal + * Reports load failed with error information. + * + * Emits signal: "load,error" with pointer to Ewk_Frame_Load_Error. + */ +void ewk_view_load_error(Evas_Object* o, const Ewk_Frame_Load_Error* error) +{ + DBG("o=%p, error=%p", o, error); + evas_object_smart_callback_call(o, "load,error", (void*)error); +} + +/** + * @internal + * Reports load progress changed. + * + * Emits signal: "load,progress" with pointer to a double from 0.0 to 1.0. + */ +void ewk_view_load_progress_changed(Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv); + + // Evas_Coord w, h; + double progress = priv->page->progress()->estimatedProgress(); + + DBG("o=%p (p=%0.3f)", o, progress); + + evas_object_smart_callback_call(o, "load,progress", &progress); +} + +/** + * @internal + * Reports view @param o should be restored to default conditions + * + * @param o View. + * @param frame Frame that originated restore. + * + * Emits signal: "restore" with frame. + */ +void ewk_view_restore_state(Evas_Object* o, Evas_Object* frame) +{ + evas_object_smart_callback_call(o, "restore", frame); +} + +/** + * @internal + * Delegates to browser the creation of a new window. If it is not implemented, + * current view is returned, so navigation might continue in same window. If + * browser supports the creation of new windows, a new Ewk_Window_Features is + * created and passed to browser. If it intends to keep the request for opening + * the window later it must increments the Ewk_Winwdow_Features ref count by + * calling ewk_window_features_ref(window_features). Otherwise this struct will + * be freed after returning to this function. + * + * @param o Current view. + * @param javascript @c EINA_TRUE if the new window is originated from javascript, + * @c EINA_FALSE otherwise + * @param window_features Features of the new window being created. If it's @c + * NULL, it will be created a window with default features. + * + * @return New view, in case smart class implements the creation of new windows; + * else, current view @param o. + * + * @see ewk_window_features_ref(). + */ +Evas_Object* ewk_view_window_create(Evas_Object* o, Eina_Bool javascript, const WebCore::WindowFeatures* coreFeatures) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + + if (!sd->api->window_create) + return o; + + Ewk_Window_Features* window_features = ewk_window_features_new_from_core(coreFeatures); + Evas_Object* view = sd->api->window_create(sd, javascript, window_features); + ewk_window_features_unref(window_features); + + return view; +} + +/** + * @internal + * Reports mouse has moved over a link. + * + * Emits signal: "link,hover,in" + */ +void ewk_view_mouse_link_hover_in(Evas_Object* o, void* data) +{ + evas_object_smart_callback_call(o, "link,hover,in", data); +} + +/** + * @internal + * Reports mouse is not over a link anymore. + * + * Emits signal: "link,hover,out" + */ +void ewk_view_mouse_link_hover_out(Evas_Object* o) +{ + evas_object_smart_callback_call(o, "link,hover,out", 0); +} + +/** + * @internal + * Set toolbar visible. + * + * Emits signal: "toolbars,visible,set" with a pointer to a boolean. + */ +void ewk_view_toolbars_visible_set(Evas_Object* o, Eina_Bool visible) +{ + DBG("o=%p (visible=%d)", o, !!visible); + evas_object_smart_callback_call(o, "toolbars,visible,set", &visible); +} + +/** + * @internal + * Get toolbar visibility. + * + * @param o View. + * @param visible boolean pointer in which to save the result. It defaults + * to @c FALSE, i.e. if browser does no listen to emitted signal, it means + * there are no toolbars and therefore they are not visible. + * + * Emits signal: "toolbars,visible,get" with a pointer to a boolean. + */ +void ewk_view_toolbars_visible_get(Evas_Object* o, Eina_Bool* visible) +{ + DBG("%s, o=%p", __func__, o); + *visible = EINA_FALSE; + evas_object_smart_callback_call(o, "toolbars,visible,get", visible); +} + +/** + * @internal + * Set statusbar visible. + * + * @param o View. + * @param visible @c TRUE if statusbar are visible, @c FALSE otherwise. + * + * Emits signal: "statusbar,visible,set" with a pointer to a boolean. + */ +void ewk_view_statusbar_visible_set(Evas_Object* o, Eina_Bool visible) +{ + DBG("o=%p (visible=%d)", o, !!visible); + evas_object_smart_callback_call(o, "statusbar,visible,set", &visible); +} + +/** + * @internal + * Get statusbar visibility. + * + * @param o View. + * @param visible boolean pointer in which to save the result. It defaults + * to @c FALSE, i.e. if browser does no listen to emitted signal, it means + * there is no statusbar and therefore it is not visible. + * + * Emits signal: "statusbar,visible,get" with a pointer to a boolean. + */ +void ewk_view_statusbar_visible_get(Evas_Object* o, Eina_Bool* visible) +{ + DBG("%s, o=%p", __func__, o); + *visible = EINA_FALSE; + evas_object_smart_callback_call(o, "statusbar,visible,get", visible); +} + +/** + * @internal + * Set text of statusbar + * + * @param o View. + * @param text New text to put on statusbar. + * + * Emits signal: "statusbar,text,set" with a string. + */ +void ewk_view_statusbar_text_set(Evas_Object* o, const char* text) +{ + DBG("o=%p (text=%s)", o, text); + INF("status bar text set: %s", text); + evas_object_smart_callback_call(o, "statusbar,text,set", (void *)text); +} + +/** + * @internal + * Set scrollbars visible. + * + * @param o View. + * @param visible @c TRUE if scrollbars are visible, @c FALSE otherwise. + * + * Emits signal: "scrollbars,visible,set" with a pointer to a boolean. + */ +void ewk_view_scrollbars_visible_set(Evas_Object* o, Eina_Bool visible) +{ + DBG("o=%p (visible=%d)", o, !!visible); + evas_object_smart_callback_call(o, "scrollbars,visible,set", &visible); +} + +/** + * @internal + * Get scrollbars visibility. + * + * @param o View. + * @param visible boolean pointer in which to save the result. It defaults + * to @c FALSE, i.e. if browser does no listen to emitted signal, it means + * there are no scrollbars and therefore they are not visible. + * + * Emits signal: "scrollbars,visible,get" with a pointer to a boolean. + */ +void ewk_view_scrollbars_visible_get(Evas_Object* o, Eina_Bool* visible) +{ + DBG("%s, o=%p", __func__, o); + *visible = EINA_FALSE; + evas_object_smart_callback_call(o, "scrollbars,visible,get", visible); +} + +/** + * @internal + * Set menubar visible. + * + * @param o View. + * @param visible @c TRUE if menubar is visible, @c FALSE otherwise. + * + * Emits signal: "menubar,visible,set" with a pointer to a boolean. + */ +void ewk_view_menubar_visible_set(Evas_Object* o, Eina_Bool visible) +{ + DBG("o=%p (visible=%d)", o, !!visible); + evas_object_smart_callback_call(o, "menubar,visible,set", &visible); +} + +/** + * @internal + * Get menubar visibility. + * + * @param o View. + * @param visible boolean pointer in which to save the result. It defaults + * to @c FALSE, i.e. if browser does no listen to emitted signal, it means + * there is no menubar and therefore it is not visible. + * + * Emits signal: "menubar,visible,get" with a pointer to a boolean. + */ +void ewk_view_menubar_visible_get(Evas_Object* o, Eina_Bool* visible) +{ + DBG("%s, o=%p", __func__, o); + *visible = EINA_FALSE; + evas_object_smart_callback_call(o, "menubar,visible,get", visible); +} + +/** + * @internal + * Set tooltip text and display if it is currently hidden. + * + * @param o View. + * @param text Text to set tooltip to. + * + * Emits signal: "tooltip,text,set" with a string. If tooltip must be actually + * removed, text will be 0 or '\0' + */ +void ewk_view_tooltip_text_set(Evas_Object* o, const char* text) +{ + DBG("o=%p text=%s", o, text); + evas_object_smart_callback_call(o, "tooltip,text,set", (void *)text); +} + +/** + * @internal + * + * @param o View. + * @param message String to show on console. + * @param lineNumber Line number. + * @sourceID Source id. + * + */ +void ewk_view_add_console_message(Evas_Object* o, const char* message, unsigned int lineNumber, const char* sourceID) +{ + DBG("o=%p message=%s lineNumber=%u sourceID=%s", o, message, lineNumber, sourceID); + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + EINA_SAFETY_ON_NULL_RETURN(sd->api); + EINA_SAFETY_ON_NULL_RETURN(sd->api->add_console_message); + sd->api->add_console_message(sd, message, lineNumber, sourceID); +} + +void ewk_view_run_javascript_alert(Evas_Object* o, Evas_Object* frame, const char* message) +{ + DBG("o=%p frame=%p message=%s", o, frame, message); + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + EINA_SAFETY_ON_NULL_RETURN(sd->api); + + if (!sd->api->run_javascript_alert) + return; + + sd->api->run_javascript_alert(sd, frame, message); +} + +Eina_Bool ewk_view_run_javascript_confirm(Evas_Object* o, Evas_Object* frame, const char* message) +{ + DBG("o=%p frame=%p message=%s", o, frame, message); + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api, EINA_FALSE); + + if (!sd->api->run_javascript_confirm) + return EINA_FALSE; + + return sd->api->run_javascript_confirm(sd, frame, message); +} + +Eina_Bool ewk_view_run_javascript_prompt(Evas_Object* o, Evas_Object* frame, const char* message, const char* defaultValue, char** value) +{ + DBG("o=%p frame=%p message=%s", o, frame, message); + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api, EINA_FALSE); + + if (!sd->api->run_javascript_prompt) + return EINA_FALSE; + + return sd->api->run_javascript_prompt(sd, frame, message, defaultValue, value); +} + +/** + * @internal + * Delegates to client to decide whether a script must be stopped because it's + * running for too long. If client does not implement it, it goes to default + * implementation, which logs and returns EINA_FALSE. Client may remove log by + * setting this function 0, which will just return EINA_FALSE. + * + * @param o View. + * + * @return @c EINA_TRUE if script should be stopped; @c EINA_FALSE otherwise + */ +Eina_Bool ewk_view_should_interrupt_javascript(Evas_Object* o) +{ + DBG("o=%p", o); + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api, EINA_FALSE); + + if (!sd->api->should_interrupt_javascript) + return EINA_FALSE; + + return sd->api->should_interrupt_javascript(sd); +} + +/** + * @internal + * This is called whenever the web site shown in @param frame is asking to store data + * to the database @param databaseName and the quota allocated to that web site + * is exceeded. Browser may use this to increase the size of quota before the + * originating operationa fails. + * + * @param o View. + * @param frame The frame whose web page exceeded its database quota. + * @param databaseName Database name. + * @param current_size Current size of this database + * @param expected_size The expected size of this database in order to fulfill + * site's requirement. + */ +uint64_t ewk_view_exceeded_database_quota(Evas_Object* o, Evas_Object* frame, const char* databaseName, uint64_t current_size, uint64_t expected_size) +{ + DBG("o=%p", o); + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api, 0); + if (!sd->api->exceeded_database_quota) + return 0; + + INF("current_size=%"PRIu64" expected_size="PRIu64, current_size, expected_size); + return sd->api->exceeded_database_quota(sd, frame, databaseName, current_size, expected_size); +} + +/** + * @internal + * Open panel to choose a file. + * + * @param o View. + * @param frame Frame in which operation is required. + * @param allows_multiple_files @c EINA_TRUE when more than one file may be + * selected, @c EINA_FALSE otherwise + * @suggested_filenames List of suggested files to select. It's advisable to + * just ignore this value, since it's a source of security flaw. + * @selected_filenames List of files selected. + * + * @return @EINA_FALSE if user canceled file selection; @EINA_TRUE if confirmed. + */ +Eina_Bool ewk_view_run_open_panel(Evas_Object* o, Evas_Object* frame, Eina_Bool allows_multiple_files, const Eina_List* suggested_filenames, Eina_List** selected_filenames) +{ + DBG("o=%p frame=%p allows_multiple_files=%d", o, frame, allows_multiple_files); + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api, EINA_FALSE); + Eina_Bool confirm; + + if (!sd->api->run_open_panel) + return EINA_FALSE; + + *selected_filenames = 0; + + confirm = sd->api->run_open_panel(sd, frame, allows_multiple_files, suggested_filenames, selected_filenames); + if (!confirm && *selected_filenames) + ERR("Canceled file selection, but selected filenames != 0. Free names before return."); + return confirm; +} + +void ewk_view_repaint(Evas_Object* o, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) +{ + DBG("o=%p, region=%d,%d + %dx%d", o, x, y, w, h); + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv); + + if (!priv->main_frame->contentRenderer()) { + ERR("no main frame content renderer."); + return; + } + + _ewk_view_repaint_add(priv, x, y, w, h); + _ewk_view_smart_changed(sd); +} + +void ewk_view_scroll(Evas_Object* o, Evas_Coord dx, Evas_Coord dy, Evas_Coord sx, Evas_Coord sy, Evas_Coord sw, Evas_Coord sh, Evas_Coord cx, Evas_Coord cy, Evas_Coord cw, Evas_Coord ch, Eina_Bool main_frame) +{ + DBG("o=%p, delta: %d,%d, scroll: %d,%d+%dx%d, clip: %d,%d+%dx%d", + o, dx, dy, sx, sy, sw, sh, cx, cy, cw, ch); + + if ((sx != cx) || (sy != cy) || (sw != cw) || (sh != ch)) + WRN("scroll region and clip are different! %d,%d+%dx%d and %d,%d+%dx%d", + sx, sy, sw, sh, cx, cy, cw, ch); + + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv); + EINA_SAFETY_ON_TRUE_RETURN(!dx && !dy); + + _ewk_view_scroll_add(priv, dx, dy, sx, sy, sw, sh, main_frame); + _ewk_view_smart_changed(sd); +} + +WebCore::Page* ewk_view_core_page_get(const Evas_Object* o) +{ + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + return priv->page; +} + +/** + * Creates a new frame for given url and owner element. + * + * Emits "frame,created" with the new frame object on success. + */ +WTF::PassRefPtr ewk_view_frame_create(Evas_Object* o, Evas_Object* frame, const WebCore::String& name, WebCore::HTMLFrameOwnerElement* ownerElement, const WebCore::KURL& url, const WebCore::String& referrer) +{ + DBG("o=%p, frame=%p, name=%s, ownerElement=%p, url=%s, referrer=%s", + o, frame, name.utf8().data(), ownerElement, + url.prettyURL().utf8().data(), referrer.utf8().data()); + + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, 0); + + WTF::RefPtr cf = _ewk_view_core_frame_new + (sd, priv, ownerElement); + if (!cf) { + ERR("Could not create child core frame '%s'", name.utf8().data()); + return 0; + } + + if (!ewk_frame_child_add(frame, cf, name, url, referrer)) { + ERR("Could not create child frame object '%s'", name.utf8().data()); + return 0; + } + + // The creation of the frame may have removed itself already. + if (!cf->page() || !cf->tree() || !cf->tree()->parent()) + return 0; + + sd->changed.frame_rect = EINA_TRUE; + _ewk_view_smart_changed(sd); + + evas_object_smart_callback_call(o, "frame,created", frame); + return cf.release(); +} + +WTF::PassRefPtr ewk_view_plugin_create(Evas_Object* o, Evas_Object* frame, const WebCore::IntSize& pluginSize, WebCore::HTMLPlugInElement* element, const WebCore::KURL& url, const WTF::Vector& paramNames, const WTF::Vector& paramValues, const WebCore::String& mimeType, bool loadManually) +{ + DBG("o=%p, frame=%p, size=%dx%d, element=%p, url=%s, mimeType=%s", + o, frame, pluginSize.width(), pluginSize.height(), element, + url.prettyURL().utf8().data(), mimeType.utf8().data()); + + EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0); + sd->changed.frame_rect = EINA_TRUE; + _ewk_view_smart_changed(sd); + + return ewk_frame_plugin_create + (frame, pluginSize, element, url, paramNames, paramValues, + mimeType, loadManually); +} + + +/** + * @internal + * + * Creates a new popup with options when a select widget was clicked. + * + * @param client PopupMenuClient instance that allows communication with webkit. + * @param selected Selected item. + * @param rect Menu's position. + * + * Emits: "popup,create" with a list of Ewk_Menu containing each item's data + */ +void ewk_view_popup_new(Evas_Object* o, WebCore::PopupMenuClient* client, int selected, const WebCore::IntRect& rect) +{ + INF("o=%p", o); + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv); + + if (priv->popup.menu_client) + ewk_view_popup_destroy(o); + + priv->popup.menu_client = client; + + // populate items + const int size = client->listSize(); + for (int i = 0; i < size; ++i) { + Ewk_Menu_Item* item = (Ewk_Menu_Item*) malloc(sizeof(*item)); + if (client->itemIsSeparator(i)) + item->type = EWK_MENU_SEPARATOR; + else if (client->itemIsLabel(i)) + item->type = EWK_MENU_GROUP; + else + item->type = EWK_MENU_OPTION; + item->text = eina_stringshare_add(client->itemText(i).utf8().data()); + + priv->popup.menu.items = eina_list_append(priv->popup.menu.items, item); + } + + priv->popup.menu.x = rect.x(); + priv->popup.menu.y = rect.y(); + priv->popup.menu.width = rect.width(); + priv->popup.menu.height = rect.height(); + evas_object_smart_callback_call(o, "popup,create", &priv->popup.menu); +} + +/** + * Destroy a previously created menu. + * + * Before destroying, it informs client that menu's data is ready to be + * destroyed by sending a "popup,willdelete" with a list of menu items. Then it + * removes any reference to menu inside webkit. It's safe to call this + * function either from inside webkit or from browser. + * + * @param o View. + * + * @returns EINA_TRUE in case menu was successfully destroyed or EINA_TRUE in + * case there wasn't any menu to be destroyed. + */ +Eina_Bool ewk_view_popup_destroy(Evas_Object* o) +{ + INF("o=%p", o); + EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE); + + if (!priv->popup.menu_client) + return EINA_FALSE; + + evas_object_smart_callback_call(o, "popup,willdelete", &priv->popup.menu); + + void* itemv; + EINA_LIST_FREE(priv->popup.menu.items, itemv) { + Ewk_Menu_Item* item = (Ewk_Menu_Item*)itemv; + eina_stringshare_del(item->text); + free(item); + } + priv->popup.menu_client->popupDidHide(); + priv->popup.menu_client = 0; + + return EINA_TRUE; +} + +/** + * Changes currently selected item. + * + * Changes the option selected in select widget. This is called by browser + * whenever user has chosen a different item. Most likely after calling this, a + * call to ewk_view_popup_destroy might be made in order to close the popup. + * + * @param o View. + * @index Index of selected item. + * + */ +void ewk_view_popup_selected_set(Evas_Object* o, int index) +{ + INF("o=%p", o); + EWK_VIEW_SD_GET_OR_RETURN(o, sd); + EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv); + EINA_SAFETY_ON_NULL_RETURN(priv->popup.menu_client); + + priv->popup.menu_client->valueChanged(index); +} + +/** + * @internal + * Request a download to user. + * + * @param o View. + * @oaram download Ewk_Download struct to be sent. + * + * Emits: "download,request" with an Ewk_Download containing the details of the + * requested download. The download per se must be handled outside of webkit. + */ +void ewk_view_download_request(Evas_Object* o, Ewk_Download* download) +{ + DBG("view=%p", o); + evas_object_smart_callback_call(o, "download,request", download); +} + +/** + * @internal + * Reports the viewport has changed. + * + * @param o view. + * @param w width. + * @param h height. + * @param init_scale initialScale value. + * @param max_scale maximumScale value. + * @param min_scale minimumScale value. + * @param user_scalable userscalable flag. + * + * Emits signal: "viewport,changed" with no parameters. + */ +void ewk_view_viewport_set(Evas_Object *o, float w, float h, float init_scale, float max_scale, float min_scale, float user_scalable) +{ + EWK_VIEW_SD_GET(o, sd); + EWK_VIEW_PRIV_GET(sd, priv); + + priv->settings.viewport.w = w; + priv->settings.viewport.h = h; + priv->settings.viewport.init_scale = init_scale; + priv->settings.viewport.min_scale = min_scale; + priv->settings.viewport.max_scale = max_scale; + priv->settings.viewport.user_scalable = user_scalable; + + evas_object_smart_callback_call(o, "viewport,changed", 0); +} + +/** + * Gets data of viewport meta tag. + * + * @param o view. + * @param w width. + * @param h height. + * @param init_scale initial Scale value. + * @param max_scale maximum Scale value. + * @param min_scale minimum Scale value. + * @param user_scalable user Scalable value. + */ +void ewk_view_viewport_get(Evas_Object *o, float* w, float* h, float* init_scale, float* max_scale, float* min_scale, float* user_scalable) +{ + EWK_VIEW_SD_GET(o, sd); + EWK_VIEW_PRIV_GET(sd, priv); + + if (w) + *w = priv->settings.viewport.w; + if (h) + *h = priv->settings.viewport.h; + if (init_scale) + *init_scale = priv->settings.viewport.init_scale; + if (max_scale) + *max_scale = priv->settings.viewport.max_scale; + if (min_scale) + *min_scale = priv->settings.viewport.min_scale; + if (user_scalable) + *user_scalable = priv->settings.viewport.user_scalable; +} + +/** + * Sets the zoom range. + * + * @param o view. + * @param min_scale minimum value of zoom range. + * @param max_scale maximum value of zoom range. + * + * @return @c EINA_TRUE if zoom range is changed, @c EINA_FALSE if not or failure. + */ +Eina_Bool ewk_view_zoom_range_set(Evas_Object* o, float min_scale, float max_scale) +{ + EWK_VIEW_SD_GET(o, sd); + EWK_VIEW_PRIV_GET(sd, priv); + + if (max_scale < min_scale) { + WRN("min_scale is larger than max_scale"); + return EINA_FALSE; + } + + priv->settings.zoom_range.min_scale = min_scale; + priv->settings.zoom_range.max_scale = max_scale; + + return EINA_TRUE; +} + +/** + * Gets the minimum value of zoom range. + * + * @param o view. + * + * @return minimum value of zoom range. + */ +float ewk_view_zoom_range_min_get(Evas_Object* o) +{ + EWK_VIEW_SD_GET(o, sd); + EWK_VIEW_PRIV_GET(sd, priv); + + return priv->settings.zoom_range.min_scale; +} + +/** + * Gets the maximum value of zoom range. + * + * @param o view. + * + * @return maximum value of zoom range. + */ +float ewk_view_zoom_range_max_get(Evas_Object* o) +{ + EWK_VIEW_SD_GET(o, sd); + EWK_VIEW_PRIV_GET(sd, priv); + + return priv->settings.zoom_range.max_scale; +} + +/** + * Sets if zoom is enabled. + * + * @param o view. + * @param user_scalable boolean pointer in which to enable zoom. It defaults + * to @c EINA_TRUE. + */ +void ewk_view_user_scalable_set(Evas_Object* o, Eina_Bool user_scalable) +{ + EWK_VIEW_SD_GET(o, sd); + EWK_VIEW_PRIV_GET(sd, priv); + + priv->settings.zoom_range.user_scalable = user_scalable; +} + +/** + * Gets if zoom is enabled. + * + * @param o view. + * @param user_scalable where to return the current user scalable value. + * + * @return @c EINA_TRUE if zoom is enabled, @c EINA_FALSE if not. + */ +Eina_Bool ewk_view_user_scalable_get(Evas_Object* o) +{ + EWK_VIEW_SD_GET(o, sd); + EWK_VIEW_PRIV_GET(sd, priv); + + return priv->settings.zoom_range.user_scalable; +}