将Firefox插件导入Edge/Chrome中

目录

将Firefox插件导入Edge/Chrome中

前言

因为博主本人想在edge/chrome中使用cph-submit插件

  • vscode中cph,实现一键将代码提交到Codeforces
  • 但是只有firefox的插件
  • 另外两个比较麻烦,nodejs还有版本问题。

写这篇文章有三个原因:

  1. 网上暂且没找到firefox插件导入edge/chrome的方法

  2. 博主个人感觉这个debug过程比较有意义

  3. 也希望能帮助到其他人。

省流:最终可用的版本如下 ,直接下载解压开发人员模式导入edge插件即可:

导出火狐插件.xpi格式

在火狐浏览器中打开这个about:profiles,就可以看到了

  • 这就是你当前浏览器用户的存储文件夹

然后点击打开文件夹

点击extensions,进去就可以看到你安装的插件的.xpi格式,这边我只安装了一个(名为cph-submit)。

但是其实你可以通过修改时间 来判断哪个是哪个(大不了卸掉再装一下,最新的那个)

  • 一般也就一两个

然后把这个文件复制出来,找个空的目录放一下,我将它重命名为cph-submit.xpi

右键解压到文件夹(我用的360解压软件)

  • 这一步如果没法完成,你可以修改后缀名为.zip
  • 然后再解压,效果一样

因为解压完的文件夹不一定直接用

  • 有些 Firefox 扩展可能需要进行一些代码修改才能在 Edge/Chrome 上运行,
  • 需要修改 manifest.json 文件以符合 Edge/Chrome 的格式。

插件导入edge/chorme中

随后在edge/chrome浏览器中,打开开发人员模式

  • 进入edge://extensions/

然后就会出现这些按钮

如果修改好了最终的插件版本,就可以点击从"加载解压缩的扩展"

随后应该就可以用了,可以跳转到"完工阶段"查看效果

下面是错误示范 ,以及修改过程

错误示范1

不能用.crx格式导入

  • 不要将刚解压的文件夹的路径 打包成为.crx格式,
  • 大概率会G掉

如下所示

选择解压后的路径

随后就可以看到

不过.crx才是我们要用的,.pem不需要

.crx直接拖进浏览器中,点击添加拓展

启动按键是灰色的,这是错误示范,所以不能这样

直接用解压好的就没下面这个问题

错误示范2

如果你修改的,有问题

  • 第一种情况,初始化的时候就有问题,那么一导入就是挂的
  • 第二种情况,运行到一半,用到才有问题

会先正常显示,然后在挂掉(错误/重新加载两个一开始不显示)

此时你其实可以点进去错误,查看哪里错了,然后修改

当然,一般修改工作量也挺大的

可以交给GPT来改


这些错误是可以展开的,直到报错后针对性修改

(无关紧要的)提醒:

要是你不知道firefox和edge他们的manifest.json的差别,其实你可以本地找一下edge拓展的源文件,然后对比下

当然,我都喂给了GPT,让他帮忙修改

复制代码
C:\Users\<你的电脑用户名>\AppData\Local\Microsoft\Edge\User Data\Default\Extensions

有问题的话,得删除插件

然后再从文件夹导入这个插件

修改过程

manifest.json

点进文件夹,查看manifest.json如下

json 复制代码
{
  "name": "CPH Submit",
  "manifest_version": 2,
  "homepage_url": "https://github.com/agrawal-d/cph-submit",
  "version": "1.6.0",
  "description": "Codeforces Submit add-on for Competitive Programming Helper.",
  "background": {
    "scripts": ["dist/backgroundScript.js"]
  },
  "permissions": ["*://localhost/*", "*://codeforces.com/*", "webNavigation"],
  "icons": {
    "48": "icon-48.png"
  },
  "browser_specific_settings": {
    "gecko": {
      "id": "{5dd8fd6e-0733-41a7-abc4-e19fba703de9}",
      "strict_min_version": "49.0"
    }
  }
}

我将其修改为:

  • manifest_version :
    • 2 改为 3
  • background :
    • scripts 改为 service_worker,并增加 "type": "module"
  • action :
    • 添加 action 部分,用于定义插件的图标和默认标题。
  • host_permissions :
    • 添加 host_permissionsoptional_host_permissions 来管理插件的权限。
  • 删除key字段 :
    • 不再包含key字段,因为这是开发中的插件,不是发布到商店的版本。
  • permissions :
    • 保留 activeTabwebNavigationscripting 权限。
  • host_permissions :
    • 使用 host_permissions 来指定对 localhostcodeforces.com 的访问权限。
  • 删除 optional_host_permissions :
    • 移除 optional_host_permissions 字段,因为它包含的权限已经在 host_permissions 中。
