目录
- 前言
- 导出火狐插件.xpi格式
- 插件导入edge/chorme中
- 错误示范1
- 错误示范2
- 修改过程
- manifest.json
- dict文件夹修改
- backgroundScript.js
- injectedScript.js
- debug过程
- 最终backgroundScript.js和injectedScript.js代码
- 完工阶段
- 修改后的源码
前言
因为博主本人想在edge/chrome中使用cph-submit
插件
- vscode中cph,实现一键将代码提交到
Codeforces
中 - 但是只有firefox的插件
- 另外两个比较麻烦,nodejs还有版本问题。
写这篇文章有三个原因:
-
网上暂且没找到firefox插件导入edge/chrome的方法
-
博主个人感觉这个debug过程比较有意义
-
也希望能帮助到其他人。
省流:最终可用的版本如下,直接下载解压,开发人员模式导入edge插件即可:
- https://wwf.lanzout.com/iJ3Gl21r9vja
- 导入的流程可以见本文章节**“插件导入edge/chorme中”**
- 可能还有点小bug,暂且没修好,但是能用(确信
导出火狐插件.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/
然后就会出现这些按钮
然后从“加载解压缩的扩展”
- 也就是我刚刚发的
- https://wwf.lanzout.com/iJ3Gl21r9vja
- 这个解压缩后,导入进去
随后应该就可以用了,可以跳转到“完工阶段”查看效果
错误示范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
如下
{
"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_permissions
和optional_host_permissions
来管理插件的权限。
- 添加
- 删除key字段:
- 不再包含key字段,因为这是开发中的插件,不是发布到商店的版本。
- permissions:
- 保留
activeTab
、webNavigation
和scripting
权限。
- 保留
- host_permissions:
- 使用
host_permissions
来指定对localhost
和codeforces.com
的访问权限。
- 使用
- 删除 optional_host_permissions:
- 移除
optional_host_permissions
字段,因为它包含的权限已经在host_permissions
中。
- 移除
{
"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文件夹修改
还需要修改解压文件夹中的dict
的backgroundScript.js
和injectedScript.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
是浏览器扩展的背景脚本,负责处理一些全局的后台任务,例如:
- 后台逻辑:处理定时任务、全局事件监听器等。
- 与服务器通信:从服务器获取数据,处理服务器响应。
- 管理浏览器标签页:创建、更新或关闭标签页。
- 在不同页面间传递消息:与内容脚本(如
injectedScript.js
)进行通信。
在该扩展中,backgroundScript.js
主要负责从服务器获取提交信息,并在相应的 Codeforces
页面中自动提交代码。
代码在"最终backgroundScript.js和injectedScript.js代码"(
injectedScript.js
injectedScript.js
是注入到目标网页中的脚本,负责与网页内容进行交互,例如:
- 操作网页的 DOM:读取和修改网页中的元素,例如表单字段。
- 接收消息并执行操作:从背景脚本接收消息,并根据消息内容执行相应的操作。
- 模拟用户行为:自动填写表单、点击按钮等。
在该扩展中,injectedScript.js
主要负责在 Codeforces
提交页面上自动填写表单,并模拟用户点击提交按钮。
代码在"最终backgroundScript.js和injectedScript.js代码"(
debug过程
个人修改的debug过程
- 解压文件夹中的
dict
的backgroundScript.js
和injectedScript.js
先将其改为最简单的.js,确保backgroundScript.js
导入没问题
console.log('Service worker registered successfully');
监听下控制台
看起来可以导入
逐渐加入代码
最终backgroundScript.js和injectedScript.js代码
最后backgroundScript.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
修改结果如下
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
蓝奏云盘下载