添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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 .

  • 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.
  • I'm getting a bunch of errors in the console:

    DOMException: Blocked a frame with origin "http://localhost:5005" from accessing a cross-origin frame.
        at HTMLIFrameElement.<anonymous> (http://localhost:5005/packages/flutter_inappwebview/assets/web/web_support.js:46:65)
    web_support.js:77 DOMException: Blocked a frame with origin "http://localhost:5005" from accessing a cross-origin frame.
        at HTMLIFrameElement.<anonymous> (http://localhost:5005/packages/flutter_inappwebview/assets/web/web_support.js:54:61)
    web_support.js:191 DOMException: Blocked a frame with origin "http://localhost:5005" from accessing a cross-origin frame.
        at HTMLIFrameElement.<anonymous> (http://localhost:5005/packages/flutter_inappwebview/assets/web/web_support.js:81:74)
    web_support.js:227 DOMException: Blocked a frame with origin "http://localhost:5005" from accessing a cross-origin frame.
        at HTMLIFrameElement.<anonymous> (http://localhost:5005/packages/flutter_inappwebview/assets/web/web_support.js:197:59)
    

    Is it expected? Is Javascript Communication supposed to be working already in the develop branch?

    👋 @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 should be 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'}))).

    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(
                            // 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();
            ])));
    

    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.)

    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.

    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