Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

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

[dart:html] IFrameElement.contentWindow does not have a property document #49626

Open
insinfo opened this issue Aug 9, 2022 · 10 comments
Open
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-html

Comments

@insinfo
Copy link

insinfo commented Aug 9, 2022

IFrameElement.contentWindow does not have a property document

workaround

 var write = js.JsObject.fromBrowserObject(iframeForPrint)['contentWindow']
        ['document'] as js.JsObject;

    write.callMethod(
        'write', ['<h1>Injected from parent frame</h1>']);

    await Future.delayed(Duration(milliseconds: 200));

    var print = js.JsObject.fromBrowserObject(iframeForPrint)['contentWindow']
        ['print'] as js.JsFunction;
    print.apply(<dynamic>[]);

Dart SDK version: 2.12.4 (stable) (Thu Apr 15 12:26:53 2021 +0200) on "windows_x64"

@lrhn lrhn added area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-html labels Aug 9, 2022
@srujzs
Copy link
Contributor

srujzs commented Aug 9, 2022

Thanks for filing! Instead of dart:js, prefer package:js and js_util to workaround this. See https://github.com/dart-lang/sdk/blob/main/sdk/lib/html/doc/WORKAROUNDS.md for details on how to use interop to workaround this.

@insinfo
Copy link
Author

insinfo commented Aug 11, 2022

@srujzs

How can I make an extension on IFrameElement to get document from an iframe?

I tried something like this but it doesn't work

@JS()
library workarounds;

import 'dart:html';

import 'package:js/js.dart';
import 'package:js/js_util.dart';

//WindowBase from contentWindow FrameElement
extension JSWindowBaseExtension on WindowBase {
  //external Document get document;
  // Document get document => JS('Document', '#.document', this);

  // external dynamic get document; // => getProperty(this, 'document');

  Document get document => JS2('Document', '#.document', this);
}

external T JS2<T>(String typeDescription, String codeTemplate,
    [arg0,
    arg1,
    arg2,
    arg3,
    arg4,
    arg5,
    arg6,
    arg7,
    arg8,
    arg9,
    arg10,
    arg11,
    arg12,
    arg13,
    arg14,
    arg51,
    arg16,
    arg17,
    arg18,
    arg19]);


 print(iframeForPrint.contentWindow.document);

@srujzs
Copy link
Contributor

srujzs commented Aug 11, 2022

The JS foreign function you seem to be trying to use (JS('Document', '#.document', this)) is not meant for end users. It's only meant for internal libraries as it's quite powerful, and therefore, quite dangerous to use.

It seems you're adding an extension onto a dart:html @Native class (WindowBase). I'd avoid that as that could lead to collisions between extension members and instance members if the class ever changes (although, external Document get document should work, so it's weird that it doesn't). I'd do something like the following:

import 'dart:html';

import 'package:js/js.dart';

@JS()
@staticInterop
class JSWindowBase {}

extension E on WindowBase {
  // Add any missing members here.
  external Document get document;
}

void main() {
  var iframeElement = ...; // Some IFrameElement from somewhere.
  var document = (iframeElement.contentWindow as JSWindowBase).document;
}

Or, since you only care about one missing member, it's way simpler to do:

import 'dart:html';
import 'dart:js_util';

void main() {
  var iframeElement = ...; // Some IFrameElement from somewhere.
  var document = getProperty(iframeElement, 'document');
}

FYI as well - you could probably just cast the result of contentWindow to Window to get document instead of doing any interop. https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/contentWindow claims it always returns a Window object.

@insinfo
Copy link
Author

insinfo commented Aug 11, 2022

@srujzs
none of the alternatives you posted works. is there another way to make a function that returns the Document instance of an IframeElement?

      //this not work
      html.Document doc = js_util.getProperty(iframeForPrint, 'document');
      print(doc);
      //print null

      //this not work
      html.Document doc = js_util.getProperty(iframeForPrint.contentWindow, 'document');
      print(doc);
      //return null

      //this not work
      html.Window win = iframeForPrint.contentWindow;
      //   throw Exception Expected a value of type 'Window', but got one of type '_DOMWindowCrossFrame'...

