Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions webview_all/lib/src/proxy_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:webview_platform_interface/webview_platform_interface.dart';

/// Manages process-wide proxy settings for WebViews.
class ProxyController {
/// Constructs a [ProxyController].
ProxyController()
: this.fromPlatformCreationParams(
const PlatformProxyControllerCreationParams(),
);

/// Constructs a [ProxyController] from creation params for a specific
/// platform.
ProxyController.fromPlatformCreationParams(
PlatformProxyControllerCreationParams params,
) : this.fromPlatform(PlatformProxyController(params));

/// Constructs a [ProxyController] from a specific platform implementation.
ProxyController.fromPlatform(this.platform);

/// Gets a shared [ProxyController] instance.
static ProxyController instance() => ProxyController();

/// Implementation of [PlatformProxyController] for the current platform.
final PlatformProxyController platform;

/// Sets proxy settings for WebViews in this process.
Future<void> setProxyOverride({required ProxySettings settings}) {
return platform.setProxyOverride(settings: settings);
}

/// Clears process-wide WebView proxy settings.
Future<void> clearProxyOverride() {
return platform.clearProxyOverride();
}
}
6 changes: 6 additions & 0 deletions webview_all/lib/webview_all.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ export 'package:webview_platform_interface/webview_platform_interface.dart'
NavigationRequestCallback,
PageEventCallback,
PlatformNavigationDelegateCreationParams,
PlatformProxyController,
PlatformProxyControllerCreationParams,
PlatformWebViewControllerCreationParams,
PlatformWebViewCookieManagerCreationParams,
PlatformWebViewDataManagerCreationParams,
PlatformWebViewPermissionRequest,
PlatformWebViewWidgetCreationParams,
ProgressCallback,
ProxyRule,
ProxySchemeFilter,
ProxySettings,
ScrollPositionChange,
UrlChange,
WebResourceError,
Expand All @@ -43,6 +48,7 @@ export 'package:webview_platform_interface/webview_platform_interface.dart'
X509Certificate;

export 'src/navigation_delegate.dart';
export 'src/proxy_controller.dart';
export 'src/offscreen_webview_session.dart';
export 'src/webview_controller.dart';
export 'src/webview_cookie_manager.dart';
Expand Down
86 changes: 86 additions & 0 deletions webview_all_linux/lib/src/linux_proxy_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import 'package:webview_platform_interface/webview_platform_interface.dart';

import 'linux_webview_controller.dart';

/// Linux-specific creation params for [LinuxProxyController].
class LinuxProxyControllerCreationParams
extends PlatformProxyControllerCreationParams {
/// Creates Linux proxy controller creation params.
const LinuxProxyControllerCreationParams();

/// Creates Linux proxy controller creation params from platform params.
const LinuxProxyControllerCreationParams.fromPlatformProxyControllerCreationParams(
PlatformProxyControllerCreationParams params,
);
}

/// Controls WebKitGTK proxy settings for Linux WebViews.
class LinuxProxyController extends PlatformProxyController {
/// Creates a [LinuxProxyController].
LinuxProxyController(PlatformProxyControllerCreationParams params)
: super.implementation(
params is LinuxProxyControllerCreationParams
? params
: LinuxProxyControllerCreationParams.fromPlatformProxyControllerCreationParams(
params,
),
);

/// Gets a Linux proxy controller instance.
static LinuxProxyController instance() {
return LinuxProxyController(const LinuxProxyControllerCreationParams());
}

@override
Future<void> setProxyOverride({required ProxySettings settings}) {
_validateProxySettings(settings);
return LinuxWebViewController.rootChannel.invokeMethod<void>(
'setProxyOverride',
settings.toMap(),
);
}

@override
Future<void> clearProxyOverride() {
return LinuxWebViewController.rootChannel.invokeMethod<void>(
'clearProxyOverride',
);
}

void _validateProxySettings(ProxySettings settings) {
if (settings.proxyRules.isEmpty) {
throw ArgumentError.value(
settings,
'settings',
'At least one proxy rule is required.',
);
}

for (final ProxyRule rule in settings.proxyRules) {
if (rule.url.isEmpty) {
throw ArgumentError.value(
rule.url,
'settings.proxyRules.url',
'Proxy URL must not be empty.',
);
}
final Uri uri = Uri.parse(rule.url);
if (!uri.hasScheme || uri.host.isEmpty) {
throw ArgumentError.value(
rule.url,
'settings.proxyRules.url',
'Proxy URL must be an absolute URI with a host.',
);
}
final ProxySchemeFilter? schemeFilter = rule.schemeFilter;
if (schemeFilter != null &&
!ProxySchemeFilter.values.contains(schemeFilter)) {
throw ArgumentError.value(
schemeFilter,
'settings.proxyRules.schemeFilter',
'Unsupported proxy scheme filter.',
);
}
}
}
}
9 changes: 9 additions & 0 deletions webview_all_linux/lib/src/linux_webview_platform.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:webview_platform_interface/webview_platform_interface.dart';

