#include "include/webview_universal/webview_universal_plugin.h" #include #include #include #include #include #include #include "webview_window.h" #include "message_channel_plugin.h" namespace { int64_t next_window_id_ = 0; } #define WEBVIEW_WINDOW_PLUGIN(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), webview_window_plugin_get_type(), \ WebviewWindowPlugin)) struct _WebviewWindowPlugin { GObject parent_instance; FlMethodChannel *method_channel; std::map> *windows; }; G_DEFINE_TYPE(WebviewWindowPlugin, webview_window_plugin, g_object_get_type()) // Called when a method call is received from Flutter. static void webview_window_plugin_handle_method_call( WebviewWindowPlugin *self, FlMethodCall *method_call) { const gchar *method = fl_method_call_get_name(method_call); if (strcmp(method, "create") == 0) { auto *args = fl_method_call_get_args(method_call); if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { fl_method_call_respond_error(method_call, "0", "create args is not map", nullptr, nullptr); return; } auto width = fl_value_get_int(fl_value_lookup_string(args, "windowWidth")); auto height = fl_value_get_int(fl_value_lookup_string(args, "windowHeight")); auto title = fl_value_get_string(fl_value_lookup_string(args, "title")); auto title_bar_height = fl_value_get_int(fl_value_lookup_string(args, "titleBarHeight")); auto window_id = next_window_id_; g_object_ref(self); auto webview = std::make_unique( self->method_channel, window_id, [self, window_id]() { self->windows->erase(window_id); g_object_unref(self); }, title, width, height, title_bar_height); self->windows->insert({window_id, std::move(webview)}); next_window_id_++; fl_method_call_respond_success(method_call, fl_value_new_int(window_id), nullptr); } else if (strcmp(method, "launch") == 0) { auto *args = fl_method_call_get_args(method_call); if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { fl_method_call_respond_error(method_call, "0", "create args is not map", nullptr, nullptr); return; } auto window_id = fl_value_get_int(fl_value_lookup_string(args, "viewId")); auto url = fl_value_get_string(fl_value_lookup_string(args, "url")); if (!self->windows->count(window_id)) { fl_method_call_respond_error(method_call, "0", "can not found webview for viewId", nullptr, nullptr); return; } self->windows->at(window_id)->Navigate(url); fl_method_call_respond_success(method_call, nullptr, nullptr); } else if (strcmp(method, "addScriptToExecuteOnDocumentCreated") == 0) { auto *args = fl_method_call_get_args(method_call); if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { fl_method_call_respond_error(method_call, "0", "args is not map", nullptr, nullptr); return; } auto window_id = fl_value_get_int(fl_value_lookup_string(args, "viewId")); auto java_script = fl_value_get_string(fl_value_lookup_string(args, "javaScript")); if (!self->windows->count(window_id)) { fl_method_call_respond_error(method_call, "0", "can not found webview for viewId", nullptr, nullptr); return; } self->windows->at(window_id)->RunJavaScriptWhenContentReady(java_script); fl_method_call_respond_success(method_call, nullptr, nullptr); } else if (strcmp(method, "clearAll") == 0) { for (const auto &item: *self->windows) { item.second->Close(); } // If application didn't create a webview, but we called webkit_website_data_manager_clear, there will be a segment fault. // To avoid crash, we create a fake webview first and then clear all data. auto *web_view = webkit_web_view_new(); auto *context = webkit_web_view_get_context(WEBKIT_WEB_VIEW(web_view)); auto *website_data_manager = webkit_web_context_get_website_data_manager(context); webkit_website_data_manager_clear(website_data_manager, WEBKIT_WEBSITE_DATA_ALL, 0, nullptr, nullptr, nullptr); fl_method_call_respond_success(method_call, nullptr, nullptr); } else if (strcmp(method, "setApplicationNameForUserAgent") == 0) { auto *args = fl_method_call_get_args(method_call); if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { fl_method_call_respond_error(method_call, "0", "setApplicationNameForUserAgent args is not map", nullptr, nullptr); return; } auto window_id = fl_value_get_int(fl_value_lookup_string(args, "viewId")); auto application_name = fl_value_get_string(fl_value_lookup_string(args, "applicationName")); if (!self->windows->count(window_id)) { fl_method_call_respond_error(method_call, "0", "can not found webview for viewId", nullptr, nullptr); return; } self->windows->at(window_id)->SetApplicationNameForUserAgent(application_name); fl_method_call_respond_success(method_call, nullptr, nullptr); } else if (strcmp(method, "back") == 0) { auto *args = fl_method_call_get_args(method_call); if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { fl_method_call_respond_error(method_call, "0", "back args is not map", nullptr, nullptr); return; } auto window_id = fl_value_get_int(fl_value_lookup_string(args, "viewId")); if (!self->windows->count(window_id)) { fl_method_call_respond_error(method_call, "0", "can not found webview for viewId", nullptr, nullptr); return; } self->windows->at(window_id)->GoBack(); fl_method_call_respond_success(method_call, nullptr, nullptr); } else if (strcmp(method, "forward") == 0) { auto *args = fl_method_call_get_args(method_call); if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { fl_method_call_respond_error(method_call, "0", "forward args is not map", nullptr, nullptr); return; } auto window_id = fl_value_get_int(fl_value_lookup_string(args, "viewId")); if (!self->windows->count(window_id)) { fl_method_call_respond_error(method_call, "0", "can not found webview for viewId", nullptr, nullptr); return; } self->windows->at(window_id)->GoForward(); fl_method_call_respond_success(method_call, nullptr, nullptr); } else if (strcmp(method, "reload") == 0) { auto *args = fl_method_call_get_args(method_call); if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { fl_method_call_respond_error(method_call, "0", "reload args is not map", nullptr, nullptr); return; } auto window_id = fl_value_get_int(fl_value_lookup_string(args, "viewId")); if (!self->windows->count(window_id)) { fl_method_call_respond_error(method_call, "0", "can not found webview for viewId", nullptr, nullptr); return; } self->windows->at(window_id)->Reload(); fl_method_call_respond_success(method_call, nullptr, nullptr); } else if (strcmp(method, "stop") == 0) { auto *args = fl_method_call_get_args(method_call); if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { fl_method_call_respond_error(method_call, "0", "stop args is not map", nullptr, nullptr); return; } auto window_id = fl_value_get_int(fl_value_lookup_string(args, "viewId")); if (!self->windows->count(window_id)) { fl_method_call_respond_error(method_call, "0", "can not found webview for viewId", nullptr, nullptr); return; } self->windows->at(window_id)->StopLoading(); fl_method_call_respond_success(method_call, nullptr, nullptr); } else if (strcmp(method, "close") == 0) { auto *args = fl_method_call_get_args(method_call); if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { fl_method_call_respond_error(method_call, "0", "close args is not map", nullptr, nullptr); return; } auto window_id = fl_value_get_int(fl_value_lookup_string(args, "viewId")); if (!self->windows->count(window_id)) { fl_method_call_respond_error(method_call, "0", "can not found webview for viewId", nullptr, nullptr); return; } self->windows->at(window_id)->Close(); fl_method_call_respond_success(method_call, nullptr, nullptr); } else if (strcmp(method, "evaluateJavaScript") == 0) { auto *args = fl_method_call_get_args(method_call); if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { fl_method_call_respond_error(method_call, "0", "evaluateJavaScript args is not map", nullptr, nullptr); return; } auto window_id = fl_value_get_int(fl_value_lookup_string(args, "viewId")); if (!self->windows->count(window_id)) { fl_method_call_respond_error(method_call, "0", "can not found webview for viewId", nullptr, nullptr); return; } auto *js = fl_value_get_string(fl_value_lookup_string(args, "javaScriptString")); self->windows->at(window_id)->EvaluateJavaScript(js, method_call); } else { fl_method_call_respond_not_implemented(method_call, nullptr); } } static void webview_window_plugin_dispose(GObject *object) { delete WEBVIEW_WINDOW_PLUGIN(object)->windows; g_object_unref(WEBVIEW_WINDOW_PLUGIN(object)->method_channel); G_OBJECT_CLASS(webview_window_plugin_parent_class)->dispose(object); } static void webview_window_plugin_class_init(WebviewWindowPluginClass *klass) { G_OBJECT_CLASS(klass)->dispose = webview_window_plugin_dispose; } static void webview_window_plugin_init(WebviewWindowPlugin *self) { self->windows = new std::map>(); } static void method_call_cb(FlMethodChannel *channel, FlMethodCall *method_call, gpointer user_data) { WebviewWindowPlugin *plugin = WEBVIEW_WINDOW_PLUGIN(user_data); webview_window_plugin_handle_method_call(plugin, method_call); } void webview_universal_plugin_register_with_registrar(FlPluginRegistrar *registrar) { client_message_channel_plugin_register_with_registrar(registrar); WebviewWindowPlugin *plugin = WEBVIEW_WINDOW_PLUGIN( g_object_new(webview_window_plugin_get_type(), nullptr)); g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); g_autoptr(FlMethodChannel) channel = fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar), "webview_window", FL_METHOD_CODEC(codec)); g_object_ref(channel); plugin->method_channel = channel; fl_method_channel_set_method_call_handler(channel, method_call_cb, g_object_ref(plugin), g_object_unref); g_object_unref(plugin); }