this not work

@JS()
library workarounds;

import 'dart:html';

import 'package:js/js.dart';

//this not work Could not resolve "@staticInterop"
@JS()
@staticInterop
class JSWindowBase {}

extension E on WindowBase {
  external Document get document;
}

//this not work 
extension JSWindowBaseExtension2 on WindowBase {
  external Document get document;

  //print(iframeForPrint.contentWindow.document.documentElement.innerHtml);
  //NoSuchMethodError: tried to call a non-function, such as null: 'dart.global.JSWindowBaseExtension|get#document'
}

image

@insinfo
Copy link
Author

insinfo commented Aug 11, 2022

The JS foreign function you seem to be trying to use (JS('Document', '#.document', this)) is not meant for end users. It's only meant for internal libraries as it's quite powerful

there had to be a way to use this function, as there's a lot of stuff missing from the dart:html

@srujzs
Copy link
Contributor

srujzs commented Aug 11, 2022

The js_util case was a typo, it should be js_util.getProperty(iframeForPrint.contentWindow, 'document'); as you note. Does it return null because the value is actually null, or does it return null, because it's undefined? You can check with hasProperty(iframeForPrint.contentWindow, 'document') to determine if we have the right type. I'm struggling to understand why it fails to fetch the document if it was working with dart:js. For sanity checking, I suppose you could just do getProperty(getProperty(iframeForPrint, 'contentWindow'), 'document').

Can you share a minimal example that I can repro, here?

html.Window win = iframeForPrint.contentWindow;
// throw Exception Expected a value of type 'Window', but got one of type '_DOMWindowCrossFrame'...

It seems like the window that is returned is from another frame, so it makes sense why that cast to html.Window does not work. Ignore this alternative.

//this not work Could not resolve "@staticInterop"

@staticInterop is in package:js version 0.6.4 - you might need to modify your pubspec. Although, you're using version 2.12 for the SDK, so I'm not sure if it'll version resolve. If it does not, just use js_util for now and ignore this alternative.

there had to be a way to use this function, as there's a lot of stuff missing from the dart:html

Our hope is that interop should offer a lot of the missing functionality (and if not, we should determine why and try and fix it). Arbitrary JS code execution can be dangerous and hard to reason about, so there aren't any plans to release the JS foreign function to users.

@insinfo
Copy link
Author

insinfo commented Aug 12, 2022

@srujzs
I created a minimal example

https://github.com/insinfo/teste_iframe

@insinfo
Copy link
Author

insinfo commented Aug 12, 2022

i can't use js 0.6.4 package

The current Dart SDK version is 2.12.4.

Because teste_iframe depends on js >=0.6.4 which requires SDK version >=2.16.0-100.0.dev <3.0.0, version solving failed.
exit code 1

@insinfo
Copy link
Author

insinfo commented Mar 8, 2023

I am trying to build a print solution which programmatically prints PDF documents using URLs and fire an event after printing.

in javascript it works perfect but in dart not

<script>
    window.onload = function () {
      var debug = { hello: "world" };
      var blob = new Blob([JSON.stringify(debug, null, 2)], { type: 'application/json' });
      var src = URL.createObjectURL(blob);

      printFrame = document.createElement('iframe');
      printFrame.id = 'print-frame';
      // printFrame.style.display = 'none';
      printFrame.src = src;
      printFrame.onload = function () {
        var mediaQueryList = printFrame.contentWindow.matchMedia('print');
        mediaQueryList.addListener(function (mql) {
          console.log('print event', mql);
          //alert('print event');
        });

        printFrame.contentWindow.addEventListener('afterprint', (event) => {
          console.log('After print');
        });

        setTimeout(function () {
          printFrame.contentWindow.print();
        }, 0);
      }
      document.body.appendChild(printFrame);

    }
  </script>

https://jsfiddle.net/anhhnt/nj851e52/