import 'linux_navigation_delegate.dart';
import 'linux_proxy_controller.dart';
import 'linux_webview_controller.dart';
import 'linux_webview_cookie_manager.dart';
import 'linux_webview_data_manager.dart';
Expand Down Expand Up @@ -42,6 +43,14 @@ class LinuxWebViewPlatform extends WebViewPlatform {
return LinuxWebViewCookieManager(params);
}


@override
LinuxProxyController createPlatformProxyController(
PlatformProxyControllerCreationParams params,
) {
return LinuxProxyController(params);
}

@override
LinuxWebViewDataManager createPlatformWebViewDataManager(
PlatformWebViewDataManagerCreationParams params,
Expand Down
1 change: 1 addition & 0 deletions webview_all_linux/lib/webview_all_linux.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export 'src/linux_navigation_delegate.dart';
export 'src/linux_proxy_controller.dart';
export 'src/linux_webview_controller.dart';
export 'src/linux_webview_cookie_manager.dart';
export 'src/linux_webview_data_manager.dart';
Expand Down
113 changes: 113 additions & 0 deletions webview_all_linux/linux/src/plugin/root_channel.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "plugin/webview_all_linux_plugin_private.h"
#include "common/method_channel_utils.h"

#include <gio/gio.h>
#include <libsoup/soup.h>

#include <cstring>
Expand Down Expand Up @@ -54,6 +55,92 @@ static void clear_website_data_finished_cb(GObject* object,
g_object_unref(method_call);
}


static GPtrArray* proxy_ignore_hosts_from_value(FlValue* value) {
GPtrArray* ignore_hosts = g_ptr_array_new_with_free_func(g_free);
if (value != nullptr && fl_value_get_type(value) == FL_VALUE_TYPE_LIST) {
const size_t length = fl_value_get_length(value);
for (size_t i = 0; i < length; ++i) {
FlValue* item = fl_value_get_list_value(value, i);
if (item != nullptr && fl_value_get_type(item) == FL_VALUE_TYPE_STRING) {
g_ptr_array_add(ignore_hosts,
g_strdup(fl_value_get_string(item)));
}
}
}
g_ptr_array_add(ignore_hosts, nullptr);
return ignore_hosts;
}

static WebKitNetworkProxySettings* proxy_settings_from_value(
FlValue* value, GError** error) {
FlValue* proxy_rules = map_lookup(value, "proxyRules");
if (proxy_rules == nullptr ||
fl_value_get_type(proxy_rules) != FL_VALUE_TYPE_LIST ||
fl_value_get_length(proxy_rules) == 0) {
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"At least one proxy rule is required.");
return nullptr;
}

GPtrArray* ignore_hosts =
proxy_ignore_hosts_from_value(map_lookup(value, "bypassRules"));
const gchar* default_proxy_uri = nullptr;
GPtrArray* scheme_rules = g_ptr_array_new();

const size_t length = fl_value_get_length(proxy_rules);
for (size_t i = 0; i < length; ++i) {
FlValue* rule = fl_value_get_list_value(proxy_rules, i);
if (rule == nullptr || fl_value_get_type(rule) != FL_VALUE_TYPE_MAP) {
continue;
}

const gchar* proxy_uri = map_lookup_string(rule, "url");
if (proxy_uri == nullptr || strlen(proxy_uri) == 0) {
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"Proxy rule URL must not be empty.");
g_ptr_array_unref(ignore_hosts);
g_ptr_array_unref(scheme_rules);
return nullptr;
}

const gchar* scheme_filter = map_lookup_string(rule, "schemeFilter");
if (scheme_filter == nullptr || strcmp(scheme_filter, "*") == 0) {
if (default_proxy_uri == nullptr) {
default_proxy_uri = proxy_uri;
}
continue;
}

if (strcmp(scheme_filter, "http") != 0 &&
strcmp(scheme_filter, "https") != 0) {
g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"Unsupported proxy scheme filter: %s", scheme_filter);
g_ptr_array_unref(ignore_hosts);
g_ptr_array_unref(scheme_rules);
return nullptr;
}

