Repository: electron/electron (language: cpp)

Issue:
[Bug]: session.getBlobData never resolves with blob sizes > 65536
### Preflight Checklist

- [X] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) for this project.
- [X] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [X] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a bug report that matches the one I want to file, without success.

### Electron Version

17.0.0

### What operating system are you using?

macOS

### Operating System Version

macOS Monterey 12.3.1 (21E258)

### What arch are you using?

x64

### Last Known Working Electron version

N/A

### Expected Behavior

When running `session.getBlobData` on a blob UUID from an `onBeforeRequest` handler, I expect the resulting promise to resolve successfully to a buffer when the blob size is <<< RAM size. According to https://source.chromium.org/chromium/chromium/src/+/main:storage/browser/blob/blob_memory_controller.cc?q=CalculateBlobStorageLimitsImpl&ss=chromium, a blob of size 65537 should be handled correctly.

### Actual Behavior

When calling `session.getBlobData` on a blob with size > 65536, the resulting promise never resolves.

### Testcase Gist URL

https://gist.github.com/b3b9955084b145c77987e4dbc6c81078

### Additional Information

This issue appears to be a possible duplicate of https://github.com/electron/electron/issues/33081, though the other issue includes very little additional information, so I have filed a separate issue to be safe.

I did a bit of digging through the Electron glue source, and I found the following:

https://github.com/electron/electron/blob/61374019c03ab1af8584550277318ba7c2adb0ca/shell/browser/api/electron_api_session.cc#L725-L735
https://github.com/electron/electron/blob/c4ea33d1bf6faf8ca685e67e63b197d89efa9870/shell/browser/api/electron_api_data_pipe_holder.h#L33-L35

I suspect that this is related.


Discussion / hints:
I have also created https://gist.github.com/quantatic/795af4d293f28608145d47cbab6ae23f for a more interactive example of this bug.
Experiencing same issue
Electron 19.0.6 has this issue too.

How about this issue's progress?  

#6941

@deepak1556 
@quantatic 
How did you get around this issue?
> @quantatic How did you get around this issue?

I wasn't able to get around this issue, this part of my application is still blocked on this bug.
@quantatic 

I don't know why you need to use the `session.getBlobData` function.
If you want to read data from File or Blob object pice by pice,like me.
Maybe these codes help:

```ts
private static saveBigFile(file: File): Promise<string> {
    return new Promise(async (resolve, reject) => {
      let imgDir = await this.getDataPath("assets");
      let imgPath = path.join(imgDir, crypto.randomUUID() + path.extname(file.name));
      let fileStream = fs.createWriteStream(imgPath);
      let reader = new FileReader();
      let loaded = 0;
      let piceSize = 10240; //1片
      let readPice = (start) => {
        let tar = file.slice(start, start + piceSize);
        reader.readAsArrayBuffer(tar);
      };
      let writePice = (data: buffer.Buffer) => {
        return new Promise((resolve, reject) => {
          let cb = (error) => {
            if (error) reject(error);
            else resolve(true);
          };
          if (!fileStream.write(data)) {
            fileStream.once("drain", cb);
          } else {
            process.nextTick(cb);
          }
        });
      };
      fileStream.once("finish", () => {
        resolve(imgPath);
      });
      reader.onload = async (e) => {
        let arrBuffer = e.target.result as ArrayBuffer;
        let data = buffer.Buffer.from(arrBuffer);
        await writePice(data);
        loaded += e.loaded;
        if (loaded < file.size) {
          readPice(loaded);
        } else {
          fileStream.end();
        }
      };
      readPice(loaded);
    });
  }
```


