/* * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using ElmSharp; using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.ComponentModel; namespace Tizen.WebView { /// /// Enumeration values used to specify search options. /// /// 6 [Flags] public enum FindOption { /// /// No search flags, this means a case sensitive, no wrap, forward only search. /// None = 0, /// /// Case insensitive search. /// CaseInsensitive = 1 << 0, /// /// Search text only at the beginning of the words. /// AtWordStart = 1 << 1, /// /// Treat capital letters in the middle of words as word start. /// TreatMedialCapitalAsWordStart = 1 << 2, /// /// Search backwards. /// Backwards = 1 << 3, /// /// If not present the search stops at the end of the document. /// WrapAround = 1 << 4, /// /// Show overlay. /// ShowOverlay = 1 << 5, /// /// Show Indicator. /// ShowIndicator = 1 << 6, /// /// Show Highlight. /// ShowHighlight = 1 << 7, } /// /// Enumeration for Http Method. /// /// 6 public enum HttpMethod { /// /// Get. /// Get, /// /// Head. /// Head, /// /// Post. /// Post, /// /// Put. /// Put, /// /// Delete. /// Delete, } /// /// Enumeration for Orientation of the device. /// /// 6 public enum Orientation { /// /// 0 degrees when the device is oriented to natural position. /// Natural = 0, /// /// -90 degrees when it's left side is at the top. /// LeftAtTop = -90, /// /// 90 degrees when it's right side is at the top. /// RightAtTop = 90, /// /// 180 degrees when it is upside down. /// UpsideDown = 180, } /// /// A view used to render the web contents. /// /// 4 public class WebView : EvasObject { private static IDictionary _javaScriptMessageHandlerMap = new Dictionary(); private IntPtr _handle; private IntPtr _realHandle; private Context _context; private Settings _settings; // focus dummy private SmartEvent _focusIn; private SmartEvent _focusOut; // Smart events private SmartEvent _loadStarted; private SmartEvent _loadFinished; private SmartEvent _loadError; private SmartEvent _titleChanged; private SmartEvent _urlChanged; private SmartEvent _policyNavigationDecide; private SmartEvent _policyNewWindowDecide; private SmartEvent _policyResponseDecide; private SmartEvent _contextMenuItemSelected; private SmartEvent _contextMenuCustomize; private ContextMenuCustomize _contextMenuCustomizeDelegate; /// /// Event that occurs when the load is started. /// /// 4 public event EventHandler LoadStarted; /// /// Event that occurs when the load is finished. /// /// 4 public event EventHandler LoadFinished; /// /// Event that occurs when the load throws an error. /// /// 4 public event EventHandler LoadError; /// /// Event that occurs when the title of the main frame is changed. /// /// 4 public event EventHandler TitleChanged; /// /// Event that occurs when the URL of the main frame is changed. /// /// 4 public event EventHandler UrlChanged; /// /// Event that occurs when the policy navigation is decided. /// /// 6 public event EventHandler NavigationPolicyDecideRequested; /// /// Event that occurs when the policy new window is decided. /// /// 6 public event EventHandler NewWindowPolicyDecideRequested; /// /// Event that occurs when the policy response is decided. /// /// 6 public event EventHandler ResponsePolicyDecideRequested; /// /// Event that occurs when the context menu item selected. /// /// 6 public event EventHandler ContextMenuItemSelected; /// The delegate is invoked when context menu customization is needed. /// /// The instance of ContextMenu. /// 6 public delegate void ContextMenuCustomize(ContextMenu menu); /// /// Current URL of the main frame. /// /// 4 public string Url { get { return Interop.ChromiumEwk.ewk_view_url_get(_realHandle); } } /// /// Current title of the main frame. /// /// 4 public string Title { get { return Interop.ChromiumEwk.ewk_view_title_get(_realHandle); } } /// /// Current user agent string of this view. /// /// 4 public string UserAgent { get { return Interop.ChromiumEwk.ewk_view_user_agent_get(_realHandle); } set { Interop.ChromiumEwk.ewk_view_user_agent_set(_realHandle, value); } } /// /// Whether a view has the focus. /// /// 4 public bool HasFocus { get { return Interop.ChromiumEwk.ewk_view_focus_get(_realHandle); } } /// /// Creates a WebView object. /// /// Parent object of the WebView. /// 4 public WebView(EvasObject parent) : base(parent) { InitializeSmartEvent(); } /// /// Gets the context object of this view. /// /// The context object of this view. /// 4 public Context GetContext() { if (_context == null) { IntPtr contextHandle = Interop.ChromiumEwk.ewk_view_context_get(_realHandle); if (contextHandle == IntPtr.Zero) { return null; } _context = new Context(contextHandle); } return _context; } /// /// Gets the settings object of this view. /// /// The settings object of this view. /// 4 public Settings GetSettings() { if (_settings == null) { IntPtr settingsHandle = Interop.ChromiumEwk.ewk_view_settings_get(_realHandle); if (settingsHandle == IntPtr.Zero) { return null; } _settings = new Settings(settingsHandle); } return _settings; } /// /// Gets the back/forward list object of this view. /// /// The BackForward List object of this view. /// 6 public BackForwardList GetBackForwardList() { IntPtr backforwardlistHandle = Interop.ChromiumEwk.ewk_view_back_forward_list_get(_realHandle); if (backforwardlistHandle == IntPtr.Zero) { return null; } return new BackForwardList(backforwardlistHandle); } /// /// Clear the back/forward list object of this view. /// /// 6 public void ClearBackForwardList() { Interop.ChromiumEwk.ewk_view_back_forward_list_clear(_realHandle); } /// /// Asks the object to load the given URL. /// /// /// You can only be sure that the URL changes after UrlChanged event. /// /// The uniform resource identifier to load. /// 4 public void LoadUrl(string url) { Interop.ChromiumEwk.ewk_view_url_set(_realHandle, url); } /// /// Loads the specified HTML string as the content of the view. /// /// HTML data to load. /// Base URL used for relative paths to external objects. /// 4 public void LoadHtml(string html, string baseUrl) { Interop.ChromiumEwk.ewk_view_html_string_load(_realHandle, html, baseUrl, null); } /// /// Asks the main frame to stop loading. /// /// 4 public void StopLoading() { Interop.ChromiumEwk.ewk_view_stop(_realHandle); } /// /// Asks the main frame to reload the current document. /// /// 4 public void Reload() { Interop.ChromiumEwk.ewk_view_reload(_realHandle); } /// /// Asks the main frame to navigate back in history. /// /// 4 public void GoBack() { Interop.ChromiumEwk.ewk_view_back(_realHandle); } /// /// Asks the main frame to navigate forward in history. /// /// 4 public void GoForward() { Interop.ChromiumEwk.ewk_view_forward(_realHandle); } /// /// Checks whether it is possible to navigate backward one item in history. /// /// Whether it is possible to navigate backward one item in history. /// 4 public bool CanGoBack() { return Interop.ChromiumEwk.ewk_view_back_possible(_realHandle); } /// /// Checks whether it is possible to navigate forward one item in history. /// /// Whether it is possible to navigate forward one item in history. /// 4 public bool CanGoForward() { return Interop.ChromiumEwk.ewk_view_forward_possible(_realHandle); } /// /// Injects the supplied javascript message handler into the view. /// /// The message callback. /// The name used to expose the object in JavaScript. /// 'true' on success, otherwise 'false'. /// 4 public bool AddJavaScriptMessageHandler(string name, JavaScriptMessageHandler handler) { lock (_javaScriptMessageHandlerMap) { if (_javaScriptMessageHandlerMap.ContainsKey(name)) { return false; } _javaScriptMessageHandlerMap[name] = handler; } Interop.ChromiumEwk.ScriptMessageCallback callback = (handle, message) => { JavaScriptMessage convertedMessage = new JavaScriptMessage(message); lock (_javaScriptMessageHandlerMap) { if (_javaScriptMessageHandlerMap.ContainsKey(convertedMessage.Name)) { _javaScriptMessageHandlerMap[convertedMessage.Name](convertedMessage); } } }; if (!Interop.ChromiumEwk.ewk_view_javascript_message_handler_add(_realHandle, callback, name)) { lock (_javaScriptMessageHandlerMap) { _javaScriptMessageHandlerMap.Remove(name); return false; } } return true; } /// /// Requests the execution of a given name and the result to the JavaScript runtime. /// /// The name used to expose the object in JavaScript. /// The result to the JavaScript runtime. /// 4 public void EvalWithResult(string name, string result) { Interop.ChromiumEwk.ewk_view_evaluate_javascript(_realHandle, name, result); } /// /// Requests the execution of the given script. /// /// The JavaScript code string to execute. /// 4 public void Eval(string script) { Interop.ChromiumEwk.ewk_view_script_execute(_realHandle, script, null, IntPtr.Zero); } /// /// Requests to set or unset a view as the currently focused one. /// /// 'true' to set the focus on the view, 'false' to remove the focus from the view. /// 4 public void SetFocus(bool focused) { Interop.ChromiumEwk.ewk_view_focus_set(_realHandle, focused); } /// /// Gets size of the content. /// /// size of the coordinate. /// 6 public Size ContentsSize { get { int width, height; Interop.ChromiumEwk.ewk_view_contents_size_get(_realHandle, out width, out height); return new Size(width, height); } } /// /// Exit full screen. /// /// 6 public void ExitFullscreen () { Interop.ChromiumEwk.ewk_view_fullscreen_exit(_realHandle); } /// /// Gets the current load progress of the page. /// /// 'value 0.0 to 1.0' on success, otherwise '-1.0'. /// 6 public double LoadProgress { get { return Interop.ChromiumEwk.ewk_view_load_progress_get(_realHandle); } } /// /// Sends the orientation of the device. /// /// The new orientation of the device in degree. /// 6 public void SendOrientation (Orientation orientation) { Interop.ChromiumEwk.ewk_view_orientation_send(_realHandle, orientation); } /// /// Suspends the operation associated with the view. /// /// 6 public void Suspend () { Interop.ChromiumEwk.ewk_view_suspend(_realHandle); } /// /// Resumes the operation associated with the view. /// /// 6 public void Resume () { Interop.ChromiumEwk.ewk_view_resume(_realHandle); } /// /// Gets the current scale factor of the page. /// /// 6 public double Scale { get { return Interop.ChromiumEwk.ewk_view_scale_get(_realHandle); } } /// /// Sets the current scale factor of the page. /// /// A new level to set. /// The class Point object with X, Y coordinates. /// 6 public void SetScale (double scaleFactor, Point scrollTo) { Interop.ChromiumEwk.ewk_view_scale_set(_realHandle, scaleFactor, scrollTo.X, scrollTo.Y); } /// /// Sets the current page's visibility. /// /// 'true' to set on the visibility of the page, 'false' otherwise. /// 6 public void SetViewVisibility (bool enable) { Interop.ChromiumEwk.ewk_view_visibility_set(_realHandle, enable); } /// /// Get and Sets the scroll position of the page. /// /// The class Point object with X, Y coordinates. /// 6 public Point ScrollPosition { get { Point p; Interop.ChromiumEwk.ewk_view_scroll_pos_get(_realHandle, out p.X, out p.Y); return p; } set { Interop.ChromiumEwk.ewk_view_scroll_set(_realHandle, value.X, value.Y); } } /// /// Scrolls the webpage by the given amount. /// /// The class Point object with X, Y coordinates. /// 6 public void ScrollBy (Point delta) { Interop.ChromiumEwk.ewk_view_scroll_by(_realHandle, delta.X, delta.Y); } /// /// Searches and highlights the given text string in the document. /// /// The text to find. /// The options to find. /// The maximum match count to find, unlimited if 0. /// 6 public void FindText (string text, FindOption option, int maxMatchCount) { Interop.ChromiumEwk.ewk_view_text_find(_realHandle, text, option, maxMatchCount); } /// /// Requests loading of the given request data. /// /// The uniform resource identifier to load. /// The http method. /// The http headers. /// The http body data. /// 6 public void SetUrlRequest (string url, HttpMethod httpMethod, IDictionary httpHeaders, string httpBody) { IntPtr hashHttpHeaders = Interop.Eina.eina_hash_string_small_new(); foreach (KeyValuePair entry in httpHeaders) { Interop.Eina.eina_hash_add(hashHttpHeaders, entry.Key, entry.Value); } Interop.ChromiumEwk.ewk_view_url_request_set(_realHandle, url, httpMethod, hashHttpHeaders, httpBody); } /// /// Creates a widget handle. /// /// Parent EvasObject. /// IntPtr of the widget handle. /// 4 protected override IntPtr CreateHandle(EvasObject parent) { // focus dummy _handle = Interop.Elementary.elm_layout_add((IntPtr)parent); Interop.Elementary.elm_layout_theme_set(_handle, "layout", "elm_widget", "default"); Interop.Elementary.elm_object_focus_allow_set(_handle, true); IntPtr evas = Interop.Evas.evas_object_evas_get(parent); _realHandle = Interop.ChromiumEwk.ewk_view_add(evas); Interop.Elementary.elm_object_part_content_set(_handle, "elm.swallow.content", _realHandle); return _handle; } /// /// Sets the delegate for context menu customization. /// /// The delegate for context menu customization. /// 6 public void SetContextMenuCustomizeDelegate(ContextMenuCustomize contextMenuCustomizeDelegate) { _contextMenuCustomizeDelegate = contextMenuCustomizeDelegate; } private void InitializeSmartEvent() { // focus dummy _focusIn = new SmartEvent(this, "focused"); _focusOut = new SmartEvent(this, "unfocused"); _focusIn.On += (s, e) => { ((WebView)s).SetFocus(true); }; _focusOut.On += (s, e) => { ((WebView)s).SetFocus(false); }; _loadStarted = new SmartEvent(this, _realHandle, "load,started"); _loadFinished = new SmartEvent(this, _realHandle, "load,finished"); _loadError = new SmartEvent(this, _realHandle, "load,error", SmartCallbackLoadErrorArgs.CreateFromSmartEvent); _titleChanged = new SmartEvent(this, _realHandle, "title,changed", SmartCallbackArgs.CreateFromSmartEvent); _urlChanged = new SmartEvent(this, _realHandle, "url,changed", SmartCallbackArgs.CreateFromSmartEvent); _contextMenuCustomize = new SmartEvent(this, _realHandle, "contextmenu,customize", ContextMenuCustomizeEventArgs.CreateFromSmartEvent); _contextMenuItemSelected = new SmartEvent(this, _realHandle, "contextmenu,selected", ContextMenuItemEventArgs.CreateFromSmartEvent); _policyNavigationDecide = new SmartEvent(this, _realHandle, "policy,navigation,decide", NavigationPolicyEventArgs.CreateFromSmartEvent); _policyNewWindowDecide = new SmartEvent(this, _realHandle, "policy,newwindow,decide", NewWindowPolicyEventArgs.CreateFromSmartEvent); _policyResponseDecide = new SmartEvent(this, _realHandle, "policy,response,decide", ResponsePolicyEventArgs.CreateFromSmartEvent); _loadStarted.On += (s, e) => { LoadStarted?.Invoke(this, EventArgs.Empty); }; _loadFinished.On += (s, e) => { LoadFinished?.Invoke(this, EventArgs.Empty); }; _loadError.On += (s, e) => { LoadError?.Invoke(this, e); }; _titleChanged.On += (s, e) => { TitleChanged?.Invoke(this, e); }; _urlChanged.On += (s, e) => { UrlChanged?.Invoke(this, e); }; _policyNavigationDecide.On += (s, e) => { NavigationPolicyDecideRequested?.Invoke(this, e); }; _policyNewWindowDecide.On += (s, e) => { NewWindowPolicyDecideRequested?.Invoke(this, e); }; _policyResponseDecide.On += (s, e) => { ResponsePolicyDecideRequested?.Invoke(this, e); }; _contextMenuItemSelected.On += (s, e) => { ContextMenuItemSelected?.Invoke(this, e); }; _contextMenuCustomize.On += (s, e) => { _contextMenuCustomizeDelegate?.Invoke(e.Menu); }; } } }