g_ptr_array_add(scheme_rules, rule);
}

WebKitNetworkProxySettings* settings = webkit_network_proxy_settings_new(
default_proxy_uri,
reinterpret_cast<const gchar* const*>(ignore_hosts->pdata));

for (guint i = 0; i < scheme_rules->len; ++i) {
FlValue* rule = static_cast<FlValue*>(g_ptr_array_index(scheme_rules, i));
const gchar* scheme_filter = map_lookup_string(rule, "schemeFilter");
const gchar* proxy_uri = map_lookup_string(rule, "url");
webkit_network_proxy_settings_add_proxy_for_scheme(settings, scheme_filter,
proxy_uri);
}

g_ptr_array_unref(ignore_hosts);
g_ptr_array_unref(scheme_rules);
return settings;
}

static void get_cookies_finished_cb(GObject* object,
GAsyncResult* result,
gpointer user_data) {
Expand Down Expand Up @@ -124,6 +211,32 @@ void root_method_call_cb(FlMethodChannel* channel,
WebKitWebsiteDataManager* website_data_manager =
webkit_web_context_get_website_data_manager(context);


if (strcmp(method, "setProxyOverride") == 0) {
GError* error = nullptr;
WebKitNetworkProxySettings* proxy_settings =
proxy_settings_from_value(args, &error);
if (error != nullptr) {
respond(method_call, error_response("invalid_proxy_settings",
error->message));
g_clear_error(&error);
return;
}

webkit_website_data_manager_set_network_proxy_settings(
website_data_manager, WEBKIT_NETWORK_PROXY_MODE_CUSTOM, proxy_settings);
webkit_network_proxy_settings_free(proxy_settings);
respond(method_call, success_response());
return;
}

if (strcmp(method, "clearProxyOverride") == 0) {
webkit_website_data_manager_set_network_proxy_settings(
website_data_manager, WEBKIT_NETWORK_PROXY_MODE_DEFAULT, nullptr);
respond(method_call, success_response());
return;
}

if (strcmp(method, "clearCookies") == 0) {
g_object_ref(method_call);
webkit_website_data_manager_clear(
Expand Down
32 changes: 32 additions & 0 deletions webview_all_linux/test/linux_webview_platform_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ void main() {
),
isA<LinuxWebViewCookieManager>(),
);
expect(
platform.createPlatformProxyController(
const PlatformProxyControllerCreationParams(),
),
isA<LinuxProxyController>(),
);
expect(
platform.createPlatformWebViewDataManager(
const PlatformWebViewDataManagerCreationParams(),
Expand All @@ -62,6 +68,32 @@ void main() {
);
});

test('forwards proxy override settings to the root channel', () async {
final List<MethodCall> calls = <MethodCall>[];
_mockLinuxWebViewCreation(onRootCall: calls.add);
final LinuxProxyController controller = LinuxProxyController.instance();

await controller.setProxyOverride(
settings: const ProxySettings(
bypassRules: <String>['localhost', '127.0.0.1'],
proxyRules: <ProxyRule>[
ProxyRule(url: 'http://127.0.0.1:8888'),
],
),
);
await controller.clearProxyOverride();

expect(calls, hasLength(2));
expect(calls[0].method, 'setProxyOverride');
expect(calls[0].arguments, <String, Object?>{
'bypassRules': <String>['localhost', '127.0.0.1'],
'proxyRules': <Map<String, Object?>>[
<String, Object?>{'schemeFilter': null, 'url': 'http://127.0.0.1:8888'},
],
});
expect(calls[1].method, 'clearProxyOverride');
});

testWidgets('moves the native view when the controller changes', (
WidgetTester tester,
) async {
Expand Down
Loading
Loading