Skip to content

Commit 7721deb

Browse files
windows: added headless inappwebview initial implementation
1 parent ab869e9 commit 7721deb

15 files changed

+443
-20
lines changed

flutter_inappwebview_windows/lib/src/in_app_webview/headless_in_app_webview.dart

+14-8
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView
274274

275275
dynamic _controllerFromPlatform;
276276

277-
WindowsHeadlessInAppWebViewCreationParams get _macosParams =>
277+
WindowsHeadlessInAppWebViewCreationParams get _windowsParams =>
278278
params as WindowsHeadlessInAppWebViewCreationParams;
279279

280280
_init() {
@@ -284,7 +284,7 @@ class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView
284284
_controllerFromPlatform =
285285
params.controllerFromPlatform?.call(_webViewController!) ??
286286
_webViewController!;
287-
_macosParams.findInteractionController?.init(id);
287+
_windowsParams.findInteractionController?.init(id);
288288
channel =
289289
MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview_$id');
290290
handler = _handleMethod;
@@ -320,8 +320,8 @@ class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView
320320
initialSettings.toMap();
321321

322322
Map<String, dynamic> pullToRefreshSettings =
323-
_macosParams.pullToRefreshController?.params.settings.toMap() ??
324-
_macosParams.pullToRefreshController?.params.options.toMap() ??
323+
_windowsParams.pullToRefreshController?.params.settings.toMap() ??
324+
_windowsParams.pullToRefreshController?.params.options.toMap() ??
325325
PullToRefreshSettings(enabled: false).toMap();
326326

327327
Map<String, dynamic> args = <String, dynamic>{};
@@ -341,8 +341,14 @@ class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView
341341
'pullToRefreshSettings': pullToRefreshSettings,
342342
'initialSize': params.initialSize.toMap()
343343
});
344-
await _sharedChannel.invokeMethod('run', args);
345-
_running = true;
344+
try {
345+
await _sharedChannel.invokeMethod('run', args);
346+
_running = true;
347+
} catch (e) {
348+
_running = false;
349+
_started = false;
350+
throw e;
351+
}
346352
}
347353

348354
void _inferInitialSettings(InAppWebViewSettings settings) {
@@ -420,8 +426,8 @@ class WindowsHeadlessInAppWebView extends PlatformHeadlessInAppWebView
420426
_webViewController?.dispose();
421427
_webViewController = null;
422428
_controllerFromPlatform = null;
423-
_macosParams.pullToRefreshController?.dispose();
424-
_macosParams.findInteractionController?.dispose();
429+
_windowsParams.pullToRefreshController?.dispose();
430+
_windowsParams.findInteractionController?.dispose();
425431
}
426432
}
427433

flutter_inappwebview_windows/lib/src/inappwebview_platform.dart

+12
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_pla
33
import 'in_app_browser/in_app_browser.dart';
44
import 'in_app_webview/in_app_webview.dart';
55
import 'in_app_webview/in_app_webview_controller.dart';
6+
import 'in_app_webview/headless_in_app_webview.dart';
67

