LateAlways / ti-connect-ce-browser

TI Connect CE App for Chrome OS but patched to work in the browser without having to install anything.
http://latealways.is-a.dev/ti-connect-ce-browser/
Other
2 stars 0 forks source link

How would I go about making this for another app, like the Chromebook Recovery Utility? #4

Closed FunnyHotDog closed 2 weeks ago

FunnyHotDog commented 2 weeks ago

Hello! After using this project for a little while, I thought to myself, "Could this be done with the recovery app or the extension, whichever works best?" How would I go about making this(preferably with the extension version)?

LateAlways commented 2 weeks ago

It's pretty simple.

  1. Download the src code of the extension. (I used this site at first: https://robwu.nl/crxviewer/)
  2. Try to add support for APIs that don't exist in the web environment. (In most cases, the Platform Apps API https://developer.chrome.com/docs/apps/reference will be the one that is unsupported)
  3. Try to emulate these APIs in a normal browser environment (take a look at latealways_patch.js, it emulates the chrome.usb and other important APIs for this to work)
LateAlways commented 2 weeks ago

By the way, a good way to simply find what APIs are used, is simply to look for errors in the console. Otherwise, you can also search for "chrome." in the js files and it should show quite a few of them.

FunnyHotDog commented 2 weeks ago

Ok I'll work on that thanks!

LateAlways commented 2 weeks ago

Also, yesterday, I attempted to port the Chromebook Recovery Utility extension. Here is a list of APIs it used from the Extension API:

Some request require a bypass from CORS. You can simply hook the XMLHttpRequest so that it redirects CORS requests through a cors proxy like so:

let corsproxy = "https://corsproxy.io/?"

let rawOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function () {
    if (arguments[1].startsWith("https://") && (new URL(arguments[1]).hostname) !== window.location.hostname) {
        arguments[1] = corsproxy + encodeURIComponent(arguments[1]);
    }
    //console.log("Fetching: "+arguments[1]+"...");
    return rawOpen.apply(this, arguments)
}
let rawSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function () {
    rawSend.apply(this, arguments);
    this.addEventListener("load", (() => {
        if (this.responseURL.startsWith(corsproxy))
            Object.defineProperty(this, "responseURL", { value: decodeURIComponent(this.responseURL.substring(corsproxy.length)) })
    }).bind(this))
}

As for the storage API, I have already coded it in this repo:

// storage api
window.chrome.storage = {
    local: {
        QUOTA_BYTES: 5242880,
        set: (w, then) => {
            for (let key in w) {
                localStorage.setItem("emul_storage_local_"+key, JSON.stringify(w[key]));
            }
            then();
        },
        get: (w, then) => {
            if (!localStorage.getItem("emul_storage_local_"+w)) {
                window.chrome.runtime.lastError = {message: "fail to get key."};
            } else {
                window.chrome.runtime.lastError = undefined;
            }

            then({ [w]: JSON.parse(localStorage.getItem("emul_storage_local_"+w)) });
        },
        getBytesInUse: (w, then) => {
            then(0);
        },
    },
    sync: {
        QUOTA_BYTES: 5242880,
        set: (w, then) => {
            for (let key in w) {
                localStorage.setItem("emul_storage_sync_"+key, JSON.stringify(w[key]));
            }
            then();
        },
        get: async (w, then) => {
            if (!localStorage.getItem("emul_storage_sync_"+w)) {
                window.chrome.runtime.lastError = {message: "fail to get key."};
                return then();
            }

            window.chrome.runtime.lastError = undefined;
            then({ [w]: JSON.parse(localStorage.getItem("emul_storage_sync_"+w)) });
        },
        getBytesInUse: (w, then) => {
            then(0);
        },
    },
    onChanged: {
        addListener: () => { }
    }
}

Finally, for the runtime API, grab the manifest json file and inline it into the js file being injected like so:

let manifest = *paste the manifest.json file content as is (no string)*

It should look like this:

let manifest = {
    ...
}

... being all the info.

And here:

// runtime api
window.chrome.runtime = {
    lastError: 0,
    getManifest: () => {
        return manifest
    },
    getBackgroundPage: (s) => {
        s(window)
        return window
    }
};
LateAlways commented 2 weeks ago

This app may actually NOT be portable to browser as it requires the use of a private API titled imageWriterPrivate. Which can be half ported to the browser (USB detection) but from the browser, we CANNOT flash a usb.

FunnyHotDog commented 2 weeks ago

Ok got it. I was just wondering if it could happen. Maybe I might have to make an extension to foward imageWriterPrivate, but that defeats the point of emulating the extension in the first place.

LateAlways commented 2 weeks ago

Since it's an ISO/ZIP you could probably only make the app download it for you and then just give you the file. That way, nothing else is needed. Only thing is you'll have to flash it yourself some way or if you find a website that can flash ISOs on usb, that could be ported.