0

I am trying to do cross extension message passing between chrome extension and chrome app according to this article. But I am not sure that how to do it correctly. I used background js to receive and send messages. But no clue whether it is working or not. Actually I want to save file from chrome extension, since it cannot be done I thought this could work. So any idea or suggestion or example is highly welcome.

I have go through many alternatives as also appears in this question. Then one of answer points this example. I found that this example is works fine. I hope that I could use this mechanism to save file using Chrome App's fileSystem API.

Community
  • 1
  • 1
Ruwanka De Silva
  • 3,555
  • 6
  • 35
  • 51

1 Answers1

1

The Chrome messaging APIs can only transfer JSON-serializable values. If the files are small, then you could just read the file content using FileReader in the extension, send the message over the external messaging channel to the Chrome App, then save the data using the FileWriter API.

When the files are big, read the file in chunks using file.slice(start, end) then follow the same method as for small files.

Extension:

var app_id = '.... ID of app (32 lowercase a-p characters) ....';
var file = ...; // File or Blob object, e.g. from an <input type=file>

var fr = new FileReader();
fr.onload = function() {
    var message = {
        blob: fr.result,
        filename: file.name,
        filetype: file.type
    };
    chrome.runtime.sendMessage(app_id, message, function(result) {
        if (chrome.runtime.lastError) {
            // Handle error, e.g. app not installed
            console.warn('Error: ' + chrome.runtime.lastError.message);
        } else {
            // Handle success
            console.log('Reply from app: ', result);
        }
    });
};
fr.onerror = function() { /* handle error */ };
// file or sliced file.
fr.readAsText(file);

App:

chrome.runtime.onMessageExternal.addListener(
  function(message, sender, sendResponse) {
    // TODO: Validate that sender.id is allowed to invoke the app!

    // Do something, e.g. convert back to Blob and do whatever you want.
    var blob = new Blob([message.blob], {type: message.filetype});

    console.log('TODO: Do something with ' + message.filename + ':', blob);

    // Do something, e.g. reply to message
    sendResponse('Processed file');
    // if you want to send a reply asynchronously, uncomment the next line.
    // return true;
});

EDIT: Although the following method using sounded nice in theory, it does not work in practice because a separate SharedWorker process is created for the app / extension.

If you want to send huge files (e.g. Files), then you could implement the following:

  1. Extension: Create proxy.html (content = <script src=proxy.js></script>). (feel free to pick any other name).
  2. Extension: Put proxy.html in web_accessible_resources.
  3. App: Bind a window.onmessage event listener. This event listener will receive messages from the frame you're going to load in the next step.
  4. App: Load chrome-extension://[EXTENSIONID]/proxy.html in a frame within your app. This extension ID can either be hard-coded (see Obtaining Chrome Extension ID for development) or exchanged via the external extension message passing API (make sure that you validate the source - hardcoding the ID would be the best way).
  5. Extension: When proxy.html is loaded, check whether location.ancestorOrigins[0] == 'chrome-extension://[APPID]' to avoid a security leak. Terminate all steps if this condition fails.
  6. Extension: When you want to pass a File or Blob to the app, use parent.postMessage(blob, 'chrome-extension://[APPID]');
  7. App: When it receives the blob from the extension frame, save it to the FileSystem that you obtained through the chrome.fileSystem API.

The last task to solve is getting a file from the extension to the extension frame (proxy.html) that is embedded in the app. This can be done via a SharedWorker, see this answer for an example (you can skip the part that creates a new frame because the extension frame is already created in one of the previous steps).
Note that at the moment (Chrome 35), Files can only be sent with a work-around due to a bug.

Community
  • 1
  • 1
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Thanks for great information, but i am little confused with step 4 and 5. Do i have to include proxy.html in the app? if so, can i use its content in extension also? I want to save file from the extension,by clicking button or any other way. Then can i use proxy html to build my ui of the extension? – Ruwanka De Silva Jul 05 '14 at 10:46
  • @RuwankaMadhushan `proxy.html` is only used to send non-JSON-serializable objects (such as `File`) from the extension to the app. `proxy.html` is part of the extension, and embedded in a frame within the app. After selecting a file in your extension's UI, the `File` object will be passed to a `SharedWorker`, which in turn passes the object to `proxy.html`, which passes the object to the app. This is quite complex, but it works. Sending a file from the app to the extension goes in a similar way. – Rob W Jul 05 '14 at 10:51
  • Since I am new to extension development again I am having problem how can i embed the `proxy.html` inside app, within embed tag or is there any other way? Since extension cannot select files, what did you mean by selecting files in extension UI? I supposed to collect some information from web page and save it as a xml, I also want to read that xml and show its content in extension UI. Can i do that with your method? – Ruwanka De Silva Jul 05 '14 at 11:00
  • @RuwankaMadhushan Now you're mentioning that you want to save a (XML) string, I strongly recommend to just send the message as a string using external message passing as I mentioned at the top of my answer. It is much simpler than the alternative method, and it works for your purpose. – Rob W Jul 05 '14 at 11:05
  • Actually I don't know how would be the size of xml string, thats why I am interested in your method though it is complicated. So can you please explain little bit more the point that i get confused. – Ruwanka De Silva Jul 05 '14 at 11:09
  • "Actually I don't know how would be the size of xml string," - I don't understand this question – Rob W Jul 05 '14 at 11:10
  • I mean some times xml string will be much larger. – Ruwanka De Silva Jul 05 '14 at 11:11
  • @RuwankaMadhushan But it is still a string that is already in memory, so the external message passing API is still the most suitable API for your purpose. The alternative method only makes sense if you already have a huge non-string object (Blob, File, typed array, etc) that needs to be passed from the extension to the app. – Rob W Jul 05 '14 at 11:13
  • OK thanks for the support I am going with message passing API. – Ruwanka De Silva Jul 05 '14 at 11:16