78
/// Implementation of [InAppWebViewPlatform] using the WebKit API.
89
class WindowsInAppWebViewPlatform extends InAppWebViewPlatform {
@@ -61,4 +62,15 @@ class WindowsInAppWebViewPlatform extends InAppWebViewPlatform {
6162
WindowsInAppBrowser createPlatformInAppBrowserStatic() {
6263
return WindowsInAppBrowser.static();
6364
}
65+
66+
/// Creates a new [WindowsHeadlessInAppWebView].
67+
///
68+
/// This function should only be called by the app-facing package.
69+
/// Look at using [HeadlessInAppWebView] in `flutter_inappwebview` instead.
70+
@override
71+
WindowsHeadlessInAppWebView createPlatformHeadlessInAppWebView(
72+
PlatformHeadlessInAppWebViewCreationParams params,
73+
) {
74+
return WindowsHeadlessInAppWebView(params);
75+
}
6476
}

flutter_inappwebview_windows/windows/CMakeLists.txt

+8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ list(APPEND PLUGIN_SOURCES
7070
"types/user_script.h"
7171
"types/plugin_script.cpp"
7272
"types/plugin_script.h"
73+
"types/size_2d.cpp"
74+
"types/size_2d.h"
7375
"custom_platform_view/custom_platform_view.cc"
7476
"custom_platform_view/custom_platform_view.h"
7577
"custom_platform_view/texture_bridge.cc"
@@ -96,6 +98,12 @@ list(APPEND PLUGIN_SOURCES
9698
"in_app_webview/in_app_webview_manager.h"
9799
"in_app_webview/webview_channel_delegate.cpp"
98100
"in_app_webview/webview_channel_delegate.h"
101+
"headless_in_app_webview/headless_in_app_webview.cpp"
102+
"headless_in_app_webview/headless_in_app_webview.h"
103+
"headless_in_app_webview/headless_in_app_webview_manager.cpp"
104+
"headless_in_app_webview/headless_in_app_webview_manager.h"
105+
"headless_in_app_webview/headless_webview_channel_delegate.cpp"
106+
"headless_in_app_webview/headless_webview_channel_delegate.h"
99107
"in_app_browser/in_app_browser_settings.cpp"
100108
"in_app_browser/in_app_browser_settings.h"
101109
"in_app_browser/in_app_browser_manager.cpp"

flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#include "flutter_inappwebview_windows_plugin.h"
22

3-
#include <flutter/method_channel.h>
43
#include <flutter/plugin_registrar_windows.h>
54

5+
#include "headless_in_app_webview/headless_in_app_webview_manager.h"
66
#include "in_app_browser/in_app_browser_manager.h"
77
#include "in_app_webview/in_app_webview_manager.h"
88

@@ -25,6 +25,7 @@ namespace flutter_inappwebview_plugin
2525
{
2626
inAppWebViewManager = std::make_unique<InAppWebViewManager>(this);
2727
inAppBrowserManager = std::make_unique<InAppBrowserManager>(this);
28+
headlessInAppWebViewManager = std::make_unique<HeadlessInAppWebViewManager>(this);
2829
}
2930

3031
FlutterInappwebviewWindowsPlugin::~FlutterInappwebviewWindowsPlugin()

flutter_inappwebview_windows/windows/flutter_inappwebview_windows_plugin.h

+2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ namespace flutter_inappwebview_plugin
88
{
99
class InAppWebViewManager;
1010
class InAppBrowserManager;
11+
class HeadlessInAppWebViewManager;
1112

1213
class FlutterInappwebviewWindowsPlugin : public flutter::Plugin {
1314
public:
1415
flutter::PluginRegistrarWindows* registrar;
1516
std::unique_ptr<InAppWebViewManager> inAppWebViewManager;
1617
std::unique_ptr<InAppBrowserManager> inAppBrowserManager;
18+
std::unique_ptr<HeadlessInAppWebViewManager> headlessInAppWebViewManager;
1719

1820
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar);
1921

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include "../utils/log.h"
2+
#include "headless_in_app_webview.h"
3+
4+
namespace flutter_inappwebview_plugin
5+
{
6+
using namespace Microsoft::WRL;
7+
8+
HeadlessInAppWebView::HeadlessInAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const HeadlessInAppWebViewCreationParams& params, const HWND parentWindow, std::unique_ptr<InAppWebView> webView)
9+
: plugin(plugin), id(params.id),
10+
webView(std::move(webView)),
11+
channelDelegate(std::make_unique<HeadlessWebViewChannelDelegate>(this, plugin->registrar->messenger()))
12+
{
13+
prepare(params);
14+
ShowWindow(parentWindow, SW_HIDE);
15+
}
16+
17+
void HeadlessInAppWebView::prepare(const HeadlessInAppWebViewCreationParams& params)
18+
{
19+
if (!webView) {
20+
return;
21+
}
22+
webView->webViewController->put_IsVisible(false);
23+
}
24+
25+
void HeadlessInAppWebView::setSize(const std::shared_ptr<Size2D> size) const
26+
{
27+
if (!webView) {
28+
return;
29+
}
30+
RECT rect = {
31+
0, 0, (LONG)size->width, (LONG)size->height
32+
};
33+
webView->webViewController->put_Bounds(rect);
34+
}
35+
36+
std::shared_ptr<Size2D> HeadlessInAppWebView::getSize() const
37+
{
38+
if (!webView) {
39+
return std::make_shared<Size2D>(-1.0, -1.0);
40+
}
41+
RECT rect;
42+
webView->webViewController->get_Bounds(&rect);
43+
auto width = rect.right - rect.left;
44+
auto height = rect.bottom - rect.top;
45+
return std::make_shared<Size2D>((double)width, (double)height);
46+
}
47+
48+
HeadlessInAppWebView::~HeadlessInAppWebView()
49+
{
50+
debugLog("dealloc HeadlessInAppWebView");
51+
if (webView && webView->webViewController) {
52+
HWND parentWindow;
53+
if (succeededOrLog(webView->webViewController->get_ParentWindow(&parentWindow))) {
54+
DestroyWindow(parentWindow);
55+
}
56+
}
57+
webView = nullptr;
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#ifndef FLUTTER_INAPPWEBVIEW_PLUGIN_HEADLESS_IN_APP_WEBVIEW_H_
2+
#define FLUTTER_INAPPWEBVIEW_PLUGIN_HEADLESS_IN_APP_WEBVIEW_H_
3+
4+
#include "../flutter_inappwebview_windows_plugin.h"
5+
#include "../in_app_webview/in_app_webview.h"
6+
#include "../types/size_2d.h"
7+
#include "headless_webview_channel_delegate.h"
8+
9+
namespace flutter_inappwebview_plugin
10+
{
11+
struct HeadlessInAppWebViewCreationParams {
12+
const std::string id;
13+
const std::shared_ptr<Size2D> initialSize;
14+
};
15+
16+
class HeadlessInAppWebView
17+
{
18+
public:
19+
static inline const wchar_t* CLASS_NAME = L"HeadlessInAppWebView";
20+
static inline const std::string METHOD_CHANNEL_NAME_PREFIX = "com.pichillilorenzo/flutter_headless_inappwebview_";
21+
22+
const FlutterInappwebviewWindowsPlugin* plugin;
23+
std::string id;
24+
std::unique_ptr<InAppWebView> webView;
25+
std::unique_ptr<HeadlessWebViewChannelDelegate> channelDelegate;
26+
27+
HeadlessInAppWebView(const FlutterInappwebviewWindowsPlugin* plugin, const HeadlessInAppWebViewCreationParams& params, const HWND parentWindow, std::unique_ptr<InAppWebView> webView);
28+
~HeadlessInAppWebView();
29+
30+
void prepare(const HeadlessInAppWebViewCreationParams& params);
31+
void setSize(const std::shared_ptr<Size2D> size) const;
32+
std::shared_ptr<Size2D> getSize() const;
33+
};
34+
}
35+
#endif //FLUTTER_INAPPWEBVIEW_PLUGIN_HEADLESS_IN_APP_WEBVIEW_H_
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#include <DispatcherQueue.h>
2+
#include <flutter/method_channel.h>
3+
#include <flutter/standard_method_codec.h>
4+
#include <shlobj.h>
5+
#include <windows.graphics.capture.h>
6+
7+
#include "../in_app_webview/in_app_webview_settings.h"
8+
#include "../types/size_2d.h"
9+
#include "../types/url_request.h"
10+
#include "../types/user_script.h"
11+
#include "../utils/flutter.h"
12+
#include "../utils/log.h"
13+
#include "../utils/string.h"
14+
#include "../utils/vector.h"
15+
#include "headless_in_app_webview_manager.h"
16+
17+
namespace flutter_inappwebview_plugin
18+
{
19+
HeadlessInAppWebViewManager::HeadlessInAppWebViewManager(const FlutterInappwebviewWindowsPlugin* plugin)
20+
: plugin(plugin),
21+
ChannelDelegate(plugin->registrar->messenger(), HeadlessInAppWebViewManager::METHOD_CHANNEL_NAME)
22+
{
23+
windowClass_.lpszClassName = HeadlessInAppWebView::CLASS_NAME;
24+
windowClass_.lpfnWndProc = &DefWindowProc;
25+
26+
RegisterClass(&windowClass_);
27+
}
28+
29+
void HeadlessInAppWebViewManager::HandleMethodCall(const flutter::MethodCall<flutter::EncodableValue>& method_call,
30+
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
31+
{
32+
auto* arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
33+
auto& methodName = method_call.method_name();
34+
35+
if (string_equals(methodName, "run")) {
36+
run(arguments, std::move(result));
37+
}
38+
else {
39+
result->NotImplemented();
40+
}
41+
}
42+
43+
void HeadlessInAppWebViewManager::run(const flutter::EncodableMap* arguments, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
44+
{
45+
auto result_ = std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>(std::move(result));
46+
47+
auto id = get_fl_map_value<std::string>(*arguments, "id");
48+
auto params = get_fl_map_value<flutter::EncodableMap>(*arguments, "params");
49+
50+
auto initialSize = std::make_shared<Size2D>(get_fl_map_value<flutter::EncodableMap>(params, "initialSize"));
51+
52+
auto settingsMap = get_fl_map_value<flutter::EncodableMap>(params, "initialSettings");
53+
auto urlRequestMap = get_optional_fl_map_value<flutter::EncodableMap>(params, "initialUrlRequest");
54+
auto initialFile = get_optional_fl_map_value<std::string>(params, "initialFile");
55+
auto initialDataMap = get_optional_fl_map_value<flutter::EncodableMap>(params, "initialData");
56+
auto initialUserScriptList = get_optional_fl_map_value<flutter::EncodableList>(params, "initialUserScripts");
57+
58+
RECT bounds;
59+
GetClientRect(plugin->registrar->GetView()->GetNativeWindow(), &bounds);
60+
61+
auto initialWidth = initialSize->width >= 0 ? initialSize->width : bounds.right - bounds.left;
62+
auto initialHeight = initialSize->height >= 0 ? initialSize->height : bounds.bottom - bounds.top;
63+
64+
auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, 0,
65+
0, (int)initialWidth, (int)initialHeight,
66+
plugin->registrar->GetView()->GetNativeWindow(),
67+
nullptr,
68+
windowClass_.hInstance, nullptr);
69+
70+
InAppWebView::createInAppWebViewEnv(hwnd, false,
71+
[=](wil::com_ptr<ICoreWebView2Environment> webViewEnv,
72+
wil::com_ptr<ICoreWebView2Controller> webViewController,
73+
wil::com_ptr<ICoreWebView2CompositionController> webViewCompositionController)
74+
{
75+
if (webViewEnv && webViewController) {
76+
auto initialSettings = std::make_unique<InAppWebViewSettings>(settingsMap);
77+
std::optional<std::vector<std::shared_ptr<UserScript>>> initialUserScripts = initialUserScriptList.has_value() ?
78+
functional_map(initialUserScriptList.value(), [](const flutter::EncodableValue& map) { return std::make_shared<UserScript>(std::get<flutter::EncodableMap>(map)); }) :
79+
std::optional<std::vector<std::shared_ptr<UserScript>>>{};
80+
81+
InAppWebViewCreationParams params = {
82+
id,
83+
std::move(initialSettings),
84+
initialUserScripts
85+
};
86+
87+
auto inAppWebView = std::make_unique<InAppWebView>(plugin, params, hwnd,
88+
std::move(webViewEnv), std::move(webViewController), nullptr
89+
);
90+
91+
HeadlessInAppWebViewCreationParams headlessParams = {
92+
id,
93+
std::move(initialSize)
94+
};
95+
96+
auto headlessInAppWebView = std::make_unique<HeadlessInAppWebView>(plugin,
97+
headlessParams,
98+
hwnd,
99+
std::move(inAppWebView));
100+
101+
headlessInAppWebView->webView->initChannel(std::nullopt, std::nullopt);
102+
103+
if (headlessInAppWebView->channelDelegate) {
104+
headlessInAppWebView->channelDelegate->onWebViewCreated();
105+
}
106+
107+
std::optional<std::shared_ptr<URLRequest>> urlRequest = urlRequestMap.has_value() ? std::make_shared<URLRequest>(urlRequestMap.value()) : std::optional<std::shared_ptr<URLRequest>>{};
108+
if (urlRequest.has_value()) {
109+
headlessInAppWebView->webView->loadUrl(urlRequest.value());
110+
}
111+
else if (initialFile.has_value()) {
112+
headlessInAppWebView->webView->loadFile(initialFile.value());
113+
}
114+
else if (initialDataMap.has_value()) {
115+
headlessInAppWebView->webView->loadData(get_fl_map_value<std::string>(initialDataMap.value(), "data"));
116+
}
117+
118+
webViews.insert({ id, std::move(headlessInAppWebView) });
119+
120+
result_->Success(true);
121+
}
122+
else {
123+
result_->Error("0", "Cannot create the HeadlessInAppWebView instance!");
124+
}
125+
}
126+
);
127+
}
128+
129+
HeadlessInAppWebViewManager::~HeadlessInAppWebViewManager()
130+
{
131+
debugLog("dealloc HeadlessInAppWebViewManager");
132+
webViews.clear();
133+
UnregisterClass(windowClass_.lpszClassName, nullptr);
134+
plugin = nullptr;
135+
}
136+
}

0 commit comments

Comments
 (0)