Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 334d8e2

Browse files
authored
[webview_flutter] Implementation of the webview_flutter_platform_interface package (#4302)
1 parent 1f472be commit 334d8e2

24 files changed

+1724
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Below is a list of people and organizations that have contributed
2+
# to the Flutter project. Names should be added to the list like so:
3+
#
4+
# Name/Organization <email address>
5+
6+
Google Inc.
7+
The Chromium Authors
8+
German Saprykin <[email protected]>
9+
Benjamin Sauer <[email protected]>
10+
11+
Ali Bitek <[email protected]>
12+
Pol Batlló <[email protected]>
13+
Anatoly Pulyaevskiy
14+
Hayden Flinner <[email protected]>
15+
Stefano Rodriguez <[email protected]>
16+
Salvatore Giordano <[email protected]>
17+
Brian Armstrong <[email protected]>
18+
Paul DeMarco <[email protected]>
19+
Fabricio Nogueira <[email protected]>
20+
Simon Lightfoot <[email protected]>
21+
Ashton Thomas <[email protected]>
22+
Thomas Danner <[email protected]>
23+
Diego Velásquez <[email protected]>
24+
Hajime Nakamura <[email protected]>
25+
Tuyển Vũ Xuân <[email protected]>
26+
Miguel Ruivo <[email protected]>
27+
Sarthak Verma <[email protected]>
28+
Mike Diarmid <[email protected]>
29+
Invertase <[email protected]>
30+
Elliot Hesp <[email protected]>
31+
Vince Varga <[email protected]>
32+
Aawaz Gyawali <[email protected]>
33+
EUI Limited <[email protected]>
34+
Katarina Sheremet <[email protected]>
35+
Thomas Stockx <[email protected]>
36+
Sarbagya Dhaubanjar <[email protected]>
37+
Ozkan Eksi <[email protected]>
38+
Rishab Nayak <[email protected]>
39+
40+
Jonathan Younger <[email protected]>
41+
Jose Sanchez <[email protected]>
42+
Debkanchan Samadder <[email protected]>
43+
Audrius Karosevicius <[email protected]>
44+
Lukasz Piliszczuk <[email protected]>
45+
SoundReply Solutions GmbH <[email protected]>
46+
Rafal Wachol <[email protected]>
47+
Pau Picas <[email protected]>
48+
Christian Weder <[email protected]>
49+
Alexandru Tuca <[email protected]>
50+
Christian Weder <[email protected]>
51+
Rhodes Davis Jr. <[email protected]>
52+
Luigi Agosti <[email protected]>
53+
Quentin Le Guennec <[email protected]>
54+
Koushik Ravikumar <[email protected]>
55+
Nissim Dsilva <[email protected]>
56+
Giancarlo Rocha <[email protected]>
57+
Ryo Miyake <[email protected]>
58+
Théo Champion <[email protected]>
59+
Kazuki Yamaguchi <[email protected]>
60+
Eitan Schwartz <[email protected]>
61+
Chris Rutkowski <[email protected]>
62+
Juan Alvarez <[email protected]>
63+
Aleksandr Yurkovskiy <[email protected]>
64+
Anton Borries <[email protected]>
65+
66+
Rahul Raj <[email protected]>
67+
Maurits van Beusekom <[email protected]>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 1.0.0
2+
3+
* Extracted platform interface from `webview_flutter`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Copyright 2013 The Flutter Authors. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without modification,
4+
are permitted provided that the following conditions are met:
5+
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above
9+
copyright notice, this list of conditions and the following
10+
disclaimer in the documentation and/or other materials provided
11+
with the distribution.
12+
* Neither the name of Google Inc. nor the names of its
13+
contributors may be used to endorse or promote products derived
14+
from this software without specific prior written permission.
15+
16+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# webview_flutter_platform_interface
2+
3+
A common platform interface for the [`webview_flutter`](https://pub.dev/packages/webview_flutter) plugin.
4+
5+
This interface allows platform-specific implementations of the `webview_flutter`
6+
plugin, as well as the plugin itself, to ensure they are supporting the
7+
same interface.
8+
9+
# Usage
10+
11+
To implement a new platform-specific implementation of `webview_flutter`, extend
12+
[`WebviewPlatform`](lib/src/platform_interface/webview_platform.dart) with an implementation that performs the
13+
platform-specific behavior, and when you register your plugin, set the default
14+
`WebviewPlatform` by calling
15+
`WebviewPlatform.setInstance(MyPlatformWebview())`.
16+
17+
# Note on breaking changes
18+
19+
Strongly prefer non-breaking changes (such as adding a method to the interface)
20+
over breaking changes for this package.
21+
22+
See https://flutter.dev/go/platform-interface-breaking-changes for a discussion
23+
on why a less-clean interface is preferable to a breaking change.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:flutter/services.dart';
8+
9+
import '../platform_interface/javascript_channel_registry.dart';
10+
import '../platform_interface/platform_interface.dart';
11+
import '../types/types.dart';
12+
13+
/// A [WebViewPlatformController] that uses a method channel to control the webview.
14+
class MethodChannelWebViewPlatform implements WebViewPlatformController {
15+
/// Constructs an instance that will listen for webviews broadcasting to the
16+
/// given [id], using the given [WebViewPlatformCallbacksHandler].
17+
MethodChannelWebViewPlatform(
18+
int id,
19+
this._platformCallbacksHandler,
20+
this._javascriptChannelRegistry,
21+
) : assert(_platformCallbacksHandler != null),
22+
_channel = MethodChannel('plugins.flutter.io/webview_$id') {
23+
_channel.setMethodCallHandler(_onMethodCall);
24+
}
25+
26+
final JavascriptChannelRegistry _javascriptChannelRegistry;
27+
28+
final WebViewPlatformCallbacksHandler _platformCallbacksHandler;
29+
30+
final MethodChannel _channel;
31+
32+
static const MethodChannel _cookieManagerChannel =
33+
MethodChannel('plugins.flutter.io/cookie_manager');
34+
35+
Future<bool?> _onMethodCall(MethodCall call) async {
36+
switch (call.method) {
37+
case 'javascriptChannelMessage':
38+
final String channel = call.arguments['channel']!;
39+
final String message = call.arguments['message']!;
40+
_javascriptChannelRegistry.onJavascriptChannelMessage(channel, message);
41+
return true;
42+
case 'navigationRequest':
43+
return await _platformCallbacksHandler.onNavigationRequest(
44+
url: call.arguments['url']!,
45+
isForMainFrame: call.arguments['isForMainFrame']!,
46+
);
47+
case 'onPageFinished':
48+
_platformCallbacksHandler.onPageFinished(call.arguments['url']!);
49+
return null;
50+
case 'onProgress':
51+
_platformCallbacksHandler.onProgress(call.arguments['progress']);
52+
return null;
53+
case 'onPageStarted':
54+
_platformCallbacksHandler.onPageStarted(call.arguments['url']!);
55+
return null;
56+
case 'onWebResourceError':
57+
_platformCallbacksHandler.onWebResourceError(
58+
WebResourceError(
59+
errorCode: call.arguments['errorCode']!,
60+
description: call.arguments['description']!,
61+
// iOS doesn't support `failingUrl`.
62+
failingUrl: call.arguments['failingUrl'],
63+
domain: call.arguments['domain'],
64+
errorType: call.arguments['errorType'] == null
65+
? null
66+
: WebResourceErrorType.values.firstWhere(
67+
(WebResourceErrorType type) {
68+
return type.toString() ==
69+
'$WebResourceErrorType.${call.arguments['errorType']}';
70+
},
71+
),
72+
),
73+
);
74+
return null;
75+
}
76+
77+
throw MissingPluginException(
78+
'${call.method} was invoked but has no handler',
79+
);
80+
}
81+
82+
@override
83+
Future<void> loadUrl(
84+
String url,
85+
Map<String, String>? headers,
86+
) async {
87+
assert(url != null);
88+
return _channel.invokeMethod<void>('loadUrl', <String, dynamic>{
89+
'url': url,
90+
'headers': headers,
91+
});
92+
}
93+
94+
@override
95+
Future<String?> currentUrl() => _channel.invokeMethod<String>('currentUrl');
96+
97+
@override
98+
Future<bool> canGoBack() =>
99+
_channel.invokeMethod<bool>("canGoBack").then((result) => result!);
100+
101+
@override
102+
Future<bool> canGoForward() =>
103+
_channel.invokeMethod<bool>("canGoForward").then((result) => result!);
104+
105+
@override
106+
Future<void> goBack() => _channel.invokeMethod<void>("goBack");
107+
108+
@override
109+
Future<void> goForward() => _channel.invokeMethod<void>("goForward");
110+
111+
@override
112+
Future<void> reload() => _channel.invokeMethod<void>("reload");
113+
114+
@override
115+
Future<void> clearCache() => _channel.invokeMethod<void>("clearCache");
116+
117+
@override
118+
Future<void> updateSettings(WebSettings settings) async {
119+
final Map<String, dynamic> updatesMap = _webSettingsToMap(settings);
120+
if (updatesMap.isNotEmpty) {
121+
await _channel.invokeMethod<void>('updateSettings', updatesMap);
122+
}
123+
}
124+
125+
@override
126+
Future<String> evaluateJavascript(String javascriptString) {
127+
return _channel
128+
.invokeMethod<String>('evaluateJavascript', javascriptString)
129+
.then((result) => result!);
130+
}
131+
132+
@override
133+
Future<void> addJavascriptChannels(Set<String> javascriptChannelNames) {
134+
return _channel.invokeMethod<void>(
135+
'addJavascriptChannels', javascriptChannelNames.toList());
136+
}
137+
138+
@override
139+
Future<void> removeJavascriptChannels(Set<String> javascriptChannelNames) {
140+
return _channel.invokeMethod<void>(
141+
'removeJavascriptChannels', javascriptChannelNames.toList());
142+
}
143+
144+
@override
145+
Future<String?> getTitle() => _channel.invokeMethod<String>("getTitle");
146+
147+
@override
148+
Future<void> scrollTo(int x, int y) {
149+
return _channel.invokeMethod<void>('scrollTo', <String, int>{
150+
'x': x,
151+
'y': y,
152+
});
153+
}
154+
155+
@override
156+
Future<void> scrollBy(int x, int y) {
157+
return _channel.invokeMethod<void>('scrollBy', <String, int>{
158+
'x': x,
159+
'y': y,
160+
});
161+
}
162+
163+
@override
164+
Future<int> getScrollX() =>
165+
_channel.invokeMethod<int>("getScrollX").then((result) => result!);
166+
167+
@override
168+
Future<int> getScrollY() =>
169+
_channel.invokeMethod<int>("getScrollY").then((result) => result!);
170+
171+
/// Method channel implementation for [WebViewPlatform.clearCookies].
172+
static Future<bool> clearCookies() {
173+
return _cookieManagerChannel
174+
.invokeMethod<bool>('clearCookies')
175+
.then<bool>((dynamic result) => result!);
176+
}
177+
178+
static Map<String, dynamic> _webSettingsToMap(WebSettings? settings) {
179+
final Map<String, dynamic> map = <String, dynamic>{};
180+
void _addIfNonNull(String key, dynamic value) {
181+
if (value == null) {
182+
return;
183+
}
184+
map[key] = value;
185+
}
186+
187+
void _addSettingIfPresent<T>(String key, WebSetting<T> setting) {
188+
if (!setting.isPresent) {
189+
return;
190+
}
191+
map[key] = setting.value;
192+
}
193+
194+
_addIfNonNull('jsMode', settings!.javascriptMode?.index);
195+
_addIfNonNull('hasNavigationDelegate', settings.hasNavigationDelegate);
196+
_addIfNonNull('hasProgressTracking', settings.hasProgressTracking);
197+
_addIfNonNull('debuggingEnabled', settings.debuggingEnabled);
198+
_addIfNonNull(
199+
'gestureNavigationEnabled', settings.gestureNavigationEnabled);
200+
_addIfNonNull(
201+
'allowsInlineMediaPlayback', settings.allowsInlineMediaPlayback);
202+
_addSettingIfPresent('userAgent', settings.userAgent);
203+
return map;
204+
}
205+
206+
/// Converts a [CreationParams] object to a map as expected by `platform_views` channel.
207+
///
208+
/// This is used for the `creationParams` argument of the platform views created by
209+
/// [AndroidWebViewBuilder] and [CupertinoWebViewBuilder].
210+
static Map<String, dynamic> creationParamsToMap(
211+
CreationParams creationParams, {
212+
bool usesHybridComposition = false,
213+
}) {
214+
return <String, dynamic>{
215+
'initialUrl': creationParams.initialUrl,
216+
'settings': _webSettingsToMap(creationParams.webSettings),
217+
'javascriptChannelNames': creationParams.javascriptChannelNames.toList(),
218+
'userAgent': creationParams.userAgent,
219+
'autoMediaPlaybackPolicy': creationParams.autoMediaPlaybackPolicy.index,
220+
'usesHybridComposition': usesHybridComposition,
221+
};
222+
}
223+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import '../types/javascript_channel.dart';
6+
import '../types/javascript_message.dart';
7+
8+
/// Utility class for managing named JavaScript channels and forwarding incoming
9+
/// messages on the correct channel.
10+
class JavascriptChannelRegistry {
11+
/// Constructs a [JavascriptChannelRegistry] initializing it with the given
12+
/// set of [JavascriptChannel]s.
13+
JavascriptChannelRegistry(Set<JavascriptChannel>? channels) {
14+
updateJavascriptChannelsFromSet(channels);
15+
}
16+
17+
/// Maps a channel name to a channel.
18+
final Map<String, JavascriptChannel> channels = <String, JavascriptChannel>{};
19+
20+
/// Invoked when a JavaScript channel message is received.
21+
void onJavascriptChannelMessage(String channel, String message) {
22+
final JavascriptChannel? javascriptChannel = channels[channel];
23+
24+
if (javascriptChannel == null) {
25+
throw ArgumentError('No channel registered with name $channel.');
26+
}
27+
28+
javascriptChannel.onMessageReceived(JavascriptMessage(message));
29+
}
30+
31+
/// Updates the set of [JavascriptChannel]s with the new set.
32+
void updateJavascriptChannelsFromSet(Set<JavascriptChannel>? channels) {
33+
this.channels.clear();
34+
if (channels == null) {
35+
return;
36+
}
37+
38+
for (final JavascriptChannel channel in channels) {
39+
this.channels[channel.name] = channel;
40+
}
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
export 'javascript_channel_registry.dart';
6+
export 'webview_platform.dart';
7+
export 'webview_platform_callbacks_handler.dart';
8+
export 'webview_platform_controller.dart';

0 commit comments

Comments
 (0)