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

getTypeOf() Can't infer datatype in Firefox Addon context #759

Open
JadeMaveric opened this issue Apr 11, 2021 · 3 comments
Open

getTypeOf() Can't infer datatype in Firefox Addon context #759

JadeMaveric opened this issue Apr 11, 2021 · 3 comments

Comments

@JadeMaveric
Copy link

I'm trying to save a bunch of image blob (from canvas.toBlob) into a zip for an addon I'm working on.
But I keep getting the Can’t read the data of […]. Is it in a supported JavaScript type error.
Here's the code block I'm using

    Promise.all(promises)
    .then( blobs => {
        blobs.forEach((blob, i) => {
            console.log("Insert image", blob);
            zip.file(`Frame ${i}.jpg`, blob, {base64: true});
        });
        
        console.log("Zipped Files", zip.files);

        zip.generateAsync({type:"blob"})
        .then( blob => {
            console.log("Saving zip...")
            saveAs(blob, "gMeetSlides.zip")
        })
        .catch( err => console.log(err));
    })

In what seems like a repetition of #151 this is the error I get

Error: Can't read the data of 'Frame 0.jpg'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?
    prepareContent moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/lib/jszip.js:3488
    promise callback*[32]</exports.prepareContent moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/lib/jszip.js:3483
    fileAdd moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/lib/jszip.js:1416
    file moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/lib/jszip.js:1572
    downloadZip moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/capture.js:121
    downloadZip moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/capture.js:119
    promise callback*downloadZip moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/capture.js:118
    <anonymous> moz-extension://d3aaac9d-8a41-48a0-9e3c-d5a52f9b1f75/content_scripts/slideshot/capture.js:163

Which leads me to exports.getTypeOf. I added some console logs to it, and this is the result

        exports.getTypeOf = function(input) {
        console.log("Input", input);
        console.log("Blob", input instanceof Blob);
        console.log("Array", input instanceof ArrayBuffer);

2 calls, seems legit, there are 2 images being zipped. (in all trials)

Input ArrayBuffer { byteLength: 12666 }        jszip.js:3361:17
Blob false                                     jszip.js:3362:17
Array false                                    jszip.js:3363:17
Input ArrayBuffer { byteLength: 12666 }        jszip.js:3361:17
Blob false                                     jszip.js:3362:17
Array false                                    jszip.js:3363:17
Error: Can't read the data...

According to this stackoverflow comment it might be better to use input.constructor.name instead of instanceof. I using that in the function blocks, but while that doesn't throw an error, I don't get the download prompt either (not the console.logs in the callback function)

        if (support.uint8array && input.constructor.name === "Uint8Array") {
            return "uint8array";
        }
        if (support.arraybuffer && input.constructor.name === "ArrayBuffer") {
            return "arraybuffer";
        }

Multiple calls, each with a different type!?

Input ArrayBuffer { byteLength: 12664 }   jszip.js:3361:17
Blob false                                jszip.js:3362:17
Array false                               jszip.js:3363:17
Input ArrayBuffer { byteLength: 12664 }   jszip.js:3361:17
Blob false                                jszip.js:3362:17
Array false                               jszip.js:3363:17
Input Uint8Array(12664) [ 255, 216, … ]   jszip.js:3361:17
Blob false                                jszip.js:3362:17
Array false                               jszip.js:3363:17
Input ArrayBuffer { byteLength: 12664 }   jszip.js:3361:17
Blob false                                jszip.js:3362:17
Array false                               jszip.js:3363:17
Input ArrayBuffer { byteLength: 12664 }   jszip.js:3361:17
Blob false                                jszip.js:3362:17
Array false                               jszip.js:3363:17
Input Uint8Array(12664) [ 255, 216, … ]   jszip.js:3361:17
Blob false                                jszip.js:3362:17
Array false                               jszip.js:3362:17

I also tried some console.logs to checkout the return value, and the output is even more confusion

    exports.getTypeOf = function(input) {
        console.log("Input", input);
        console.log("Blob", input instanceof Blob);
        console.log("Array", input instanceof ArrayBuffer);
        let _type = undefined;
        if (typeof input === "string") {
            _type = "string";
        }
        if (Object.prototype.toString.call(input) === "[object Array]") {
            _type = "array";
        }
        if (support.nodebuffer && nodejsUtils.isBuffer(input)) {
            _type = "nodebuffer";
        }
        if (support.uint8array && input.constructor.name === "Uint8Array") {
            _type = "uint8array";
        }
        if (support.arraybuffer && input.constructor.name === "ArrayBuffer") {
            _type = "arraybuffer";
        }
        console.log("Constr Type", input.constructor.name);
        console.log("Return Type", type);
        return _type;
    };

Only 1 call to the function (a Blob?)

Input Blob { size: 12671, type: "image/jpeg" }     jszip.js:3361:17
Blob true                                          jszip.js:3362:17
Array false                                        jszip.js:3363:17
Constr Type undefined                              jszip.js:3380:17
@JadeMaveric
Copy link
Author

The addon works as expected on chrome though. Which is ironic, since I'm developing primarily with firefox in mind

@Tithen-Firion
Copy link

This issue is easy to reproduce with a UserScript. Install Greasemonkey or Violentmonkey in Firefox. Then install this script:

// ==UserScript==
// @name        test
// @version     1
// @include     https://github.com/*
// @grant       none
// @require     https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.js
// ==/UserScript==

(async () => {
  const zip = new JSZip();
  const blob = await zip.generateAsync({type:'blob'});

  try {
    zip.file('test.txt', new Blob(['']));
  }
  catch(error) {
    console.error(error);
  }
  try {
    await JSZip.loadAsync(blob);
  }
  catch(error) {
    console.error(error);
  }
})();

Adding blob to a zip throws Error: Can't read the data of 'test.txt'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?. Loading zip from blob throws Error: Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?.

Both can be fixed with either #578 or #598. But then loading zip from blob throws Error: Permission denied to access property "constructor". Caused by this line:

var result = this.data.subarray(this.zero + this.index, this.zero + this.index + size);

And I'm not sure how to fix that.

WORKAROUND

Since I don't know when it's going to be fixed and in my case those PRs don't solve my problem I'm using this workaround:

const readAsBinaryString = blob => new Promise(resolve => {
  const reader = new FileReader();
  reader.onload = function(event) {
    resolve(event.target.result);
  };
  reader.readAsBinaryString(blob);
});

(async () => {
  const zip = new JSZip();
  const blob = await zip.generateAsync({type:'blob'});
  zip.file('test.txt', await readAsBinaryString(new Blob([''])), {binary: true});
  await JSZip.loadAsync(await readAsBinaryString(blob));
})();

And now it works.

Related issue greasemonkey/greasemonkey#3120

@ethernidee
Copy link

ethernidee commented Dec 6, 2023

Faced the same problem in Firefox web extension content script.

const encodedText = new TextEncoder().encode('some text'); // ok
const buffer = new Uint8Array(encodedText.buffer); //  ok, but seems like some security flags for new object are missing
buffer.slice(); // Error: Permission denied to access property "constructor"
buffer.subarray(0, 2); // Error: Permission denied to access property "constructor"

Creating new ArrayBufferView from existing ArrayBuffer in web extension content script leads to Error: Permission denied to access property "constructor" on any method call for new object.

Created a bug report here: https://bugzilla.mozilla.org/show_bug.cgi?id=1868675

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants