The Webview Univeral Flutter package for Appeto SDK.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

235 lignes
9.1 KiB

  1. //
  2. // Created by boyan on 10/21/21.
  3. //
  4. #include "webview_window.h"
  5. #include <utility>
  6. #include "message_channel_plugin.h"
  7. namespace {
  8. gboolean on_load_failed_with_tls_errors(
  9. WebKitWebView *web_view,
  10. char *failing_uri,
  11. GTlsCertificate *certificate,
  12. GTlsCertificateFlags errors,
  13. gpointer user_data) {
  14. auto *webview = static_cast<WebviewWindow *>(user_data);
  15. g_critical("on_load_failed_with_tls_errors: %s %p error= %d", failing_uri, webview, errors);
  16. // TODO allow certificate for some certificate ?
  17. // maybe we can use the pem from https://source.chromium.org/chromium/chromium/src/+/master:net/data/ssl/ev_roots/
  18. // webkit_web_context_allow_tls_certificate_for_host(webkit_web_view_get_context(web_view), certificate, uri->host);
  19. // webkit_web_view_load_uri(web_view, failing_uri);
  20. return false;
  21. }
  22. GtkWidget *on_create(WebKitWebView *web_view,
  23. WebKitNavigationAction *navigation_action,
  24. gpointer user_data) {
  25. return GTK_WIDGET(web_view);
  26. }
  27. void on_load_changed(WebKitWebView *web_view,
  28. WebKitLoadEvent load_event,
  29. gpointer user_data) {
  30. auto *window = static_cast<WebviewWindow *>(user_data);
  31. window->OnLoadChanged(load_event);
  32. }
  33. gboolean decide_policy_cb(WebKitWebView *web_view,
  34. WebKitPolicyDecision *decision,
  35. WebKitPolicyDecisionType type,
  36. gpointer user_data) {
  37. auto *window = static_cast<WebviewWindow *>(user_data);
  38. return window->DecidePolicy(decision, type);
  39. }
  40. }
  41. WebviewWindow::WebviewWindow(
  42. FlMethodChannel *method_channel,
  43. int64_t window_id,
  44. std::function<void()> on_close_callback,
  45. const std::string &title,
  46. int width,
  47. int height,
  48. int title_bar_height
  49. ) : method_channel_(method_channel),
  50. window_id_(window_id),
  51. on_close_callback_(std::move(on_close_callback)),
  52. default_user_agent_() {
  53. g_object_ref(method_channel_);
  54. window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  55. g_signal_connect(G_OBJECT(window_), "destroy",
  56. G_CALLBACK(+[](GtkWidget *, gpointer arg) {
  57. auto *window = static_cast<WebviewWindow *>(arg);
  58. if (window->on_close_callback_) {
  59. window->on_close_callback_();
  60. }
  61. auto *args = fl_value_new_map();
  62. fl_value_set(args, fl_value_new_string("id"), fl_value_new_int(window->window_id_));
  63. fl_method_channel_invoke_method(
  64. FL_METHOD_CHANNEL(window->method_channel_), "onWindowClose", args,
  65. nullptr, nullptr, nullptr);
  66. }), this);
  67. gtk_window_set_title(GTK_WINDOW(window_), title.c_str());
  68. gtk_window_set_default_size(GTK_WINDOW(window_), width, height);
  69. gtk_window_set_position(GTK_WINDOW(window_), GTK_WIN_POS_CENTER);
  70. box_ = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
  71. gtk_container_add(GTK_CONTAINER(window_), GTK_WIDGET(box_));
  72. // initial flutter_view
  73. g_autoptr(FlDartProject) project = fl_dart_project_new();
  74. const char *args[] = {"web_view_title_bar", g_strdup_printf("%ld", window_id), nullptr};
  75. fl_dart_project_set_dart_entrypoint_arguments(project, const_cast<char **>(args));
  76. auto *title_bar = fl_view_new(project);
  77. g_autoptr(FlPluginRegistrar) webview_universal_registrar =
  78. fl_plugin_registry_get_registrar_for_plugin(FL_PLUGIN_REGISTRY(title_bar), "WebviewUniversalPlugin");
  79. client_message_channel_plugin_register_with_registrar(webview_universal_registrar);
  80. gtk_widget_set_size_request(GTK_WIDGET(title_bar), -1, title_bar_height);
  81. gtk_box_pack_start(box_, GTK_WIDGET(title_bar), FALSE, FALSE, 0);
  82. // initial web_view
  83. webview_ = webkit_web_view_new();
  84. g_signal_connect(G_OBJECT(webview_), "load-failed-with-tls-errors",
  85. G_CALLBACK(on_load_failed_with_tls_errors), this);
  86. g_signal_connect(G_OBJECT(webview_), "create",
  87. G_CALLBACK(on_create), this);
  88. g_signal_connect(G_OBJECT(webview_), "load-changed",
  89. G_CALLBACK(on_load_changed), this);
  90. g_signal_connect(G_OBJECT(webview_), "decide-policy",
  91. G_CALLBACK(decide_policy_cb), this);
  92. auto settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview_));
  93. webkit_settings_set_javascript_can_open_windows_automatically(settings, true);
  94. default_user_agent_ = webkit_settings_get_user_agent(settings);
  95. gtk_box_pack_start(box_, webview_, true, true, 0);
  96. gtk_widget_grab_focus(GTK_WIDGET(webview_));
  97. gtk_widget_show_all(window_);
  98. gtk_widget_queue_resize(GTK_WIDGET(title_bar));
  99. }
  100. WebviewWindow::~WebviewWindow() {
  101. g_object_unref(method_channel_);
  102. g_debug("~WebviewWindow");
  103. }
  104. void WebviewWindow::Navigate(const char *url) {
  105. webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview_), url);
  106. }
  107. void WebviewWindow::RunJavaScriptWhenContentReady(const char *java_script) {
  108. auto *manager = webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(webview_));
  109. webkit_user_content_manager_add_script(
  110. manager,
  111. webkit_user_script_new(java_script,
  112. WEBKIT_USER_CONTENT_INJECT_TOP_FRAME,
  113. WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START,
  114. nullptr,
  115. nullptr));
  116. }
  117. void WebviewWindow::SetApplicationNameForUserAgent(const std::string &app_name) {
  118. auto *setting = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview_));
  119. webkit_settings_set_user_agent(setting, (default_user_agent_ + app_name).c_str());
  120. }
  121. void WebviewWindow::Close() {
  122. gtk_window_close(GTK_WINDOW(window_));
  123. }
  124. void WebviewWindow::OnLoadChanged(WebKitLoadEvent load_event) {
  125. // notify history changed event.
  126. {
  127. auto can_go_back = webkit_web_view_can_go_back(WEBKIT_WEB_VIEW(webview_));
  128. auto can_go_forward = webkit_web_view_can_go_forward(WEBKIT_WEB_VIEW(webview_));
  129. auto *args = fl_value_new_map();
  130. fl_value_set(args, fl_value_new_string("id"), fl_value_new_int(window_id_));
  131. fl_value_set(args, fl_value_new_string("canGoBack"), fl_value_new_bool(can_go_back));
  132. fl_value_set(args, fl_value_new_string("canGoForward"), fl_value_new_bool(can_go_forward));
  133. fl_method_channel_invoke_method(
  134. FL_METHOD_CHANNEL(method_channel_), "onHistoryChanged", args,
  135. nullptr, nullptr, nullptr);
  136. }
  137. // notify load start/finished event.
  138. switch (load_event) {
  139. case WEBKIT_LOAD_STARTED: {
  140. auto *args = fl_value_new_map();
  141. fl_value_set(args, fl_value_new_string("id"), fl_value_new_int(window_id_));
  142. fl_method_channel_invoke_method(
  143. FL_METHOD_CHANNEL(method_channel_), "onNavigationStarted", args,
  144. nullptr, nullptr, nullptr);
  145. break;
  146. }
  147. case WEBKIT_LOAD_FINISHED: {
  148. auto *args = fl_value_new_map();
  149. fl_value_set(args, fl_value_new_string("id"), fl_value_new_int(window_id_));
  150. fl_method_channel_invoke_method(
  151. FL_METHOD_CHANNEL(method_channel_), "onNavigationCompleted", args,
  152. nullptr, nullptr, nullptr);
  153. break;
  154. }
  155. default :break;
  156. }
  157. }
  158. void WebviewWindow::GoForward() {
  159. webkit_web_view_go_forward(WEBKIT_WEB_VIEW(webview_));
  160. }
  161. void WebviewWindow::GoBack() {
  162. webkit_web_view_go_back(WEBKIT_WEB_VIEW(webview_));
  163. }
  164. void WebviewWindow::Reload() {
  165. webkit_web_view_reload(WEBKIT_WEB_VIEW(webview_));
  166. }
  167. void WebviewWindow::StopLoading() {
  168. webkit_web_view_stop_loading(WEBKIT_WEB_VIEW(webview_));
  169. }
  170. gboolean WebviewWindow::DecidePolicy(WebKitPolicyDecision *decision, WebKitPolicyDecisionType type) {
  171. if (type == WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION) {
  172. auto *navigation_decision = WEBKIT_NAVIGATION_POLICY_DECISION (decision);
  173. auto *navigation_action = webkit_navigation_policy_decision_get_navigation_action(navigation_decision);
  174. auto *request = webkit_navigation_action_get_request(navigation_action);
  175. auto *uri = webkit_uri_request_get_uri(request);
  176. auto *args = fl_value_new_map();
  177. fl_value_set(args, fl_value_new_string("id"), fl_value_new_int(window_id_));
  178. fl_value_set(args, fl_value_new_string("url"), fl_value_new_string(uri));
  179. fl_method_channel_invoke_method(
  180. FL_METHOD_CHANNEL(method_channel_), "onUrlRequested", args,
  181. nullptr, nullptr, nullptr);
  182. }
  183. return false;
  184. }
  185. void WebviewWindow::EvaluateJavaScript(const char *java_script, FlMethodCall *call) {
  186. webkit_web_view_run_javascript(
  187. WEBKIT_WEB_VIEW(webview_), java_script, nullptr,
  188. [](GObject *object, GAsyncResult *result, gpointer user_data) {
  189. auto *call = static_cast<FlMethodCall *>(user_data);
  190. GError *error = nullptr;
  191. auto *js_result = webkit_web_view_run_javascript_finish(WEBKIT_WEB_VIEW(object), result, &error);
  192. if (!js_result) {
  193. fl_method_call_respond_error(call, "failed to evaluate javascript.", error->message, nullptr, nullptr);
  194. g_error_free(error);
  195. } else {
  196. auto *js_value = jsc_value_to_json(webkit_javascript_result_get_js_value(js_result), 0);
  197. fl_method_call_respond_success(call, js_value ? fl_value_new_string(js_value) : nullptr, nullptr);
  198. }
  199. g_object_unref(call);
  200. },
  201. g_object_ref(call));
  202. }