/*
* 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); };
}
}
}