在 Chromium 中构建 Chrome 扩展的标准方法
我已经构建了一个 Chrome 扩展程序,我已经使用 Selenium 将其安装到 Chrome 中.
现在我想从源代码构建我自己的 Chromium,这样我的扩展就被预先捆绑到构建的分布式包中,这样我就不必担心需要 Selenium 为我的用例安装 CRX 文件.
我找到了几个论坛,人们建议他们尝试这个,但没有一个最终看起来像他们成功.
我发现了一些关于系统管理员如何为网络中的用户强制将扩展安装到 Chrome 中的提示:
3: 现在,我们将开始修改 C++ 源文件.让我们声明我们的扩展名和 ID.我们将在这些文件中这样做:srcextensionscommonextension.h
命名空间扩展{extern const int kOurNumExtensions;extern const char* kOurExtensionIds[];extern const char* kOurExtensionFilenames[];
我刚刚在 extensions
命名空间下声明了这些变量.请记住,我们在下面分配的扩展 ID 必须与 Chromium 分配的扩展 ID 匹配.
那些变量的定义在:srcextensionscommonextension.cc
命名空间扩展{const char* kOurExtensionIds[] = {aaaaaaaaaaaaaaaaaaaaaaaaa"};//假定 tab_capture 的扩展 IDconst char* kOurExtensionFilenames[] = {tab_capture.crx"};const int kOurNumExtensions = 1;
Chromium 将在首次启动时创建配置文件.所以我们假设还没有配置文件存在,因为我们将在它第一次启动时在运行时安装我们的扩展.Windows 计算机上的配置文件通常应存在于此处:C:UsersUsernameAppDataLocalCompanyNameChromiumForkName
,因此请确保在启动 Chromium 之前删除 CompanyName
文件夹.当然,我们也可以在创建配置文件后执行安装过程.为此,您必须检查我们的扩展程序是否已安装,以防止多次尝试安装.
Chromium 在这个文件中处理启动浏览器的创建内容:srcchromerowseruistartupstartup_browser_creator.cc
所以我们在配置文件初始化和浏览器启动后安装这个扩展.您还必须添加一些头文件.我们将在 LaunchBrowser
方法中这样做:
//添加这些头文件因为我们将使用它们#include "base/path_service.h";#include "chrome/browser/extensions/crx_installer.h";#include "chrome/browser/extensions/extension_install_prompt.h";#include "chrome/browser/extensions/extension_service.h";#include "chrome/common/chrome_paths.h";#include "extensions/browser/extension_system.h";bool StartupBrowserCreator::LaunchBrowser(const base::CommandLine&命令行,个人资料* 个人资料,const base::FilePath&cur_dir,铬::启动:: IsProcessStartup process_startup,chrome::startup::IsFirstRun is_first_run) {//省略了 Chromium 代码in_synchronous_profile_launch_ = false;}//安装我们的扩展base::FilePath extension_dir;如果 (first_run::IsChromeFirstRun() &&base::PathService::Get(chrome::DIR_EXTERNAL_EXTENSIONS, &extension_dir)){for (int i = 0; i < extensions::kOurNumExtensions; ++i) {base::FilePath file_to_install(extension_dir.AppendASCII(扩展名::kOurExtensionFilenames[i]));std::unique_ptr迅速的(new ExtensionInstallPrompt(chrome::FindBrowserWithProfile(profile)->tab_strip_model()->GetActiveWebContents()));scoped_refptrcrx_installer(extensions::CrxInstaller::Create(extensions::ExtensionSystem::Get(profile)->extension_service(), std::move(prompt)));crx_installer->set_error_on_unsupported_requirements(true);crx_installer->set_off_store_install_allow_reason(扩展::CrxInstaller::OffStoreInstallAllowedFromSettingsPage);crx_installer->set_install_immediately(true);crx_installer->InstallCrx(file_to_install);}}//安装我们的扩展程序结束//铬代码profile_launch_observer.Get().AddLaunched(profile);
这应该会安装我们的扩展程序,但由于我们希望在没有任何用户交互的情况下强制安装我们的扩展程序,让我们在这里进行:chrome/browser/extensions/extension_install_prompt.cc
void ExtensionInstallPrompt::ShowDialog(const DoneCallback&done_回调,const 扩展* 扩展,const SkBitmap* 图标,std::unique_ptr<提示>迅速的,std::unique_ptr自定义权限,const ShowDialogCallback&show_dialog_callback) {//铬代码返回;}//不要为我们的扩展显示添加扩展提示for (int i = 0; i < extensions::kOurNumExtensions; ++i) {if (extension->id() == extensions::kOurExtensionIds[i]) {//注意:下面的行在最新版本的 Chromium 中不起作用.因此,如果您使用的是最新版本,请使用其下方的代码而不是此代码base::ResetAndReturn(&done_callback_).Run(结果::接受);//注意:对于最新版本的 Chromium.如果上述行在编译时抛出错误,则使用下面的代码std::move(done_callback_).Run(DoneCallbackPayload(Result::ACCEPTED));返回;}}//结束不显示为我们的扩展添加扩展提示//铬代码LoadImageIfNeeded();
4:即使我们自动执行安装过程,Chromium 也会禁用我们的扩展程序,因为它不是从 Chrome 网上应用店安装的.在这里处理:srcchromerowserextensionsinstall_verifier.cc
在这个方法中:
bool InstallVerifier::MustRemainDisabled(const Extension* extension,disable_reason::DisableReason* 原因,base::string16* 错误) const {//省略了 Chromium 代码//铬代码if (Manifest::IsUnpackedLocation(extension->location())) {MustRemainDisabledHistogram(UNPACKED);返回假;}//始终启用我们的选项卡捕获扩展//如果您有多个扩展名,请使用循环if (extension->id() == extensions::kOurExtensionIds[0]) {返回假;}//结束总是启用我们的选项卡捕获扩展//铬代码if (extension->location() == Manifest::COMPONENT) {MustRemainDisabledHistogram(COMPONENT);返回假;}
这将确保在我们绕过 Chrome 网上应用店检查时启用我们的扩展程序.
如果您不希望您的扩展程序被卸载并保持启用状态,那么您可以通过修改此文件来实现:chrome/browser/extensions/standard_management_policy_provider.cc
并修改这些方法:MustRemainInstalled
和 MustRemainEnabled
5:现在您可以通过执行此命令来构建迷你安装程序
ninja -C outBuildFolder mini_installer
以上命令将构建mini_installer.exe
.注意如果您将 --system-level
参数传递给 mini_installer.exe
那么它应该在 Program files
中安装您的 Chromium fork代码>文件夹.安装完成后,您的 crx 文件应位于:C:Program Files (x86)YourChromiumApplication66.0.3359.139Extensions ab_capture.crx
.
Chromium 将解压此 crx 文件并将其安装到您的配置文件中:C:UsersUsernameAppDataLocalYourChromiumUser DataDefaultExtensions
(假定的默认配置文件)
注意:为了提高代码可读性和易用性,您可以使用容器类来保存这些扩展文件名及其对应的 ID,并在基于范围的 for 循环中轻松使用.
让我知道它是否有效.花费的时间比预期的要长,因为我注意到他们的代码库中有很多变化,而且我们的旧代码在最新的 Chromium 版本中不起作用.我敢肯定,我没有错过任何其他东西:)
I have built a Chrome extension that I have been installing into Chrome using Selenium.
Now I would like to build my own Chromium from source so that my extension is pre-bundled into the built distributed package so that I don't have to worry about needing Selenium to install the CRX file for my use case.
I have found several forums where people suggested they were going to try this, but none of them ended up seeming like they were successful.
I found some tips on how a system administrator can force install extensions into chromium for users in their network: https://support.google.com/chrome/a/answer/6306504?hl=en But that is for chrome enterprise, probably not going to be useful for me.
Here is another post which talks about how to offline install chrome extensions. I might be able to use some of this to make what I want happen.
Has anyone had success actually building into chromium a CRX so that the CRX is just installed automatically?
Quick update:
I just want to note: I'm installing my custom version of chrome with an InnoSetup installer. So I do have the chance to, after my chromium fork is installed, do some custom execution steps post install. And my extensions are hosted on the chrome web store and approved.
So if there is some way to programmatically install chrome extensions into a Chromium installation from the web store, I would could easily use that.
解决方案This has been tested in our Chromium fork versions 66.0.3359.139 to 7x.x.x
on Windows 10. The extension bundling process might be different for Linux and macOS. I have also tried to make it as easy as possible to accomplish this task. There are a couple of things you will have to do to accomplish this:
- Add your Chromium extension (.crx) file to a list of default extensions to bundle with mini installer
- Find out the ID of that extension
- Automate extension installation process
- By pass Chrome Web Store check
- Build mini installer to install your Chromium fork
1: To bundle your extension with the installer, you will have to modify: srcchromerowserextensionsdefault_extensionsBUILD.gn
file. Suppose tab_capture.crx
is your extension then it's contents should look something like this:
if (is_win) {
copy("default_extensions") {
sources = [
"external_extensions.json",
"tab_capture.crx"
]
outputs = [
"$root_out_dir/extensions/{{source_file_part}}",
]
I have just appended tab_capture.crx
and have not modified anything else.
Your extension file should be in this location: srcchromerowserextensionsdefault_extensions ab_capture.crx
2: Each extension will have a unique ID assigned to it by Chromium to identify that extension. To find out the ID of your extension you should go to chrome://extensions/
page and drag and drop your crx
file. A confirmation dialog box should popup. Click Add extension
button and make sure Developer mode
is enabled then your ID should be visible but the extension will be disabled as shown below:
3: Now, we will start modifying C++ source files. Let's declare our extension's name and ID. We will do so in these files:
srcextensionscommonextension.h
namespace extensions {
extern const int kOurNumExtensions;
extern const char* kOurExtensionIds[];
extern const char* kOurExtensionFilenames[];
I have just declared those variables below extensions
namespace. Remember, extension ID that we are assigning below must match with the extension ID assigned by Chromium.
Those variables' definition in: srcextensionscommonextension.cc
namespace extensions {
const char* kOurExtensionIds[] = {
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"}; // Assumed extension ID of tab_capture
const char* kOurExtensionFilenames[] = {
"tab_capture.crx"};
const int kOurNumExtensions = 1;
Chromium will create a profile when it's first launched. So we assume no profile exists yet because we will install our extension at run time when it's first launched. A profile on a Windows machine should typically exists here: C:UsersUsernameAppDataLocalCompanyNameChromiumForkName
so make sure to delete CompanyName
folder before launching Chromium. Of course, we can do the installation process after a profile has been created too. For that you will have to check if our extension has been installed or not to prevent multiple attempts of installation.
Chromium handles startup browser creation stuff in this file: srcchromerowseruistartupstartup_browser_creator.cc
so we install this extension after a profile has been initialized and browser has been launched. You will have to add some header files too. We will do so in LaunchBrowser
method:
// Add these header files cause we we will be using them
#include "base/path_service.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_install_prompt.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/common/chrome_paths.h"
#include "extensions/browser/extension_system.h"
bool StartupBrowserCreator::LaunchBrowser(
const base::CommandLine& command_line,
Profile* profile,
const base::FilePath& cur_dir,
chrome::startup::IsProcessStartup process_startup,
chrome::startup::IsFirstRun is_first_run) {
// Omitted Chromium code
in_synchronous_profile_launch_ = false;
}
// Install our extension
base::FilePath extension_dir;
if (first_run::IsChromeFirstRun() &&
base::PathService::Get(chrome::DIR_EXTERNAL_EXTENSIONS, &extension_dir))
{
for (int i = 0; i < extensions::kOurNumExtensions; ++i) {
base::FilePath file_to_install(extension_dir.AppendASCII(
extensions::kOurExtensionFilenames[i]));
std::unique_ptr<ExtensionInstallPrompt> prompt(
new ExtensionInstallPrompt(chrome::FindBrowserWithProfile(profile)->tab_strip_model()->GetActiveWebContents()));
scoped_refptr<extensions::CrxInstaller> crx_installer(extensions::CrxInstaller::Create(
extensions::ExtensionSystem::Get(profile)->extension_service(), std::move(prompt)));
crx_installer->set_error_on_unsupported_requirements(true);
crx_installer->set_off_store_install_allow_reason(
extensions::CrxInstaller::OffStoreInstallAllowedFromSettingsPage);
crx_installer->set_install_immediately(true);
crx_installer->InstallCrx(file_to_install);
}
}
// End of install our extension
// Chromium code
profile_launch_observer.Get().AddLaunched(profile);
That should install our extension but as we want our extension to be forcefully installed without any user interaction, let's do it here: chrome/browser/extensions/extension_install_prompt.cc
void ExtensionInstallPrompt::ShowDialog(
const DoneCallback& done_callback,
const Extension* extension,
const SkBitmap* icon,
std::unique_ptr<Prompt> prompt,
std::unique_ptr<const PermissionSet> custom_permissions,
const ShowDialogCallback& show_dialog_callback) {
// Chromium code
return;
}
// Don't show add extension prompt for our extensions
for (int i = 0; i < extensions::kOurNumExtensions; ++i) {
if (extension->id() == extensions::kOurExtensionIds[i]) {
// Note: The line below won't work in recent versions of Chromium. So if you are using a recent version then use the code just below it instead of this one
base::ResetAndReturn(&done_callback_).Run(
Result::ACCEPTED);
// Note: For recent versions of Chromium. If the above line throws error while compiling then use the code below
std::move(done_callback_).Run(
DoneCallbackPayload(Result::ACCEPTED));
return;
}
}
// End of don't show add extension prompt for our extensions
// Chromium code
LoadImageIfNeeded();
4: Even if we automate the installation process, Chromium will disable our extension cause it was not installed from Chrome Web Store. It's handled here: srcchromerowserextensionsinstall_verifier.cc
in this method:
bool InstallVerifier::MustRemainDisabled(const Extension* extension,
disable_reason::DisableReason* reason,
base::string16* error) const {
// Omitted Chromium code
// Chromium code
if (Manifest::IsUnpackedLocation(extension->location())) {
MustRemainDisabledHistogram(UNPACKED);
return false;
}
// Always enable our tab capture extension
// Use loop if you have more than one extension
if (extension->id() == extensions::kOurExtensionIds[0]) {
return false;
}
// End of always enable our tab capture extension
// Chromium code
if (extension->location() == Manifest::COMPONENT) {
MustRemainDisabledHistogram(COMPONENT);
return false;
}
This will ensure that our extension will be enabled as we are bypassing Chrome Web Store check.
If you don't want your extension to be uninstalled and remain enabled then you can do so by modifying this file: chrome/browser/extensions/standard_management_policy_provider.cc
and modify these methods: MustRemainInstalled
and MustRemainEnabled
5: Now you can build mini installer by executing this command
ninja -C outBuildFolder mini_installer
The above command will build mini_installer.exe
. Note If you pass --system-level
argument to mini_installer.exe
then it should install your Chromium fork in Program files
folder. After the installation is complete your crx file should be located here: C:Program Files (x86)YourChromiumApplication66.0.3359.139Extensions ab_capture.crx
.
Chromium will unpack and install this crx file to your profile: C:UsersUsernameAppDataLocalYourChromiumUser DataDefaultExtensions
(Assumed default profile)
Note: To improve code readability and ease of use, you could use container classes to hold those extension file names and their corresponding IDs and use it easily in a range based for loop.
Let me know if it works. Took longer than expected cause I noticed lots of changes in their code base and our old code was not working in this latest Chromium build. I am sure, I have not missed anything else :)
相关文章