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.
 
 
 
 
 
 

336 lignes
11 KiB

  1. //
  2. // Created by yangbin on 2021/11/12.
  3. //
  4. #include <windows.h>
  5. #include <tchar.h>
  6. #include <cassert>
  7. #include <iostream>
  8. #include <utility>
  9. #include <thread>
  10. #include "web_view.h"
  11. #include "utils.h"
  12. #include "strconv.h"
  13. namespace webview_window {
  14. static LRESULT CALLBACK WndProc(HWND const window,
  15. UINT const message,
  16. WPARAM const wparam,
  17. LPARAM const lparam) noexcept {
  18. return DefWindowProc(window, message, wparam, lparam);
  19. }
  20. const auto kWebViewClassName = _T("web_view_window_web_view");
  21. using namespace Microsoft::WRL;
  22. WebView::WebView(
  23. std::shared_ptr<flutter::MethodChannel<flutter::EncodableValue>> method_channel,
  24. int64_t web_view_id, std::wstring userDataFolder,
  25. std::function<void(HRESULT)> on_web_view_created
  26. ) : method_channel_(std::move(method_channel)),
  27. web_view_id_(web_view_id), user_data_folder_(std::move(userDataFolder)),
  28. on_web_view_created_callback_(std::move(on_web_view_created)) {
  29. RegisterWindowClass(kWebViewClassName, WndProc);
  30. view_window_ = wil::unique_hwnd(::CreateWindowEx(
  31. 0,
  32. kWebViewClassName,
  33. L"",
  34. WS_CHILD | WS_VISIBLE,
  35. 0,
  36. 0,
  37. 0,
  38. 0,
  39. HWND_MESSAGE,
  40. nullptr,
  41. ::GetModuleHandle(nullptr),
  42. nullptr));
  43. assert(view_window_ != nullptr);
  44. if (!view_window_) {
  45. on_web_view_created_callback_(S_FALSE);
  46. return;
  47. }
  48. CreateCoreWebView2EnvironmentWithOptions(
  49. nullptr, user_data_folder_.c_str(), nullptr,
  50. Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
  51. [this](HRESULT result, ICoreWebView2Environment *env) -> HRESULT {
  52. if (!SUCCEEDED(result)) {
  53. on_web_view_created_callback_(result);
  54. return S_OK;
  55. }
  56. env->CreateCoreWebView2Controller(
  57. view_window_.get(),
  58. Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
  59. [this](HRESULT result, ICoreWebView2Controller *controller) -> HRESULT {
  60. on_web_view_created_callback_(result);
  61. if (SUCCEEDED(result)) {
  62. webview_controller_ = controller;
  63. OnWebviewControllerCreated();
  64. }
  65. return S_OK;
  66. }).Get());
  67. return S_OK;
  68. }).Get());
  69. }
  70. void WebView::OnWebviewControllerCreated() {
  71. if (!webview_controller_) {
  72. return;
  73. }
  74. webview_controller_->get_CoreWebView2(&webview_);
  75. if (!webview_) {
  76. std::cerr << "failed to get core webview" << std::endl;
  77. return;
  78. }
  79. ICoreWebView2Settings *settings;
  80. webview_->get_Settings(&settings);
  81. settings->put_IsScriptEnabled(true);
  82. settings->put_IsZoomControlEnabled(false);
  83. settings->put_AreDefaultContextMenusEnabled(false);
  84. settings->put_IsStatusBarEnabled(false);
  85. settings->put_IsWebMessageEnabled(true);
  86. ICoreWebView2Settings2 *settings2;
  87. auto hr = settings->QueryInterface(IID_PPV_ARGS(&settings2));
  88. if (SUCCEEDED(hr)) {
  89. LPWSTR user_agent[256];
  90. settings2->get_UserAgent(user_agent);
  91. default_user_agent_ = std::wstring(*user_agent);
  92. }
  93. UpdateBounds();
  94. // Always use single window to load web page.
  95. webview_->add_NewWindowRequested(
  96. Callback<ICoreWebView2NewWindowRequestedEventHandler>(
  97. [](ICoreWebView2 *sender, ICoreWebView2NewWindowRequestedEventArgs *args) {
  98. LPWSTR url;
  99. args->get_Uri(&url);
  100. sender->Navigate(url);
  101. args->put_Handled(true);
  102. return S_OK;
  103. }).Get(), nullptr);
  104. webview_->add_ContentLoading(
  105. Callback<ICoreWebView2ContentLoadingEventHandler>(
  106. [](ICoreWebView2 *sender, ICoreWebView2ContentLoadingEventArgs *args) {
  107. return S_OK;
  108. }).Get(), nullptr);
  109. webview_->add_HistoryChanged(
  110. Callback<ICoreWebView2HistoryChangedEventHandler>(
  111. [this](ICoreWebView2 *sender, IUnknown *args) {
  112. auto method_args = flutter::EncodableMap{
  113. {flutter::EncodableValue("id"), flutter::EncodableValue(web_view_id_)},
  114. {flutter::EncodableValue("canGoBack"), flutter::EncodableValue(CanGoBack())},
  115. {flutter::EncodableValue("canGoForward"), flutter::EncodableValue(CanGoForward())},
  116. };
  117. method_channel_->InvokeMethod("onHistoryChanged",
  118. std::make_unique<flutter::EncodableValue>(method_args));
  119. return S_OK;
  120. }
  121. ).Get(), nullptr);
  122. webview_->add_NavigationStarting(
  123. Callback<ICoreWebView2NavigationStartingEventHandler>(
  124. [this](ICoreWebView2 *sender, ICoreWebView2NavigationStartingEventArgs *args) {
  125. method_channel_->InvokeMethod(
  126. "onNavigationStarted",
  127. std::make_unique<flutter::EncodableValue>(flutter::EncodableMap{
  128. {flutter::EncodableValue("id"), flutter::EncodableValue(web_view_id_)},
  129. }));
  130. LPWSTR uri;
  131. args->get_Uri(&uri);
  132. method_channel_->InvokeMethod(
  133. "onUrlRequested",
  134. std::make_unique<flutter::EncodableValue>(flutter::EncodableMap{
  135. {flutter::EncodableValue("id"), flutter::EncodableValue(web_view_id_)},
  136. {flutter::EncodableValue("url"), flutter::EncodableValue(wide_to_utf8(std::wstring(uri)))},
  137. }));
  138. return S_OK;
  139. }
  140. ).Get(), nullptr);
  141. webview_->add_NavigationCompleted(
  142. Callback<ICoreWebView2NavigationCompletedEventHandler>(
  143. [this](ICoreWebView2 *sender, ICoreWebView2NavigationCompletedEventArgs *args) {
  144. auto method_args = flutter::EncodableMap{
  145. {flutter::EncodableValue("id"), flutter::EncodableValue(web_view_id_)},
  146. };
  147. method_channel_->InvokeMethod("onNavigationCompleted",
  148. std::make_unique<flutter::EncodableValue>(method_args));
  149. return S_OK;
  150. }
  151. ).Get(), nullptr);
  152. webview_->add_WebMessageReceived(
  153. Callback<ICoreWebView2WebMessageReceivedEventHandler>(
  154. [this](ICoreWebView2 *sender, ICoreWebView2WebMessageReceivedEventArgs *args) {
  155. wil::unique_cotaskmem_string messageRaw;
  156. HRESULT hrString = args->TryGetWebMessageAsString(&messageRaw);
  157. if (FAILED(hrString)) {
  158. if (hrString == E_INVALIDARG) {
  159. // web message was not a string --> should only happen if it was a JSON object
  160. HRESULT hrJson = args->get_WebMessageAsJson(&messageRaw);
  161. if (FAILED(hrJson)) {
  162. return hrJson;
  163. }
  164. } else {
  165. return hrString;
  166. }
  167. }
  168. method_channel_->InvokeMethod(
  169. "onWebMessageReceived",
  170. std::make_unique<flutter::EncodableValue>(flutter::EncodableMap{
  171. {flutter::EncodableValue("id"), flutter::EncodableValue(web_view_id_)},
  172. {flutter::EncodableValue("message"), flutter::EncodableValue(wide_to_utf8(std::wstring(messageRaw.get())))},
  173. }));
  174. return S_OK;
  175. }
  176. ).Get(), nullptr);
  177. }
  178. void WebView::UpdateBounds() {
  179. // Resize WebView to fit the bounds of the parent window
  180. RECT bounds;
  181. GetClientRect(view_window_.get(), &bounds);
  182. webview_controller_->put_Bounds(bounds);
  183. }
  184. void WebView::Navigate(const std::wstring &url) {
  185. if (webview_) {
  186. webview_->Navigate(url.c_str());
  187. } else {
  188. std::cerr << "webview not created" << std::endl;
  189. }
  190. }
  191. void WebView::AddScriptToExecuteOnDocumentCreated(const std::wstring &javaScript) {
  192. if (webview_) {
  193. webview_->AddScriptToExecuteOnDocumentCreated(javaScript.c_str(), nullptr);
  194. }
  195. }
  196. void WebView::SetApplicationNameForUserAgent(const std::wstring &name) {
  197. if (webview_) {
  198. ICoreWebView2Settings *settings;
  199. webview_->get_Settings(&settings);
  200. ICoreWebView2Settings2 *settings2;
  201. auto hr = settings->QueryInterface(IID_PPV_ARGS(&settings2));
  202. if (SUCCEEDED(hr)) {
  203. settings2->put_UserAgent((default_user_agent_ + name).c_str());
  204. }
  205. }
  206. }
  207. void WebView::GoBack() {
  208. if (webview_) {
  209. webview_->GoBack();
  210. }
  211. }
  212. void WebView::GoForward() {
  213. if (webview_) {
  214. webview_->GoForward();
  215. }
  216. }
  217. void WebView::Reload() {
  218. if (webview_) {
  219. webview_->Reload();
  220. }
  221. }
  222. void WebView::Stop() {
  223. if (webview_) {
  224. webview_->Stop();
  225. }
  226. }
  227. void WebView::openDevToolsWindow() {
  228. if (webview_) {
  229. webview_->OpenDevToolsWindow();
  230. }
  231. }
  232. bool WebView::CanGoBack() const {
  233. if (webview_) {
  234. BOOL can_go_back;
  235. webview_->get_CanGoBack(&can_go_back);
  236. return can_go_back;
  237. }
  238. return false;
  239. }
  240. bool WebView::CanGoForward() const {
  241. if (webview_) {
  242. BOOL can_go_forward;
  243. webview_->get_CanGoForward(&can_go_forward);
  244. return can_go_forward;
  245. }
  246. return false;
  247. }
  248. void WebView::ExecuteJavaScript(const std::wstring &javaScript,
  249. std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> completer) {
  250. if (webview_) {
  251. webview_->ExecuteScript(
  252. javaScript.c_str(),
  253. Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
  254. [completer(std::move(completer))](HRESULT error, PCWSTR result) -> HRESULT {
  255. if (error != S_OK) {
  256. completer->Error("0", "Error executing JavaScript");
  257. } else {
  258. completer->Success(flutter::EncodableValue(wide_to_utf8(std::wstring(result))));
  259. }
  260. return S_OK;
  261. }).Get());
  262. } else {
  263. completer->Error("0", "webview not created");
  264. }
  265. }
  266. void WebView::PostWebMessageAsString(const std::wstring &webmessage,
  267. std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> completer) {
  268. if (webview_) {
  269. if (webview_->PostWebMessageAsString(
  270. webmessage.c_str()) == NOERROR) {
  271. completer->Success();
  272. } else {
  273. completer->Error("0", "Error posting webmessage as String");
  274. }
  275. } else {
  276. completer->Error("0", "webview not created");
  277. }
  278. }
  279. void WebView::PostWebMessageAsJson(const std::wstring& webmessage,
  280. std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> completer) {
  281. if (webview_) {
  282. if (webview_->PostWebMessageAsJson(
  283. webmessage.c_str()) == NOERROR) {
  284. completer->Success();
  285. } else {
  286. completer->Error("0", "Error posting webmessage as JSON");
  287. }
  288. } else {
  289. completer->Error("0", "webview not created");
  290. }
  291. }
  292. WebView::~WebView() {
  293. if (webview_) {
  294. webview_->Stop();
  295. }
  296. if (webview_controller_) {
  297. webview_controller_->Close();
  298. }
  299. }
  300. }