Open wujekbogdan opened 2 years ago
👋 @wujekbogdan
NOTE: This comment is auto-generated.
Are you sure you have already searched for the same problem?
Some people open new issues but they didn't search for something similar or for the same issue. Please, search for it using the GitHub issue search box or on the official inappwebview.dev website, or, also, using Google, StackOverflow, etc. before posting a new one. You may already find an answer to your problem!
If this is really a new issue, then thank you for raising it. I will investigate it and get back to you as soon as possible. Please, make sure you have given me as much context as possible! Also, if you didn't already, post a code example that can replicate this issue.
In the meantime, you can already search for some possible solutions online! Because this plugin uses native WebView, you can search online for the same issue adding android WebView [MY ERROR HERE]
or ios WKWebView [MY ERROR HERE]
keywords.
Following these steps can save you, me, and other people a lot of time, thanks!
Currently, It's not possible to use javascript handlers on Web platform.
iframe
objects are very very limited, unfortunately.
To know if a method/event/class is supported on Web, check the code docs directly.
EDIT - rephrased since I'm focusing on one part of @wujekbogdan's post (not the error output)
From outside the webview (in dart), it's possible to push in javascript to get run with eval
(as long as the "origin" matches -- otherwise you will always get the errors mentioned in the original post).
In case of JavaScript Handlers the window.flutter_inappwebview.callHandler ("outside" the iframe, in the global scope) resolves with undefined Web Message Listeners don't work either: window. myObject object resolves with undefined when accessed from the "inside" of the iframe.
The above quote mentions things that if implemented would address my issue (and I believe it is possible as long as the origin matches). "evaluateJavascript" has a (synchronous) return value, but that won't work for all the cases. For a lot of cases the action originates inside the webview and you might want immediate recognition of that in flutter.
So even if you have the same origin, it being unnecessarily restrictive.
Although it would be a bit of a hack, it is currently possible to use onConsoleMessage
(the callback parameter for InAppWebView
constructor) as a way to trigger dart things based on a JS statement (e.g. console.log(JSON.stringify({type: 'callback', name: 'foo', value: 'bar'}))
).
Although it would be a bit of a hack, it should be currently possible to use
onConsoleMessage
(the callback parameter forInAppWebView
constructor) as a way to trigger dart things based on a JS statement (e.g.console.log(JSON.stringify({type: 'callback', name: 'foo', value: 'bar'}))
).
I don't understand what happen, I'm using the inappwebview v6.0.0-beta.22 and saw the documentation that javascript injection should works. But i have the same errors:
DOMException: Blocked a frame with origin "http://localhost:63239" from accessing a cross-origin frame.
at HTMLIFrameElement.<anonymous> (http://localhost:63239/assets/packages/flutter_inappwebview/assets/web/web_support.js:49:65)
js_primitives.dart:30
DOMException: Blocked a frame with origin "http://localhost:63239" from accessing a cross-origin frame.
at HTMLIFrameElement.<anonymous> (http://localhost:63239/assets/packages/flutter_inappwebview/assets/web/web_support.js:57:61)
DOMException: Blocked a frame with origin "http://localhost:63239" from accessing a cross-origin frame.
at HTMLIFrameElement.<anonymous> (http://localhost:63239/assets/packages/flutter_inappwebview/assets/web/web_support.js:84:74)
DOMException: Blocked a frame with origin "http://localhost:63239" from accessing a cross-origin frame.
at HTMLIFrameElement.<anonymous> (http://localhost:63239/assets/packages/flutter_inappwebview/assets/web/web_support.js:200:59)
And I get null value when run
onLoadStop: (controller, url) async {
var result =
await controller.evaluateJavascript(source: "1 + 1");
print(result.runtimeType); // int
print(result); // 2
}
Here my WebView
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:url_launcher/url_launcher.dart';
class BelvoInAppWebView extends StatefulWidget {
const BelvoInAppWebView({
Key? key,
required this.url,
this.width,
this.height,
this.bypass = false,
this.horizontalScroll = false,
this.verticalScroll = false,
}) : super(key: key);
final bool bypass;
final bool horizontalScroll;
final bool verticalScroll;
final double? height;
final double? width;
final String url;
@override
_BelvoInAppWebViewState createState() => new _BelvoInAppWebViewState();
}
class _BelvoInAppWebViewState extends State<BelvoInAppWebView> {
final GlobalKey webViewKey = GlobalKey();
InAppWebViewController? webViewController;
InAppWebViewSettings settings = InAppWebViewSettings(
useShouldOverrideUrlLoading: true,
mediaPlaybackRequiresUserGesture: false,
allowsInlineMediaPlayback: true,
iframeAllow: "camera; microphone",
iframeAllowFullscreen: true);
PullToRefreshController? pullToRefreshController;
String url = "";
double progress = 0;
final urlController = TextEditingController();
@override
void initState() {
super.initState();
pullToRefreshController = kIsWeb
? null
: PullToRefreshController(
settings: PullToRefreshSettings(
color: Colors.blue,
),
onRefresh: () async {
if (defaultTargetPlatform == TargetPlatform.android) {
webViewController?.reload();
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
webViewController?.loadUrl(
urlRequest:
URLRequest(url: await webViewController?.getUrl()));
}
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Official InAppWebView website")),
body: SafeArea(
child: Column(children: <Widget>[
TextField(
decoration: InputDecoration(prefixIcon: Icon(Icons.search)),
controller: urlController,
keyboardType: TextInputType.url,
onSubmitted: (value) {
var url = WebUri(value);
if (url.scheme.isEmpty) {
url = WebUri("https://www.google.com/search?q=" + value);
}
webViewController?.loadUrl(urlRequest: URLRequest(url: url));
},
),
Expanded(
child: Stack(
children: [
InAppWebView(
key: webViewKey,
initialUrlRequest:
URLRequest(url: WebUri("https://flutter.dev")),
initialSettings: settings,
pullToRefreshController: pullToRefreshController,
onWebViewCreated: (controller) {
debugPrint('onWebViewCreated');
webViewController = controller;
},
onLoadStart: (controller, url) {
debugPrint('onLoadStart');
setState(() {
this.url = url.toString();
urlController.text = this.url;
});
},
onPermissionRequest: (controller, request) async {
return PermissionResponse(
resources: request.resources,
action: PermissionResponseAction.GRANT);
},
shouldOverrideUrlLoading:
(controller, navigationAction) async {
var uri = navigationAction.request.url!;
if (![
"http",
"https",
"file",
"chrome",
"data",
"javascript",
"about"
].contains(uri.scheme)) {
if (await canLaunchUrl(uri)) {
// Launch the App
await launchUrl(
uri,
);
// and cancel the request
return NavigationActionPolicy.CANCEL;
}
}
return NavigationActionPolicy.ALLOW;
},
onLoadStop: (controller, url) async {
var result =
await controller.evaluateJavascript(source: "1 + 1");
print(result.runtimeType); // int
print(result); // 2
},
onReceivedError: (controller, request, error) {
pullToRefreshController?.endRefreshing();
},
onProgressChanged: (controller, progress) {
if (progress == 100) {
pullToRefreshController?.endRefreshing();
}
setState(() {
this.progress = progress / 100;
urlController.text = this.url;
});
},
onUpdateVisitedHistory: (controller, url, androidIsReload) {
setState(() {
this.url = url.toString();
urlController.text = this.url;
});
},
onConsoleMessage: (controller, consoleMessage) {
print(consoleMessage);
},
),
progress < 1.0
? LinearProgressIndicator(value: progress)
: Container(),
],
),
),
ButtonBar(
alignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
child: Icon(Icons.arrow_back),
onPressed: () {
webViewController?.goBack();
},
),
ElevatedButton(
child: Icon(Icons.arrow_forward),
onPressed: () {
webViewController?.goForward();
},
),
ElevatedButton(
child: Icon(Icons.refresh),
onPressed: () {
webViewController?.reload();
},
),
],
),
])));
}
}
@jyr
Blocked a frame with origin "http://localhost:63239" from accessing a cross-origin frame.
This is a web security feature that is built into the browser you view your app from (and every common web browser). See here:
initialUrlRequest: URLRequest(url: WebUri("https://flutter.dev")),
... since the iframe url is a different origin than your flutter app (flutter.dev
is different than localhost:63239
), you won't be able to do javascript communication across the barrier. If instead you loaded localhost:63239/some-other-page
into the web view, then the error would go away.
This is mentioned in the docs here (require the iframe to have the same origin of the website.
)
@jyr
Blocked a frame with origin "http://localhost:63239" from accessing a cross-origin frame.
This is a web security feature that is built into the browser you view your app from (and every common web browser). See here:
initialUrlRequest: URLRequest(url: WebUri("https://flutter.dev")),
... since the iframe url is a different origin than your flutter app (
flutter.dev
is different thanlocalhost:63239
), you won't be able to do javascript communication across the barrier. If instead you loadedlocalhost:63239/some-other-page
into the web view, then the error would go away.
Does works with inappwebview 6xxx? because I disabled the cors with:
flutter run -d chrome --web-browser-flag "--disable-web-security"
and same errors
Does works with inappwebview 6xxx?
The inappwebview version shouldn't have any effect on this behaviour (since all versions are implemented in Web with iframe
).
because I disabled the cors
I'm not sure what scope --disable-web-security
all has (how many security features it disables). It's not the typical CORS situation (at least from what I'm familiar with) because it's not a network request that's being blocked, it's javascript execution being blocked.
This also might help: a similar situation asked about on stack overflow
If instead you loaded
localhost:63239/some-other-page
into the web view, then the error would go away.
So, is it impossible to use a WebView with a external link? or Do I have to insert the external link in localhost:63239/some-other-page
and this page loading it in a WebView?
So, is it impossible to use a WebView with a external link?
In general, yes it's impossible (assuming you need javascript evaluations in the embedded page, and need to do web builds).
It's a browser security feature because evaluating arbitrary javascript would let you do things like steal passwords/tokens/cookies/etc. from the user.
For example, the attack could be:
The iframe goes to "google.com/signin", and on that page you enter your Google password to log in. If the Flutter app hosting the iframe can evaluate javascript inside the iframe, the Flutter app could inject something that observes keystrokes that happen inside the iframe, and then can send the password you entered to a hacker's server.
I'm kinda surprised the security feature isn't there for Android Webviews etc. But then again, nothing is stopping an app that is implementing its own "web browser" from skipping web security features.
There might be ways that the "external" page could allow certain data in/out (I would maybe look into things like "web messages"), but that would require the person developing that external page to do the implementation.
Using in Web platform with ^6.0.0-beta.23 version
In my case the HTML is local html injected with initialData In that case of local injected HTML (with initialData) is it possible to have communication between JS and Flutter ? are the 2 different piece of code (Flutter and JS) in the same origin ?
InAppWebView(
initialData: InAppWebViewInitialData(data: html, encoding: 'utf-8'),
onLoadStop: (controller, url) async {
var result = await controller.evaluateJavascript(source: "1 + 1");
print("===>${result.runtimeType}"); // int
print("===>Result: $result"); // 2
print("====>onLoadStop");
},
I get the same cross origin frame error
DOMException: Blocked a frame with origin "http://localhost:64428" from accessing a cross-origin frame.
at HTMLIFrameElement.<anonymous> (http://localhost:64428/assets/packages/flutter_inappwebview/assets/web/web_support.js:49:65)
web_support.js:80 DOMException: Blocked a frame with origin "http://localhost:64428" from accessing a cross-origin frame.
at HTMLIFrameElement.<anonymous> (http://localhost:64428/assets/packages/flutter_inappwebview/assets/web/web_support.js:57:61)
In that case of local injected HTML (with initialData) is it possible to have communication between JS and Flutter ?
I noticed the same problem in that situation, my workaround was to add the html as an asset file (where it gets hosted under the same origin at localhost:64428/assets/assets
). But that only works if your html content is static enough to put into a file.
/// for interactive JS, this URI needs to have the same origin as the flutter app (In Flutter Web builds, the assets folders will work for this).
/// however, in non-web builds, assets aren't hosted in URI's like this, so you'll need to use something else
static final WebUri htmlFileURI = WebUri.uri(Uri.base.replace(path: "/assets/assets/webview-content.html")); // note the double `assets/`, that's not a typo, it was actually needed
/// Instead of using `htmlFileURI`, you can use a string with the content of the HTML.
/// In Web builds, loading the WebView with this content doesn't seem to allow for interactive JS.
static final Future<String> htmlString = rootBundle.loadString("assets/webview-content.html");
/// If true we will use `htmlString` instead of `htmlFileURI`, for the content of the WebView.
static bool loadFromString = !isWeb;
Alternatively.... another way (that should work but I haven't gotten to work yet)... You might be able to set the starting page that the html gets injected into with baseUrl
in loadData
. I recently noticed that the default baseUrl is actually about:blank
instead of localhost
InAppWebView(
onWebViewCreated: (InAppWebViewController controller) async {
controller.loadData(
baseUrl: WebUri.uri(Uri.base),
data: html
@pichillilorenzo Any solution for this ?
The above is just returning null. I'm using flutter web.
if webview open a localhost site or a site where you deploy web. It will work fine. I tried to use open document through docs.google.com, but almost none of the features work. Currently, look like inappwebview is very limited for web platform
In this topic I found that
flutter_inappwebview
works on the web. I gave it a try and indeed the webview renders inside an<iframe>
, but I'm not able to implement JavaScript Communication.window.flutter_inappwebview.callHandler
("outside" the iframe, in the global scope) resolves withundefined
window. myObject
object resolves withundefined
when accessed from the "inside" of the iframe.I'm getting a bunch of errors in the console:
Is it expected? Is Javascript Communication supposed to be working already in the
develop
branch?