json 复制代码
{
  "name": "CPH Submit",
  "manifest_version": 3,
  "homepage_url": "https://github.com/agrawal-d/cph-submit",
  "version": "1.6.0",
  "description": "Codeforces Submit add-on for Competitive Programming Helper.",
  "background": {
    "service_worker": "dist/backgroundScript.js",
    "type": "module"
  },
  "permissions": ["activeTab", "webNavigation", "scripting"],
  "icons": {
    "48": "icon-48.png"
  },
  "action": {
    "default_icon": {
      "48": "icon-48.png"
    },
    "default_title": "CPH Submit"
  },
  "host_permissions": ["*://localhost/*", "*://codeforces.com/*"]
}

dict文件夹修改

还需要修改解压文件夹中的dictbackgroundScript.jsinjectedScript.js

  • 并不能从火狐直接移植到chrome/edge
  • 里面的browser API 需要修改为chrome API
    • **Edge浏览器现在基于Chromium,所以使用与Chrome相同的API。**二者可以共享,但是与火狐不行
    • 因此,只需要将代码中的 browser 替换为 chrome,并使用 chrome.scripting.executeScript 来代替 browser.tabs.executeScript
  • 需要修改的地方可能还有
    • browser 替换为 chrome :所有 browser API 调用替换为 chrome
      • browser.tabs.create 修改为 chrome.tabs.create
      • browser.windows.update 修改为 chrome.windows.update
      • 使用 chrome.scripting.executeScript 代替 browser.tabs.executeScript
      • browser.tabs.sendMessage 修改为 chrome.tabs.sendMessage
backgroundScript.js

backgroundScript.js 是浏览器扩展的背景脚本,负责处理一些全局的后台任务,例如:

  1. 后台逻辑:处理定时任务、全局事件监听器等。
  2. 与服务器通信:从服务器获取数据,处理服务器响应。
  3. 管理浏览器标签页:创建、更新或关闭标签页。
  4. 在不同页面间传递消息 :与内容脚本(如 injectedScript.js)进行通信。

在该扩展中,backgroundScript.js 主要负责从服务器获取提交信息,并在相应的 Codeforces 页面中自动提交代码。

