~glacambre/firenvim

eed5303434d54255ba5bfe3d373e75956951ba2e — glacambre 2 months ago 7a3fa47
Implement thunderbird support
M src/NeovimFrame.ts => src/NeovimFrame.ts +1 -0
@@ 19,6 19,7 @@ export const isReady = new Promise((resolve, reject) => {
            const extCmdline = document.getElementById("ext_cmdline") as HTMLSpanElement;
            const extMessages = document.getElementById("ext_messages") as HTMLSpanElement;
            const keyHandler = document.getElementById("keyhandler");
            console.log(await Promise.all([infoPromise, connectionPromise]));
            const [[url, selector, cursor, language], connectionData] =
                await Promise.all([infoPromise, connectionPromise]);
            const nvimPromise = neovim(host, extCmdline, extMessages, connectionData);

M src/background.ts => src/background.ts +7 -0
@@ 400,3 400,10 @@ async function updateIfPossible() {
}
(window as any).updateIfPossible = updateIfPossible;
browser.runtime.onUpdateAvailable.addListener(updateIfPossible);

// In thunderbird, register the script to be loaded in the compose window
if ((browser as any).composeScripts !== undefined) {
    (browser as any).composeScripts.register({
        js: [{file: "compose.js"}],
    });
}

A src/compose.ts => src/compose.ts +62 -0
@@ 0,0 1,62 @@
import { FirenvimElement } from "./FirenvimElement";
import { executeInPage } from "./utils/utils";

const computedStyles : {[k:string]: CSSStyleDeclaration} = {
    documentElement: window.getComputedStyle(document.documentElement),
    body: window.getComputedStyle(document.body),
};
const overridenStyles : {[k: string]: string} = {
    "height": "100%",
    "width": "100%",
    "marginBottom": "0px",
    "marginLeft": "0px",
    "marginRight": "0px",
    "marginTop": "0px",
    "paddingBottom": "0px",
    "paddingLeft": "0px",
    "paddingRight": "0px",
    "paddingTop": "0px",
};
function restoreComputedStyles (_: any) {
    for (const element of Object.keys(computedStyles)) {
        const e = (document as any)[element];
        for (const style of Object.keys(overridenStyles)) {
            e.style[style] = (computedStyles as any)[element][style];
        }
    }
}
function overrideComputedStyles () {
    for (const element of Object.keys(computedStyles)) {
        const e = (document as any)[element];
        for (const style of Object.keys(overridenStyles)) {
            e.style[style] = overridenStyles[style];
        }
    }
}
overrideComputedStyles();

const firenvimElement = new FirenvimElement(document.body, () => Promise.resolve(), restoreComputedStyles);

firenvimElement.attachToPage(
    new Promise((resolve, reject) => {
        setTimeout(reject, 10000);
        browser.runtime.onMessage.addListener((request: { funcName: string[], args: any[] }) => {
            const args = request.args;
            switch (request.funcName[0]) {
                case "evalInPage": return executeInPage(args[1]);
                case "focusInput": throw new Error("focusInput not implemented in Thunderbird");
                case "focusPage": throw new Error("focusPage not implemented in Thunderbird");
                case "getEditorInfo": return firenvimElement.getBufferInfo();
                case "getElementContent": return firenvimElement.getPageElementContent();
                case "hideEditor": throw new Error("hideEditor not implemented in Thunderbird");
                case "killEditor": return firenvimElement.detachFromPage();
                case "pressKeys": throw new Error("pressKeys not implemented in Thunderbird");
                case "registerNewFrameId": return (resolve(request.args[0]));
                case "resizeEditor": throw new Error("resizeEditor not implemented in Thunderbird");
                case "setElementContent": return firenvimElement.setPageElementContent(args[1]);
                case "setElementCursor": return firenvimElement.setPageElementCursor(args[1], args[2]);
                default: throw new Error("Unhandeld request: " + JSON.stringify(request));
            }
        });
    })
);

M src/manifest.json => src/manifest.json +1 -1
@@ 45,7 45,7 @@
   "icons": {
      "128": "firenvim.svg"
   },
   BROWSER_SPECIFIC_SETTINGS,
   TARGET_SPECIFIC_SETTINGS,
   "manifest_version": 2,
   "name": "Firenvim",
   "permissions": [ "nativeMessaging", "storage", "tabs" ],

M src/utils/configuration.ts => src/utils/configuration.ts +3 -1
@@ 51,7 51,9 @@ export const confReady = new Promise(resolve => {
browser.storage.onChanged.addListener((changes: any) => {
    Object
        .entries(changes)
        .forEach(([key, value]: [keyof IConfig, any]) => conf[key] = value.newValue);
        .forEach(([key, value]: [keyof IConfig, any]) => confReady.then(() => {
            conf[key] = value.newValue
        }));
});

export function getGlobalConf() {

M src/utils/utils.ts => src/utils/utils.ts +6 -1
@@ 105,7 105,12 @@ export function getIconImageData(kind: IconKind, dimensions = "32x32") {
// Given a url and a selector, tries to compute a name that will be unique,
// short and readable for the user.
export function toFileName(url: string, id: string, language: string) {
    const parsedURL = new URL(url);
    let parsedURL;
    try {
        parsedURL = new URL(url);
    } catch (e) {
        parsedURL = { hostname: 'thunderbird', pathname: 'mail' };
    }
    const shortId = id.replace(/:nth-of-type/g, "");
    const toAlphaNum = (str: string) => (str.match(/[a-zA-Z0-9]+/g) || [])
        .join("-")

M webpack.config.js => webpack.config.js +47 -3
@@ 79,6 79,7 @@ const package_json = JSON.parse(require("fs").readFileSync(path.join(__dirname, 

const chrome_target_dir = path.join(__dirname, "target", "chrome")
const firefox_target_dir = path.join(__dirname, "target", "firefox")
const thunderbird_target_dir = path.join(__dirname, "target", "thunderbird")

const chromeConfig = (config, env) => {
  const result = Object.assign(deepCopy(config), {


@@ 91,7 92,7 @@ const chromeConfig = (config, env) => {
      transform: (content, src) => {
        if (path.basename(src) === "manifest.json") {
          content = content.toString()
            .replace('BROWSER_SPECIFIC_SETTINGS', '"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk3pkgh862ElxtREZVPLxVNbiFWo9SnvZtZXZavNvs2GsUTY/mB9yHTPBGJiBMJh6J0l+F5JZivXDG7xdQsVD5t39CL3JGtt93M2svlsNkOEYIMM8tHbp69shNUKKjZOfT3t+aZyigK2OUm7PKedcPeHtMoZAY5cC4L1ytvgo6lge+VYQiypKF87YOsO/BGcs3D+MMdS454tLBuMp6LxMqICQEo/Q7nHGC3eubtL3B09s0l17fJeq/kcQphczKbUFhTVnNnIV0JX++UCWi+BP4QOpyk5FqI6+SVi+gxUosbQPOmZR4xCAbWWpg3OqMk4LqHaWpsBfkW9EUt6EMMMAfQIDAQAB"')
            .replace('TARGET_SPECIFIC_SETTINGS', '"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk3pkgh862ElxtREZVPLxVNbiFWo9SnvZtZXZavNvs2GsUTY/mB9yHTPBGJiBMJh6J0l+F5JZivXDG7xdQsVD5t39CL3JGtt93M2svlsNkOEYIMM8tHbp69shNUKKjZOfT3t+aZyigK2OUm7PKedcPeHtMoZAY5cC4L1ytvgo6lge+VYQiypKF87YOsO/BGcs3D+MMdS454tLBuMp6LxMqICQEo/Q7nHGC3eubtL3B09s0l17fJeq/kcQphczKbUFhTVnNnIV0JX++UCWi+BP4QOpyk5FqI6+SVi+gxUosbQPOmZR4xCAbWWpg3OqMk4LqHaWpsBfkW9EUt6EMMMAfQIDAQAB"')
            .replace("FIRENVIM_VERSION", package_json.version)
            .replace("PACKAGE_JSON_DESCRIPTION", package_json.description)
          // Chrome doesn't support svgs in its manifest


@@ 136,7 137,7 @@ const firefoxConfig = (config, env) => {
        transform: (content, src) => {
          switch(path.basename(src)) {
            case "manifest.json":
              content = content.toString().replace("BROWSER_SPECIFIC_SETTINGS",
              content = content.toString().replace("TARGET_SPECIFIC_SETTINGS",
`  "browser_specific_settings": {
    "gecko": {
      "id": "firenvim@lacamb.re",


@@ 163,6 164,46 @@ const firefoxConfig = (config, env) => {
  return result;
}

const thunderbirdConfig = (config, env) => {
  const result = Object.assign(deepCopy(config), {
    output: {
      path: thunderbird_target_dir,
    },
    plugins: [new CopyWebPackPlugin({
      patterns: CopyWebPackFiles.map(file => ({
        from: file,
        to: thunderbird_target_dir,
        transform: (content, src) => {
          switch(path.basename(src)) {
            case "manifest.json":
              content = content.toString().replace("TARGET_SPECIFIC_SETTINGS",
`  "browser_specific_settings": {
    "gecko": {
      "id": "firenvim@lacamb.re",
      "strict_min_version": "84.0a1"
    }
  }`)
                .replace("FIRENVIM_VERSION", package_json.version)
                .replace("PACKAGE_JSON_DESCRIPTION", package_json.description)
                .replace(`"permissions": [`, `"permissions": [ "compose",`);
              ;
              if (env.endsWith("testing")) {
                content = content.replace(`content.js`, `content.js", "testing.js`);
              }
          }
          return content;
        }
      }))
    })]
  });
  try {
    fs.rmdirSync(result.output.path, { recursive: true })
  } catch (e) {
    console.log(`Could not delete output dir (${e.message})`);
  }
  return result;
}

module.exports = args => {
  let env = Object.keys(args)[0];
  if (env === undefined){


@@ 178,7 219,10 @@ module.exports = args => {
    return [chromeConfig(config, env)];
  } else if (env.startsWith("firefox")) {
    return [firefoxConfig(config, env)];
  } else if (env.startsWith("thunderbird")) {
    config.entry.compose = "./src/compose.ts";
    return [thunderbirdConfig(config, env)];
  }
  return [chromeConfig(config, env), firefoxConfig(config, env)];
  return [chromeConfig(config, env), firefoxConfig(config, env), thunderbirdConfig(config, env)];
}