在 Chromium 中构建 Chrome 扩展的标准方法

Standard way to build a Chrome extension into Chromium

我已经构建了一个 Chrome 扩展,我一直在使用 Selenium 安装到 Chrome 中。

现在我想从源代码构建我自己的 Chromium,以便我的扩展被预先捆绑到构建的分布式包中,这样我就不必担心需要 Selenium 来为我的用例安装 CRX 文件.

我找到了几个论坛,人们建议他们尝试这个,但是 none 最终看起来他们成功了。

我找到了一些关于系统管理员如何强制为其网络中的用户在 chromium 中安装扩展的提示:https://support.google.com/chrome/a/answer/6306504?hl=en 但那是针对 chrome 企业的,可能对我没有用。

Here is another post which talks about how to offline install chrome extensions。我也许可以使用其中的一些来实现我想要的。

有没有人成功地在 chromium 中构建了一个 CRX,以便自动安装 CRX?

快速更新:

我只想说明:我正在使用 InnoSetup 安装程序安装自定义版本的 chrome。所以我确实有机会,在我的 chromium fork 安装之后,执行一些自定义执行步骤 post install。我的扩展托管在 chrome 网上商店并获得批准。

因此,如果有某种方法可以通过编程方式将 chrome 扩展程序安装到网上商店的 Chromium 安装中,我会很容易地使用它。

这已经在 Windows 10 上的 Chromium 分支版本 66.0.3359.139 to 7x.x.x 中进行了测试。Linux 和 macOS 的扩展捆绑过程可能不同。我还尝试使完成此任务尽可能简单。要完成此操作,您需要做几件事:

  1. 将您的 Chromium 扩展 (.crx) 文件添加到默认扩展列表以与迷你安装程序捆绑在一起
  2. 找出该扩展程序的 ID
  3. 自动扩展安装过程
  4. 通过 Chrome 网上商店检查
  5. 构建迷你安装程序以安装您的 Chromium 分支

1: 要将您的扩展程序与安装程序捆绑在一起,您必须修改:src\chrome\browser\extensions\default_extensions\BUILD.gn 文件。假设 tab_capture.crx 是您的扩展,那么它的内容应该是这样的:

if (is_win) {
copy("default_extensions") {
sources = [
  "external_extensions.json",
  "tab_capture.crx"
]
outputs = [
  "$root_out_dir/extensions/{{source_file_part}}",
]

我只是附加了 tab_capture.crx 并没有修改任何其他内容。 您的扩展文件应位于此位置:src\chrome\browser\extensions\default_extensions\tab_capture.crx

2: 每个扩展程序都会有一个由 Chromium 分配给它的唯一 ID 来标识该扩展程序。要找出您的扩展程序的 ID,您应该转到 chrome://extensions/ 页面并拖放您的 crx 文件。应弹出一个确认对话框。单击 Add extension 按钮并确保 Developer mode 已启用,然后您的 ID 应该可见,但扩展程序将被禁用,如下所示:

3: 现在,我们开始修改C++源文件。让我们声明我们的扩展名和 ID。我们将在这些文件中这样做: src\extensions\common\extension.h

namespace extensions {

extern const int kOurNumExtensions;
extern const char* kOurExtensionIds[];
extern const char* kOurExtensionFilenames[];

我刚刚在 extensions 命名空间下声明了那些变量。请记住,我们在下面分配的扩展 ID 必须与 Chromium 分配的扩展 ID 匹配。

这些变量的定义在:src\extensions\common\extension.cc

namespace extensions {

const char* kOurExtensionIds[] = {
    "aaaaaaaaaaaaaaaaaaaaaaaaaaa"}; // Assumed extension ID of tab_capture
const char* kOurExtensionFilenames[] = {
    "tab_capture.crx"};
const int kOurNumExtensions = 1;

Chromium 将在首次启动时创建配置文件。所以我们假设还没有配置文件存在,因为我们将在 运行 首次启动时安装我们的扩展。 Windows 机器上的配置文件通常应存在于此处:C:\Users\Username\AppData\Local\CompanyName\ChromiumForkName 因此请确保在启动 Chromium 之前删除 CompanyName 文件夹。当然,我们也可以在创建配置文件后进行安装过程。为此,您必须检查我们的扩展是否已安装,以防止多次安装尝试。

Chromium 在此文件中处理启动浏览器创建内容:src\chrome\browser\ui\startup\startup_browser_creator.cc 因此我们在配置文件初始化并启动浏览器后安装此扩展程序。您还必须添加一些头文件。我们将在 LaunchBrowser 方法中这样做:

// 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);

这应该会安装我们的扩展程序,但由于我们希望在没有任何用户交互的情况下强制安装我们的扩展程序,所以我们在这里进行: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: 即使我们自动执行安装过程,Chromium 也会禁用我们的扩展,因为它不是从 Chrome 网上商店安装的。在这里处理:src\chrome\browser\extensions\install_verifier.cc 在这个方法中:

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;
}

这将确保在我们绕过 Chrome 网上商店检查时启用我们的扩展程序。

如果您不希望您的扩展程序被卸载并保持启用状态,那么您可以通过修改此文件来实现:chrome/browser/extensions/standard_management_policy_provider.cc 并修改这些方法:MustRemainInstalledMustRemainEnabled

5:现在你可以通过执行这个命令来构建迷你安装程序

ninja -C out\BuildFolder mini_installer

以上命令将构建 mini_installer.exe注意 如果您将 --system-level 参数传递给 mini_installer.exe 那么它应该在 Program files 文件夹中安装您的 Chromium 分支。安装完成后,您的 crx 文件应位于此处:C:\Program Files (x86)\YourChromium\Application.0.3359.139\Extensions\tab_capture.crx.

Chromium 将解压缩此 crx 文件并将其安装到您的配置文件中:C:\Users\Username\AppData\Local\YourChromium\User Data\Default\Extensions(假定的默认配置文件)

注意:为了提高代码的可读性和易用性,您可以使用容器类来保存这些扩展文件名及其对应的ID,并在基于 for 循环的范围。

让我知道它是否有效。花费的时间比预期的要长,因为我注意到他们的代码库发生了很多变化,而我们的旧代码在最新的 Chromium 版本中无法正常工作。我确定,我没有错过任何其他东西:)

如果您遵循 Asesh 的回答并使用 chromium 88 或更高版本,您将遇到此错误

"error: no member named 'ResetAndReturn' in namespace 'base' "

我已经在这里回答了这个问题How to build a component extension in chromium browser for windows?