@xland Certain Electron APIs (such as https://www.electronjs.org/docs/latest/api/structures/upload-data) only return blob UUIDs in certain situations. How do you advise getting the data from simply the blob UUID contained in `UploadData`?
I'm not a member of the electron team,
And I don't konw how to fix this issue,
I just want help.
I don't konw why @microwugui dislike my answer.

@quantatic 
I can't get the data of the `UploadData` object by `blobUUID` too.
In my case,I need to read a big file pice by pice
And the `File` object doesn't have path property (Print screen and paste into the electron app) 
I tried to post the file to the main process, and use `getBlobData` to read the data, I failed.
So I have to read the data by `slice` and `readAsArrayBuffer` method.
That's all.

> I'm not a member of the electron team, And I don't konw how to fix this issue, I just want help. I don't konw why @microwugui dislike my answer.
> 
> @quantatic I can't get the data of the `UploadData` object by `blobUUID` too. In my case,I need to read a big file pice by pice And the `File` object doesn't have path property (Print screen and paste into the electron app) I tried to post the file to the main process, and use `getBlobData` to read the data, I failed. So I have to read the data by `slice` and `readAsArrayBuffer` method. That's all.

We are not talking about your case, so I dislike your helpless comment

In my case, I can't get `interceptBufferProtocol` work as I expect because `getBlobData` not returned on large data

We need to focus on the issue not any other workaround, thx
@microwugui 
As we know, this issue has been there for six years. #6941
And I think they're not going to fix it soon.
Someone whould get help by my comment.
Of course not you.


> @microwugui As we know, this issue has been there for six years. #6941 And I think they're not going to fix it soon. Someone whould get help by my comment. Of course not you.

Neither the author of this issue (quantatic) nor I could get help with your comment

Why not create a separate issue to talk about your solution, for newbies who don't know `fs` module can read file from disk ?
@microwugui 

This is my last reply.

When you cut screen and paste to the electron app.
The `File` object doesn't have a `path` property.
Without file's path, developer cann't read the file's data by `fs` module.
(Don't you know this? newbie.)



> @microwugui
> 
> This is my last reply.
> 
> When you cut screen and paste to the electron app. The `File` object doesn't have a `path` property. Without file's path, developer cann't read the file's data by `fs` module. (Don't you know this? newbie.)

So which piece of your comment is help to solve `getBlobData` not returned issue?

Is `File` object undocumented that deserve to notice on this irrelevant issue?

I can't get any help of this issue, so I vote down your useless comment, and will not trying to notice you focus on it

Fix patch:
diff --git a/shell/browser/api/electron_api_data_pipe_holder.cc b/shell/browser/api/electron_api_data_pipe_holder.cc
index f33f1d9b85b99..67fe81959a12f 100644
--- a/shell/browser/api/electron_api_data_pipe_holder.cc
+++ b/shell/browser/api/electron_api_data_pipe_holder.cc
@@ -86,8 +86,11 @@ class DataPipeReader {
     if (result == MOJO_RESULT_OK) {  // success
       remaining_size_ -= length;
       head_ += length;
-      if (remaining_size_ == 0)
+      if (remaining_size_ == 0) {
         OnSuccess();
+      } else {
+        handle_watcher_.ArmOrNotify();
+      }
     } else if (result == MOJO_RESULT_SHOULD_WAIT) {  // IO pending
       handle_watcher_.ArmOrNotify();
     } else {  // error
diff --git a/spec/api-session-spec.ts b/spec/api-session-spec.ts
index 1aa8fdc6fd343..019317a510095 100644
--- a/spec/api-session-spec.ts
+++ b/spec/api-session-spec.ts
@@ -529,6 +529,50 @@ describe('session module', () => {
     });
   });
 
+  describe('ses.getBlobData2()', () => {
+    const scheme = 'cors-blob';
+    const protocol = session.defaultSession.protocol;
+    const url = `${scheme}://host`;
+    after(async () => {
+      await protocol.unregisterProtocol(scheme);
+    });
+    afterEach(closeAllWindows);
+
+    it('returns blob data for uuid', (done) => {
+      const content = `<html>
+                       <script>
+                       let fd = new FormData();
+                       fd.append("data", new Blob(new Array(65_537).fill('a')));
+                       fetch('${url}', {method:'POST', body: fd });
+                       </script>
+                       </html>`;
+
+      protocol.registerStringProtocol(scheme, (request, callback) => {
+        try {
+          if (request.method === 'GET') {
+            callback({ data: content, mimeType: 'text/html' });
+          } else if (request.method === 'POST') {
+            const uuid = request.uploadData![1].blobUUID;
+            expect(uuid).to.be.a('string');
+            session.defaultSession.getBlobData(uuid!).then(result => {
+              try {
+                const data = new Array(65_537).fill('a');
+                expect(result.toString()).to.equal(data.join(''));
+                done();
+              } catch (e) {
+                done(e);
+              }
+            });
+          }
+        } catch (e) {
+          done(e);
+        }
+      });
+      const w = new BrowserWindow({ show: false });
+      w.loadURL(url);
+    });
+  });
+
   describe('ses.setCertificateVerifyProc(callback)', () => {
     let server: http.Server;
 


Test patch: