cburgmer / rasterizeHTML.js

Renders HTML into the browser's canvas
http://cburgmer.github.io/rasterizeHTML.js
MIT License
2.45k stars 219 forks source link

"...document's frame is sandboxed and the 'allow-scripts' permission is not set" #117

Open hugolpz opened 8 years ago

hugolpz commented 8 years ago

Report

Using CSScritics, I get the error :

Blocked script execution in 'http://solar-druid.codio.io/tests/csscritic.html' because the document's frame is sandboxed and the 'allow-scripts' permission is not set. . h.1.e.calculateDocumentContentSize @ rasterizeHTML.allinone.js:12 . h.1.f.drawDocumentAsSvg @ rasterizeHTML.allinone.js:12 . h.1.k @ rasterizeHTML.allinone.js:12 . (anonymous function) @ rasterizeHTML.allinone.js:12 . (anonymous function) @ rasterizeHTML.allinone.js:12

2015 12 29_csscritic_sanboxed_frame

Resolution hints

Fortunately, Stackoverflow helps. See Why am I getting a blocked script execution error?

This error message (introduced here) is displayed when an <iframe> element with sandbox attribute is attempting to run JavaScript (only allowed when allow-scripts is specified in that attribute). A look at the rasterizeHTML source code shows that the function createHiddenSandboxedIFrame() creates such a frame. It is used for calculateDocumentContentSize(), the contents of the document are being copied into the temporary sandboxed frame in order to measure their size. Apparently, that document also contains some <script> tags and these cause the error (not because of the script origin but because scripts are generally disallowed).

Reproduce bug

I got this bug using Chromium 45.0.2454.101 Ubuntu 15.04 (64-bit), upon ./test/csscritic.html.

The tested page is ./index.html. There is what you should see if all right (on first connexion) : 2015-12-29_mathador_ui_home

(+ Hi Christopher, long time not seen !)

edouard-lopez commented 8 years ago

I get screenshots on Firefox and no error.

cc @cburgmer

cburgmer commented 7 years ago

Hey sorry for being silent on this. At some point it must have slipped just right under the other issues.

Disabling the script execution is done on purpose. When we are measuring the dimensions, the script content has already been executed and it's effects on the DOM have already been recorded. As the script tags are still there however, we want to avoid that they are executed yet again. Sadly Chrome and Safari complain loudly.

I'd like to clean this up, be don't see a way. Removing the sandboxing as described is not an option :(

inuyashamx commented 7 years ago

Change iframe.sandbox = 'allow-same-origin';

for

iframe.sandbox = 'allow-same-origin allow-scripts allow-popups allow-forms';

in src/browser.js

change

cburgmer commented 7 years ago

Hey, could you let me know what problem you are facing? As mentioned above, this sandbox setting is intentional, so that JavaScript is not executed twice.

richardsonvix commented 7 years ago

I'm getting the same error message, but my page is simple, just with table tags and divs.

cburgmer commented 7 years ago

@richardsonvix If there's no JS involved, and you are seeing this, please consider posting an example (jsfiddle or something similar maybe). As mentioned above, rasterizeHTML.js is using the sandboxing property by design, which sadly triggers this (unwanted) log.

richardsonvix commented 7 years ago

Hi @cburgmer, I can´t create a case in jsfiddle, it´s a dynamic generated page (ASP.NET MVC), but using simple tags (tables, divs, ...) loading .js and .css by bundles (css using classes). Some contents like table rows are created dynamically, also some css classes change for tds.

I can save HTML to evaluate and a screenshot, would it be possible to check only with this?

cburgmer commented 7 years ago

Have a try and see whether this still triggers the behaviour.

richardsonvix commented 7 years ago

I´ll try again when back to this task in my work.

Thank you.

devellopah commented 5 years ago

same error here, i am on win10, rasterizehtml version is 1.3.0 seems like the error occures in webkit based browsers(chrome, opera, et cetera) works perfectly fine in firefox

dunno how to hadle it, please help.

rasterizehtml_error

cburgmer commented 5 years ago

Hey Islam, can I assume that the HTML you are trying to render includes references to external scripts?

The way this library works with scripts is to inline them before injecting them into the sandboxed iframe. So while this enables scripts to run, it will likely fail for your script, as it is sourced from a foreign origin and rasterizeHTML.js' approach with loading the script via AJAX then fails.

But happy to have a look at an example (maybe on jsfiddle or a similar site).

devellopah commented 5 years ago

I cannot provide full example, but i can share relevant parts

<template>
...
    <div class="col-sm-6">
      <div class="pdf-container">
      <div class="pdf-loader-container">
          <img src="../../img/loader.gif" alt="loader" class="pdf-loader">
      </div>
        <object
          id="pdf_viewer"
          :data="[pdf ? pdf : intro]"
          width="100%"
          height="100%"
        ></object>
      </div>
    </div>
...
</template>
<script>
import * as rasterizeHTML from 'rasterizehtml'
import jsPDF from 'jspdf'
import _ from 'lodash'
import proximanova from '../fonts'

export default {
  name: 'Generator',
  components: {},

  data: () => ({
    text: 'Сообщение для учеников',
    pdf: null,
  }),
  computed: {
    textComputed: {
      get() { return this.text },
      set: _.debounce(newValue => this.text = newValue, 3000)
    },
    intro() {
        const pdf = new jsPDF()
        pdf.addFileToVFS(proximanova.regular.ttf, proximanova.regular.base64)
        pdf.addFont(proximanova.regular.ttf, proximanova.name, proximanova.regular.weight)
        pdf.setFont(proximanova.name)
        pdf.setFontSize(16)
        pdf.text(this.textComputed, 10, 10)
        return pdf.output('datauristring')
    },
  },

  methods: {
    run() {
      Promise.resolve(true)
        .then(() => this.generateTasks())
        .then(tasks => this.drawHTML(tasks))
    },
    generateTasks() {
      const { quantity } = this.options
      const tasks = []

      for (let i = 0; i < quantity; i++) {
        tasks.push(this.getTask())
      }

      console.log('tasks', tasks)
      return tasks
    },
    createTaskHtml(list) {
      return `
        <div style="max-width: 435px; margin: auto;">
            <div style="display: flex; justify-content: center; flex-wrap: wrap">
                ${list
                  .map(task => {
                    return `
                        <div style="width: 20%; text-align: center; margin-bottom: 25px;">
                            <div style=" border: 1px solid #333; width: 60px; color: #333; font-size: 18px; line-height: 25px; font-weight: 400; display: inline-block;">
                            ${task
                              .map(num => {
                                return `
                                    <div style="border-bottom: 1px solid #333;">${num}</div>
                                `.trim()
                              })
                              .join('')}
                            <div style="font-size: 0 !important;">:)</div>
                            </div
                            >
                        </div>`.trim()
                  })
                  .join('')}
            </div>
        </div>
            `
    },

    async createCanvas(html) {
      const canvas = document.createElement('canvas')

      canvas.width = 435
      canvas.height = 900

      await rasterizeHTML.drawHTML(html, canvas)

      return canvas
    },

    drawHTML(tasks) {

      const { nums } = this.options
      let k

      if ([6, 7, 8].indexOf(nums) !== -1) {
        k = 15
      } else if ([9, 10].indexOf(nums) !== -1) {
        k = 10
      } else {
        k = 20
      }

      console.log('k', k)

      const promises = _.chunk(tasks, k)
        .map(this.createTaskHtml)
        .map(this.createCanvas)

      Promise.all(promises).then(canvas => this.generatePDF(canvas))
    },
    generatePDF(canvas) {
      const pdf = new jsPDF('p', 'pt', 'a4')

      pdf.addFileToVFS(proximanova.regular.ttf, proximanova.regular.base64)
      pdf.addFont(proximanova.regular.ttf, proximanova.name, proximanova.regular.weight)
      pdf.setFont(proximanova.name)
      pdf.setFontSize(16)

      pdf.text(this.textComputed, 30, 30)

      canvas.forEach((image, i) => {
        !!i && pdf.addPage() // если не первый элемент, добавляем новую страницу в документ
        pdf.addImage(image.toDataURL(), 'PNG', 80, 80, image.width, image.height) // добавляем изображение на страницу
      })
      this.pdf = pdf.output('datauristring')
    },
  }
}
</script>