代码在"最终backgroundScript.js和injectedScript.js代码"(

injectedScript.js

injectedScript.js 是注入到目标网页中的脚本,负责与网页内容进行交互,例如:

  1. 操作网页的 DOM:读取和修改网页中的元素,例如表单字段。
  2. 接收消息并执行操作:从背景脚本接收消息,并根据消息内容执行相应的操作。
  3. 模拟用户行为:自动填写表单、点击按钮等。

在该扩展中,injectedScript.js 主要负责在 Codeforces 提交页面上自动填写表单,并模拟用户点击提交按钮。

代码在"最终backgroundScript.js和injectedScript.js代码"(

debug过程

个人修改的debug过程

  • 解压文件夹中的dictbackgroundScript.jsinjectedScript.js

先将其改为最简单的.js,确保backgroundScript.js导入没问题

复制代码
console.log('Service worker registered successfully');

监听下控制台

看起来可以导入

逐渐加入代码

最终backgroundScript.js和injectedScript.js代码

最后backgroundScript.js修改结果如下

js 复制代码
const config = {
  cphServerEndpoint: new URL("http://localhost:27121/getSubmit"),
  cfSubmitPage: new URL("https://codeforces.com/problemset/submit"),
  loopTimeOut: 3000,
  debug: false
};

const log = (...args) => {
  if (config.debug) {
    console.log(...args);
  }
};

const isContestProblem = (problemUrl) => {
  return problemUrl.indexOf("contest") !== -1;
};

const getSubmitUrl = (problemUrl) => {
  if (!isContestProblem(problemUrl)) {
    return config.cfSubmitPage.href;
  }
  const url = new URL(problemUrl);
  const contestNumber = url.pathname.split("/")[2];
  const submitURL = `https://codeforces.com/contest/${contestNumber}/submit`;
  return submitURL;
};

const handleSubmit = async (problemName, languageId, sourceCode, problemUrl) => {
  if (problemName === "" || languageId === -1 || sourceCode === "") {
    log("Invalid arguments to handleSubmit");
    return;
  }

  log("isContestProblem", isContestProblem(problemUrl));

  chrome.tabs.create({ active: true, url: getSubmitUrl(problemUrl) }, (tab) => {
    chrome.windows.update(tab.windowId, { focused: true });

    if (tab.id === undefined) {
      log("No tab id to send message to", tab);
      return;
    }

    chrome.tabs.onUpdated.addListener(function listener(tabId, changeInfo) {
      if (tabId === tab.id && changeInfo.status === 'complete') {
        chrome.tabs.onUpdated.removeListener(listener);

        chrome.scripting.executeScript({
          target: { tabId: tab.id },
          files: ["/dist/injectedScript.js"]
        }, () => {
          chrome.tabs.sendMessage(tab.id, {
            type: "cph-submit",
            problemName,
            languageId,
            sourceCode,
            url: problemUrl,
          }, () => {
            if (chrome.runtime.lastError) {
              console.error("Error sending message:", chrome.runtime.lastError);
            } else {
              log("Message sent to tab with script");
            }
          });
        });
      }
    });
  });
};

const mainLoop = async () => {
  let cphResponse;
  try {
    const headers = new Headers();
    headers.append("cph-submit", "true");

    const request = new Request(config.cphServerEndpoint.href, {
      method: "GET",
      headers,
    });

    cphResponse = await fetch(request);
  } catch (err) {
    log("Error while fetching cph response", err);
    return;
  }

  if (!cphResponse.ok) {
    log("Error while fetching cph response", cphResponse);
    return;
  }

  const response = await cphResponse.json();

  if (response.empty) {
    log("Got empty valid response from CPH");
    return;
  }

  log("Got non-empty valid response from CPH");
  handleSubmit(response.problemName, response.languageId, response.sourceCode, response.url);
};

setInterval(mainLoop, config.loopTimeOut);

console.log('Service worker registered successfully');

以及injectedScript.js修改结果如下

js 复制代码
console.log("cph-submit script injected");

const isContestProblem = (problemUrl) => {
  return problemUrl.indexOf("contest") !== -1;
};

const handleData = (data) => {
  console.log("Handling submit message");
  const languageEl = document.getElementsByName("programTypeId")[0];
  const sourceCodeEl = document.getElementById("sourceCodeTextarea");

  sourceCodeEl.value = data.sourceCode;
  languageEl.value = data.languageId.toString();

  if (!isContestProblem(data.url)) {
    const problemNameEl = document.getElementsByName("submittedProblemCode")[0];
    problemNameEl.value = data.problemName;
  } else {
    const problemIndexEl = document.getElementsByName("submittedProblemIndex")[0];
    let problemName = data.url.split("/problem/")[1];

    if (problemName == "0") {
      problemName = "A";
    }
    problemIndexEl.value = problemName;
  }

  console.log("Submitting problem");
  const submitBtn = document.querySelector(".submit");
  submitBtn.disabled = false;
  submitBtn.click();
};

console.log("Adding event listener");

chrome.runtime.onMessage.addListener((data, sender) => {
  console.log("Got message", data, sender);
  if (data.type === "cph-submit") {
    handleData(data);
  }
});

完工阶段

点击competitive Companion,将样例一键导入vscode中

随后,在右侧编写代码

然后点击Submit提交代码

就成功在edge上,先自动跳转到提交界面,然后自动填写代码,提交表单

最后自动跳转到这个界面,提交成功

修改后的源码

上面其实也可以复制下来

我整理了可以直接用的版本

https://wwf.lanzout.com/iJ3Gl21r9vja

蓝奏云盘下载

相关推荐
一张假钞10 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行10 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_5937581010 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox
mywpython14 小时前
mac 最新的chrome版本配置selenium的方式
chrome·python·selenium·macos
獨枭1 天前
Linux 下安装和使用 Jupyter Notebook
linux·chrome·jupyter
日升2 天前
Chrome 134 版本开发者工具(DevTools)更新内容
前端·chrome·浏览器
白露与泡影2 天前
SpringBoot 最大连接数及最大并发数是多少?
spring boot·后端·firefox
sukida1002 天前
Firefox 浏览器同步一个账户和书签网址
android·linux·firefox
我要升天!3 天前
Linux中《环境变量》详细介绍
linux·运维·chrome
muzidigbig5 天前
Chrome(Google) 浏览器安装Vue2、Vue3 Devtools插件方法
chrome·vue.js devtools·google vue插件方法