sdk icon indicating copy to clipboard operation
sdk copied to clipboard

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

Open insinfo opened this issue 3 years ago • 1 comments

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"

insinfo avatar Aug 09 '22 21:08 insinfo

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.

srujzs avatar Aug 09 '22 22:08 srujzs

@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);

insinfo avatar Aug 11 '22 14:08 insinfo

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.

srujzs avatar Aug 11 '22 17:08 srujzs

@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 avatar Aug 11 '22 20:08 insinfo

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

insinfo avatar Aug 11 '22 20:08 insinfo

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.

srujzs avatar Aug 11 '22 21:08 srujzs

@srujzs I created a minimal example

https://github.com/insinfo/teste_iframe

insinfo avatar Aug 12 '22 14:08 insinfo

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 avatar Aug 12 '22 14:08 insinfo

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 avatar Mar 08 '23 21:03 insinfo

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

insinfo avatar Mar 08 '23 21:03 insinfo