var debug = {'hello': "world"};
    var blob = Blob([jsonEncode(debug)], 'application/json');
    //final blob = Blob([bytes], type);
    final url = Url.createObjectUrlFromBlob(blob);
    IFrameElement? printFrame =
        document.getElementById('printFrame') as IFrameElement?;

    if (printFrame == null) {
      printFrame = IFrameElement();
      printFrame.src = url;
      printFrame.id = 'printFrame';
      document.body!.append(printFrame);
    }
    printFrame.style.display = 'none';
    printFrame.onLoad.listen((event) {
      // print('printFile');
      var printPage =
          js.JsObject.fromBrowserObject(printFrame!)['contentWindow']['print']
              as js.JsFunction;
      printPage.apply(<dynamic>[]);

      // printFrame.contentWindow!.addEventListener('onafterprint', (event) {
      //   print('onafterprint');
      // });
      var contentWindow = printFrame.contentWindow as JSWindowBase;
      
      var mediaQueryList = contentWindow.matchMedia('print');

      mediaQueryList.addListener((mql) {
        print('print event $mql');
       
      });
    });


@JS()
library workarounds;

import 'dart:html';

import 'package:js/js.dart';
//import 'package:js/js_util.dart';

//https://github.com/dart-lang/sdk/issues/49626
@JS()
@staticInterop
class JSWindowBase {}

extension E on JSWindowBase {
  // Add any missing members here.
  external Document get document;
  external MediaQueryList matchMedia(String query);
}

image

@insinfo
Copy link
Author

insinfo commented Mar 8, 2023

this works in Google Chrome but not in Firefox

 var printPage = js.JsObject.fromBrowserObject(printFrame)['contentWindow']
          ['print'] as js.JsFunction;
      printPage.apply(<dynamic>[]);
EXCEPTION: SecurityError: Permission denied to access property Symbol("_is__DartObject") on cross-origin object
STACKTRACE: 
dart:sdk_internal 56798:17                                                          _get
package:new_sali_frontend/src/shared/utils/frontend_utils.dart 130:63               <fn>
dart:sdk_internal 104087:100                                                        <fn>
package:ngdart/src/core/zone/ng_zone.dart 122:18                                    <fn>
dart:sdk_internal 34765:14                                                          runUnary
package:ngdart/src/core/zone/ng_zone.dart 119:18                                    [_runUnary]
dart:sdk_internal 103888:16                                                         listen
package:new_sali_frontend/src/shared/utils/frontend_utils.dart 129:15               printFile
package:new_sali_frontend/src/modules/protocolo/services/gera_pdf_guia.dart 198:17  geraGuiaEncaminhamento
dart:sdk_internal 35575:33                                                          <fn>
package:ngdart/src/core/zone/ng_zone.dart 122:18                                    <fn>
dart:sdk_internal 34765:14                                                          runUnary
package:ngdart/src/core/zone/ng_zone.dart 119:18                                    [_runUnary]
dart:sdk_internal 31163:35                                                          <fn>
package:ngdart/src/core/zone/ng_zone.dart 84:11                                     safeMicrotask
package:ngdart/src/core/zone/ng_zone.dart 105:18                                    <fn>
dart:sdk_internal 34759:14                                                          run
package:ngdart/src/core/zone/ng_zone.dart 102:18                                    [_run]
dart:sdk_internal 34803:14                                                          scheduleMicrotask
package:ngdart/src/core/zone/ng_zone.dart 93:11                                     [_scheduleMicrotask]
dart:sdk_internal 30578:36                                                          complete
package:http/src/browser_client.dart 56:16                                          <fn>
package:ngdart/src/core/zone/ng_zone.dart 122:18                                    <fn>
dart:sdk_internal 34765:14                                                          runUnary
package:ngdart/src/core/zone/ng_zone.dart 119:18                                    [_runUnary]
dart:sdk_internal 104044:106                                                        <fn>
package:ngdart/src/core/zone/ng_zone.dart 122:18                                    <fn>
dart:sdk_internal 34765:14                                                          runUnary

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-html
Projects
None yet
Development

No branches or pull requests

3 participants