Chromium扩展(Extension)加载过程分析

发表于 5年以前  | 总阅读数:2671 次

Chromium在启动的时候,会根据当前用户的Profile创建一个Extension Service。Extension Service在创建过程中,会加载当前已经安装的所有Extension,并且将它们注册在一个Extension Registry中。以后通过这个Extension Registry,就可以得到当前可用的Extension的信息了。本文接下来就分析Extension的加载过程。

《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

Chromium的启动,指的实际上是Chromium的Browser进程的启动。关于Chromium的多进程架构,可以参考前面Chromium多进程架构简要介绍和学习计划这个系列的文章。Chromium的Browser进程在启动之后,会创建一系列的Startup Task。每一个Startup Task都会负责初始化相应的模块。上述的Extension Service就是在一个Startup Task中创建的,如图1所示:

图1 Extension的加载过程

Extension Service在创建的过程中,会通过一个Installed Loader加载当前已经安装的所有Extension,并且将那些设置为Enabled的Extension注册到Extension Registry中,从而得到一个当前可用的Extension列表。

接下来,我们就从Chromium的Browser进程的启动开始,分析它加载Extension的过程。

在前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文中,我们以Content Shell APK为例,分析了Browser进程的启动过程。在这个启动过程中,会创建一个BrowserStartupController对象,并且调用这个BrowserStartupController对象的成员函数startBrowserProcessesAsync异步启动和初始化Chromium的Content模块,如下所示:

public class BrowserStartupController {
        ......

        public void startBrowserProcessesAsync(final StartupCallback callback)
                throws ProcessInitException {
            ......

            // Browser process has not been fully started yet, so we defer executing the callback.
            mAsyncStartupCallbacks.add(callback);
            ......

            if (!mHasStartedInitializingBrowserProcess) {
                ......

                prepareToStartBrowserProcess(MAX_RENDERERS_LIMIT);
                ......

                if (contentStart() > 0) {
                    // Failed. The callbacks may not have run, so run them.
                    enqueueCallbackExecution(STARTUP_FAILURE, NOT_ALREADY_STARTED);
                }
            }
        }

        ......
    }

这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java中。

在Browser进程的Content模块还没有启动过的情况下,BrowserStartupController类的成员变量mHasStartedInitializingBrowserProcess的值会等于false。在这种情况下,BrowserStartupController类的成员函数startBrowserProcessesAsync会做两件事情:

1. 调用成员函数prepareToStartBrowserProcess在VM中加载libcontent_shell_content_view.so。

2. 调用成员函数contentStart启动和初始化Content模块。

Dalvik虚拟机JNI方法的注册过程分析这篇文章可以知道,VM在加载so的过程中,将会调用它导出的一个名称为JNI_OnLoad的函数。对libcontent_shell_content_view.so来说,它导出的JNI_OnLoad函数的实现如下所示:

// This is called by the VM when the shared library is first loaded.
    JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
      ......

      content::SetContentMainDelegate(new content::ShellMainDelegate());
      return JNI_VERSION_1_4;
    }

这个函数定义在文件external/chromium_org/content/shell/android/shell_library_loader.cc中。

函数JNI_OnLoad将会创建一个ShellMainDelegate对象,并且调用函数SetContentMainDelegate将它保存在一个全局变量g_content_main_delegate中,如下所示:

namespace {
    ......

    LazyInstance<scoped_ptr<contentmaindelegate> > g_content_main_delegate =
        LAZY_INSTANCE_INITIALIZER;
    }  // namespace

    void SetContentMainDelegate(ContentMainDelegate* delegate) {
      DCHECK(!g_content_main_delegate.Get().get());
      g_content_main_delegate.Get().reset(delegate);
    }

这个函数定义在文件external/chromium_org/content/app/android/content_main.cc中。

这一步执行完成后,回到前面分析的BrowserStartupController类的成员函数startBrowserProcessesAsync中,它接下来将会调用另外一个成员函数contentStart启动和初始化Content模块,如下所示:

public class BrowserStartupController {
        ......

        int contentStart() {
            return ContentMain.start();
        }

        ......
    }

这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java中。

BrowserStartupController类的成员函数contentStart调用ContentMain类的静态成员函数start在Browser进程中启动和初始化Content模块。在前面Chromium的Render进程启动过程分析一文中,我们已经分析过ContentMain类的静态成员函数start的实现了。它最终会调用到C++层的一个函数Start启动和初始化Content模块,如下所示:

namespace {
    LazyInstance<scoped_ptr<ContentMainRunner> > g_content_runner =
        LAZY_INSTANCE_INITIALIZER;

    ......
    }  // namespace

    ......

    static jint Start(JNIEnv* env, jclass clazz) {
      ......

      if (!g_content_runner.Get().get()) {
        ContentMainParams params(g_content_main_delegate.Get().get());
        g_content_runner.Get().reset(ContentMainRunner::Create());
        g_content_runner.Get()->Initialize(params);
      }
      return g_content_runner.Get()->Run();
    }

这个函数定义在文件external/chromium_org/content/app/android/content_main.cc中。

函数Start首先判断一个全局变量g_content_runner是否已经指向了一个ContentMainRunner对象。如果还没有指向,那么就会调用ContentMainRunner类的静态成员函数Create创建一个ContentMainRunner对象,如下所示:

ContentMainRunner* ContentMainRunner::Create() {
      return new ContentMainRunnerImpl();
    }

这个函数定义在文件external/chromium_org/content/app/content_main_runner.cc中。

从这里可以看到,ContentMainRunner类的静态成员函数Create创建的实际上是一个ContentMainRunnerImpl对象。这个ContentMainRunnerImpl对象返回给前面分析的函数Start之后,就会保存在全局变量g_content_runner中。

函数Start获得了新创建的ContentMainRunnerImpl对象之后,会调用它的成员函数Initialize,并且将全局变量g_content_main_delegate指向的ShellMainDelegate对象封装在一个类型为ContentMainParams的参数中传递给它,让它执行初始化工作。

ContentMainRunnerImpl类的成员函数Initialize的实现如下所示:

class ContentMainRunnerImpl : public ContentMainRunner {
     public:
      ......

      virtual int Initialize(const ContentMainParams& params) OVERRIDE {
        ......

        delegate_ = params.delegate;

        ......
      }

      ......

     private:
      ......

      ContentMainDelegate* delegate_;

      ......
    };

这个函数定义在文件external/chromium_org/content/app/content_main_runner.cc中。

ContentMainRunnerImpl类的成员函数Initialize将封装在参数params中的一个ShellMainDelegate对象保存在成员变量delegate_中,也就是ContentMainRunnerImpl类的成员变量delegate_指向了一个ShellMainDelegate对象。

回到前面分析的函数Start中,它最后调用前面创建的ContentMainRunnerImpl对象的成员函数Run启动和初始化Content模块,如下所示:

class ContentMainRunnerImpl : public ContentMainRunner {
     public:
      ......

      virtual int Run() OVERRIDE {
        ......
        const CommandLine& command_line = *CommandLine::ForCurrentProcess();
        std::string process_type =
              command_line.GetSwitchValueASCII(switches::kProcessType);

        MainFunctionParams main_params(command_line);
        ......

    #if !defined(OS_IOS)
        return RunNamedProcessTypeMain(process_type, main_params, delegate_);
    #else
        return 1;
    #endif
      }

      ......
    };

这个函数定义在文件external/chromium_org/content/app/content_main_runner.cc中。

ContentMainRunnerImpl类的成员函数Run首先检查当前进程是否指定了switches::kProcessType启动选项。如果指定了,那么就会获取它的值。获取到的值保存在本地变量process_type中,表示当前进程的类型。Browser进程没有指定switches::kProcessType启动选项,因此本地变量process_type的值将为空,表示当前进程是Browser进程。

ContentMainRunnerImpl类的成员函数Run接下来调用函数RunNamedProcessTypeMain启动和初始化Content模块,如下所示:

int RunNamedProcessTypeMain(
        const std::string& process_type,
        const MainFunctionParams& main_function_params,
        ContentMainDelegate* delegate) {
      static const MainFunction kMainFunctions[] = {
    #if !defined(CHROME_MULTIPLE_DLL_CHILD)
        { "",                            BrowserMain },
    #endif
    #if !defined(CHROME_MULTIPLE_DLL_BROWSER)
    #if defined(ENABLE_PLUGINS)
    #if !defined(OS_LINUX)
        { switches::kPluginProcess,      PluginMain },
    #endif
        { switches::kWorkerProcess,      WorkerMain },
        { switches::kPpapiPluginProcess, PpapiPluginMain },
        { switches::kPpapiBrokerProcess, PpapiBrokerMain },
    #endif  // ENABLE_PLUGINS
        { switches::kUtilityProcess,     UtilityMain },
        { switches::kRendererProcess,    RendererMain },
        { switches::kGpuProcess,         GpuMain },
    #endif  // !CHROME_MULTIPLE_DLL_BROWSER
      };

      ......

      for (size_t i = 0; i < arraysize(kMainFunctions); ++i) {
        if (process_type == kMainFunctions[i].name) {
          if (delegate) {
            int exit_code = delegate->RunProcess(process_type,
                main_function_params);
    #if defined(OS_ANDROID)
            // In Android's browser process, the negative exit code doesn't mean the
            // default behavior should be used as the UI message loop is managed by
            // the Java and the browser process's default behavior is always
            // overridden.
            if (process_type.empty())
              return exit_code;
    #endif
            if (exit_code >= 0)
              return exit_code;
          }
          return kMainFunctions[i].function(main_function_params);
        }
      }

      ......

      return 1;
    }

这个函数定义在文件external/chromium_org/content/app/content_main_runner.cc中。

函数RunNamedProcessTypeMain在内部定义了一个静态数组kMainFunctions,它会根据参数process_type的值在这个数组中找到对应的函数执行,也就是不同的进程执行不同的函数来启动和初始化Content模块。

从前面Chromium的Render进程启动过程分析Chromium的GPU进程启动过程分析Chromium的Plugin进程启动过程分析这三篇文章可以知道,Render进程、GPU进程和Plugin进程分别通过调用函数RendererMain、GpuMain和PluginMain启动和初始化Content模块。

对于Browser进程来说,情况有点特殊,它并没有调用函数BrowserMain来启动和初始化Content模块。这是因为当参数process_type的值等于空时,函数RunNamedProcessTypeMain调用完成另外一个参数delegate指向的一个ShellMainDelegate对象的成员函数RunProcess后,就会直接直接返回,从而不会执行函数BrowserMain。

这意味着Browser进程是通过调用ShellMainDelegate类的成员函数RunProcess来启动和初始化Content模块的,如下所示:

int ShellMainDelegate::RunProcess(
        const std::string& process_type,
        const MainFunctionParams& main_function_params) {
      ......

      browser_runner_.reset(BrowserMainRunner::Create());
      return ShellBrowserMain(main_function_params, browser_runner_);
    }

这个函数定义在文件external/chromium_org/content/shell/app/shell_main_delegate.cc中。

ShellMainDelegate类的成员函数RunProcess首先调用BrowserMainRunner类的静态成员函数Create创建一个BrowserMainRunnerImpl对象,如下所示:

BrowserMainRunner* BrowserMainRunner::Create() {
      return new BrowserMainRunnerImpl();
    }

这个函数定义在文件external/chromium_org/content/browser/browser_main_runner.cc中。

创建出来的BrowserMainRunnerImpl对象将会保存在ShellMainDelegate类的成员变量browser_runner_中,并且这个BrowserMainRunnerImpl对象会传递给另外一个函数ShellBrowserMain进行处理,如下所示:

// Main routine for running as the Browser process.
    int ShellBrowserMain(
        const content::MainFunctionParams& parameters,
        const scoped_ptr<content::BrowserMainRunner>& main_runner) {
      ......

      int exit_code = main_runner->Initialize(parameters);
      ......

      if (exit_code >= 0)
        return exit_code;

      ......

      return exit_code;
    }

这个函数定义在文件external/chromium_org/content/shell/browser/shell_browser_main.cc中。

函数ShellBrowserMain主要是调用参数main_runner指向的一个BrowserMainRunnerImpl对象的成员函数Initialize在Browser进程中初始化Content模块。BrowserMainRunnerImpl类的成员函数Initialize的返回值将会大于等于0,这时候函数ShellBrowserMain就会沿着调用路径一直返回到Java层去了,从而使得当前线程(Browser进程的主线程)在Java层进入到消息循环中去。

以上就是Content Shell APK的Browser进程的启动流程。Chrome APK的Browser进程的启动流程也是类似的,它们最后都会通过调用BrowserMainRunnerImpl类的成员函数Initialize初始化Content模块。为了方便描述,接下来我们就将以Chrome APK为例,继续分析BrowserMainRunnerImpl类的成员函数Initialize的实现,从中就可以看到Extension的加载过程。

BrowserMainRunnerImpl类的成员函数Initialize的实现如下所示:

class BrowserMainRunnerImpl : public BrowserMainRunner {
     public:
      ......

      virtual int Initialize(const MainFunctionParams& parameters) OVERRIDE {
        ......

        if (!initialization_started_) {
          initialization_started_ = true;
          ......

          main_loop_.reset(new BrowserMainLoop(parameters));

          main_loop_->Init();

          ......
        }

        main_loop_->CreateStartupTasks();
        int result_code = main_loop_->GetResultCode();
        if (result_code > 0)
          return result_code;

        // Return -1 to indicate no early termination.
        return -1;
      }

      ......
    };

这个函数定义在文件external/chromium_org/content/browser/browser_main_runner.cc中。

BrowserMainRunnerImpl类的成员函数Initialize首先检查成员变量initialization_started_的值是否不等于true。如果不等于true,那么就说明Browser进程还没有执行过初始化操作。在这种情况下,BrowserMainRunnerImpl类的成员函数Initialize接下来就会创建一个BrowserMainLoop对象,并且调用这个BrowserMainLoop对象的成员函数Init执行初始化工作,如下所示:

void BrowserMainLoop::Init() {
      ......
      parts_.reset(
          GetContentClient()->browser()->CreateBrowserMainParts(parameters_));
    }

这个函数定义在文件external/chromium_org/content/browser/browser_main_loop.cc中。

BrowserMainLoop对象的成员函数Init的主要任务是创建一个BrowserMainParts对象,并且保存在成员变量parts_中。后面会通过这个BrowserMainParts对象执行一些初始化工作。

为了创建这个BrowserMainParts对象,BrowserMainLoop对象的成员函数Init首先调用函数GetContentClient获得一个ContentClient对象。对于Chrome APK来说,这个ContentClient对象的实际类型为ChromeContentClient,也就是这里调用函数GetContentClient获得的是一个ChromeContentClient对象。

有了这个ChromeContentClient对象之后,就可以调用它的成员函数browser获得一个ChromeContentBrowserClient对象。有了这个ChromeContentBrowserClient对象,就可以调用它的成员函数CreateBrowserMainParts创建一个BrowserMainParts对象了,如下所示:

content::BrowserMainParts* ChromeContentBrowserClient::CreateBrowserMainParts(
        const content::MainFunctionParams& parameters) {
      ChromeBrowserMainParts* main_parts;
      // Construct the Main browser parts based on the OS type.
    #if defined(OS_WIN)
      main_parts = new ChromeBrowserMainPartsWin(parameters);
    #elif defined(OS_MACOSX)
      main_parts = new ChromeBrowserMainPartsMac(parameters);
    #elif defined(OS_CHROMEOS)
      main_parts = new chromeos::ChromeBrowserMainPartsChromeos(parameters);
    #elif defined(OS_LINUX)
      main_parts = new ChromeBrowserMainPartsLinux(parameters);
    #elif defined(OS_ANDROID)
      main_parts = new ChromeBrowserMainPartsAndroid(parameters);
    #elif defined(OS_POSIX)
      main_parts = new ChromeBrowserMainPartsPosix(parameters);
    #else
      NOTREACHED();
      main_parts = new ChromeBrowserMainParts(parameters);
    #endif

      ......

      return main_parts;
    }

这个函数定义在文件external/chromium_org/chrome/browser/chrome_content_browser_client.cc中。

从这里可以看到,在Android平台上,ChromeContentBrowserClient类的成员函数CreateBrowserMainParts创建的是一个ChromeBrowserMainPartsAndroid对象。这个ChromeBrowserMainPartsAndroid对象是从ChromeBrowserMainParts类继承下来的。

这一步执行完成之后,BrowserMainLoop对象的成员函数Init就创建了一个ChromeBrowserMainPartsAndroid对象,并且保存在成员变量parts_中。回到前面分析的BrowserMainRunnerImpl类的成员函数Initialize中,它最后会调用前面已经初始化好的BrowserMainLoop对象的成员函数CreateStartupTasks创建一系列Startup Tasks,用来初始化Content模块。这其中就包含了一个类型为PreMainMessageLoopRun的Startup Task,也就是在Browser进程的主线程进入消息循环前执行的Startup Task,如下所示:

void BrowserMainLoop::CreateStartupTasks() {
      .....

      // First time through, we really want to create all the tasks
      if (!startup_task_runner_.get()) {
    #if defined(OS_ANDROID)
        startup_task_runner_ = make_scoped_ptr(new StartupTaskRunner(
            base::Bind(&BrowserStartupComplete),
            base::MessageLoop::current()->message_loop_proxy()));
    #else
        startup_task_runner_ = make_scoped_ptr(new StartupTaskRunner(
            base::Callback<void(int)>(),
            base::MessageLoop::current()->message_loop_proxy()));
    #endif
        ......

        StartupTask pre_main_message_loop_run = base::Bind(
            &BrowserMainLoop::PreMainMessageLoopRun, base::Unretained(this));
        startup_task_runner_->AddTask(pre_main_message_loop_run);

        ......
      }
    #if defined(OS_ANDROID)
      if (!BrowserMayStartAsynchronously()) {
        // A second request for asynchronous startup can be ignored, so
        // StartupRunningTasksAsync is only called first time through. If, however,
        // this is a request for synchronous startup then it must override any
        // previous call for async startup, so we call RunAllTasksNow()
        // unconditionally.
        startup_task_runner_->RunAllTasksNow();
      }
    #else
      startup_task_runner_->RunAllTasksNow();
    #endif
    }

这个函数定义在文件external/chromium_org/content/browser/browser_main_loop.cc中。

BrowserMainLoop类的成员函数CreateStartupTasks首先会检查成员变量startup_task_runner_是否还没有指向一个StartupTaskRunner对象。如果没有指向,那么就会创建一个StartupTaskRunner对象让它指向。这个StartupTaskRunner对象可以用来向当前线程(Browser进程的主线程)的消息队列发送消息,从而可以执行指定的Startup Task。

类型为PreMainMessageLoopRun的Startup Task绑定了BrowserMainLoop类的成员函数PreMainMessageLoopRun。这意味着接下来BrowserMainLoop类的成员函数PreMainMessageLoopRun会在Browser进程的主线程执行,如下所示:

int BrowserMainLoop::PreMainMessageLoopRun() {
      if (parts_) {
        ......
        parts_->PreMainMessageLoopRun();
      }

      ......
      return result_code_;
    }

这个函数定义在文件external/chromium_org/content/browser/browser_main_loop.cc中。

从前面的分析可以知道,BrowserMainLoop类的成员变量parts_指向的是一个ChromeBrowserMainPartsAndroid对象。BrowserMainLoop类的成员函数PreMainMessageLoopRun调用这个ChromeBrowserMainPartsAndroid对象的成员函数PreMainMessageLoopRun执行Browser进程在PreMainMessageLoopRun阶段的初始化工作。

ChromeBrowserMainPartsAndroid类的成员函数PreMainMessageLoopRun是从父类ChromeBrowserMainParts继承下来的,它的实现如下所示:

void ChromeBrowserMainParts::PreMainMessageLoopRun() {
      ......

      result_code_ = PreMainMessageLoopRunImpl();

      ......
    }

这个函数定义在文件external/chromium_org/chrome/browser/chrome_browser_main.cc中。

ChromeBrowserMainParts类的成员函数PreMainMessageLoopRun调用另外一个成员函数PreMainMessageLoopRunImpl执行Browser进程在PreMainMessageLoopRun阶段的初始化工作,如下所示:

int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() {
      ......

      profile_ = CreatePrimaryProfile(parameters(),
                                      user_data_dir_,
                                      parsed_command_line());
      ......

      return result_code_;
    }

这个函数定义在文件external/chromium_org/chrome/browser/chrome_browser_main.cc中。

ChromeBrowserMainParts类的成员函数PreMainMessageLoopRunImpl执行了一系列的初始化工作。其中的一个初始化工作是为当前登录的用户创建Profile。这是通过调用函数CreatePrimaryProfile实现的。在创建Profile的过程中,就会加载为当前登录的用户安装的Extension。

接下来我们就继续分析函数CreatePrimaryProfile的实现,如下所示:

Profile* CreatePrimaryProfile(const content::MainFunctionParams& parameters,
                                  const base::FilePath& user_data_dir,
                                  const CommandLine& parsed_command_line) {
      ......

      Profile* profile = NULL;
    #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
      ......
      profile = ProfileManager::GetActiveUserProfile();
    #else
      ......
    #endif
      if (profile) {
        ......
        return profile;
      }

      ......

      return NULL;
    }

这个函数定义在文件external/chromium_org/chrome/browser/chrome_browser_main.cc中。

在Android平台上,函数CreatePrimaryProfile调用ProfileManager类的静态成员函数GetActiveUserProfile获得当前用户的Profile,如下所示:

Profile* ProfileManager::GetActiveUserProfile() {
      ProfileManager* profile_manager = g_browser_process->profile_manager();
      ......
      Profile* profile =
          profile_manager->GetActiveUserOrOffTheRecordProfileFromPath(
              profile_manager->user_data_dir());
      ......
      return profile;
    }

这个函数定义在文件external/chromium_org/chrome/browser/profiles/profile_manager.cc中。

ProfileManager类的静态成员函数GetActiveUserProfile首先在当前进程(Browser进程)中获得一个ProfileManager单例对象。通过调用这个ProfileManager单例对象的成员函数user_data_dir可以获得当前用户的数据目录。有了这个数据目录之后 ,再调用上述ProfileManager单例对象的成员函数GetActiveUserOrOffTheRecordProfileFromPath就可以获得当前用户的Profile,如下所示:

Profile* ProfileManager::GetActiveUserOrOffTheRecordProfileFromPath(
        const base::FilePath& user_data_dir) {
    #if defined(OS_CHROMEOS)
      ......
    #else
      base::FilePath default_profile_dir(user_data_dir);
      default_profile_dir = default_profile_dir.Append(GetInitialProfileDir());
      return GetProfile(default_profile_dir);
    #endif
    }

这个函数定义在文件external/chromium_org/chrome/browser/profiles/profile_manager.cc中。

ProfileManager类的成员函数GetActiveUserOrOffTheRecordProfileFromPath首先调用另外一个成员函数etInitialProfileDir获得Profile目录。这个Profile目录是相对参数user_data_dir描述的数据目录之下的。将Profile目录添加到数据目录之后,就得到Profile目录的绝对路径。有了这个绝对路径之后,ProfileManager类的成员函数GetActiveUserOrOffTheRecordProfileFromPath就调用成员函数GetProfile获得当前用户的Profile,如下所示:

Profile* ProfileManager::GetProfile(const base::FilePath& profile_dir) {
      TRACE_EVENT0("browser", "ProfileManager::GetProfile")
      // If the profile is already loaded (e.g., chrome.exe launched twice), just
      // return it.
      Profile* profile = GetProfileByPath(profile_dir);
      if (NULL != profile)
        return profile;

      profile = CreateProfileHelper(profile_dir);
      DCHECK(profile);
      if (profile) {
        bool result = AddProfile(profile);
        DCHECK(result);
      }
      return profile;
    }

这个函数定义在文件external/chromium_org/chrome/browser/profiles/profile_manager.cc中。

ProfileManager类的成员函数GetProfile首先调用成员函数GetProfileInfoByPath检查是否已经为参数profile_dir描述的Profile目录创建过Profile。如果已经创建,那么就将该Profile返回给调用者。否则的话,就会调用成员函数CreateProfileHelper为参数profile_dir描述的Profile目录创建一个Profile,并且调用另外一个成员函数AddProfile将其保存在内部,以及返回给调用者。

ProfileManager类的成员函数AddProfile在将当前用户的Profile保存在内部之后,会根据Profile的内容执行相应初始化工作,如下所示:

bool ProfileManager::AddProfile(Profile* profile) {
      ......

      RegisterProfile(profile, true);
      ......
      DoFinalInit(profile, ShouldGoOffTheRecord(profile));
      return true;
    }

这个函数定义在文件external/chromium_org/chrome/browser/profiles/profile_manager.cc中。

ProfileManager类的成员函数AddProfile首先调用成员函数RegisterProfile将参数profile描述的Profile保存在内部,接下来调用另外一个成员函数DoFinalnit根据该Profile执行相应的初始化工作,其中就包括创建Extension Service,如下所示:

void ProfileManager::DoFinalInit(Profile* profile, bool go_off_the_record) {
      DoFinalInitForServices(profile, go_off_the_record);
      ......
    }

这个函数定义在文件external/chromium_org/chrome/browser/profiles/profile_manager.cc中。

ProfileManager类的成员函数DoFinalnit是在调用成员函数DoFinalInitForServices的过程中创建Extension Service的,如下所示:

void ProfileManager::DoFinalInitForServices(Profile* profile,
                                                bool go_off_the_record) {
    #if defined(ENABLE_EXTENSIONS)
      extensions::ExtensionSystem::Get(profile)->InitForRegularProfile(
          !go_off_the_record);
      ......
    #endif
      ......
    }

这个函数定义在文件external/chromium_org/chrome/browser/profiles/profile_manager.cc中。

从这里可以看到,在定义了宏ENABLE_EXTENSIONS的情况下,Chromium才会支持Extension。这时候ProfileManager类的成员函数DoFinalInitForServices首先根据参数profile描述的Profile获得一个ExtensionSystemImpl对象,然后再调用这个ExtensionSystemImpl对象的成员函数InitForRegularProfile创建一个Extension Service,如下所示:

void ExtensionSystemImpl::InitForRegularProfile(bool extensions_enabled) {
      ......

      process_manager_.reset(ProcessManager::Create(profile_));

      shared_->Init(extensions_enabled);
    }

这个函数定义在文件external/chromium_org/chrome/browser/extensions/extension_system_impl.cc中。

ExtensionSystemImpl类的成员函数InitForRegularProfile首先会调用ProcessManager类的静态成员函数Create创建一个ProcessManager对象,并且保存在成员变量processmanager。在接下来一篇文章中,我们就会看到,Extension的Background Page就是通过这个ProcessManager对象加载起来的。

ExtensionSystemImpl类的成员变量shared_指向的是一个ExtensionSystemImpl::Shared对象。ExtensionSystemImpl类的成员函数InitForRegularProfile调用这个ExtensionSystemImpl::Shared对象的成员函数Init创建一个Extension Service,如下所示:

void ExtensionSystemImpl::Shared::Init(bool extensions_enabled) {
      ......

      user_script_master_ = new UserScriptMaster(profile_);
      ......

      extension_service_.reset(new ExtensionService(
          profile_,
          CommandLine::ForCurrentProcess(),
          profile_->GetPath().AppendASCII(extensions::kInstallDirectoryName),
          ExtensionPrefs::Get(profile_),
          blacklist_.get(),
          autoupdate_enabled,
          extensions_enabled,
          &ready_));
      ......

      extension_service_->Init();

      ......
    }

这个函数定义在文件external/chromium_org/chrome/browser/extensions/extension_system_impl.cc中。

Extension Service通过一个ExtensionService对象描述。ExtensionSystemImpl::Shared类的成员函数Init创建了这个ExtensionService对象之后,会保存在成员变量extension_service_中,并且调用这个ExtensionService对象的成员函数对其描述的Extension Service进行初始化,如下所示:

此外,我们还看到,ExtensionSystemImpl::Shared类的成员函数Init还创建了一个UserScriptMaster对象保存在成员变量user_script_master_中。这个UserScriptMaster是用来管理接下来要加载的Extension的Content Script的。这一点我们在后面的文章会进行详细分析。

现在,我们主要关注Extension Service的初始化过程。因此,接下来我们继续分析ExtensionService类的成员函数Init的实现,如下所示:

void ExtensionService::Init() {
      ......

      const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
      if (cmd_line->HasSwitch(switches::kInstallFromWebstore) ||
          cmd_line->HasSwitch(switches::kLimitedInstallFromWebstore)) {
        ......
      } else {
        ......

        // LoadAllExtensions() calls OnLoadedInstalledExtensions().
        ......
        extensions::InstalledLoader(this).LoadAllExtensions();
        ......

        // Attempt to re-enable extensions whose only disable reason is reloading.
        std::vector<std::string> extensions_to_enable;
        const ExtensionSet& disabled_extensions = registry_->disabled_extensions();
        for (ExtensionSet::const_iterator iter = disabled_extensions.begin();
            iter != disabled_extensions.end(); ++iter) {
          const Extension* e = iter->get();
          if (extension_prefs_->GetDisableReasons(e->id()) ==
              Extension::DISABLE_RELOAD) {
            extensions_to_enable.push_back(e->id());
          }
        }
        for (std::vector<std::string>::iterator it = extensions_to_enable.begin();
             it != extensions_to_enable.end(); ++it) {
          EnableExtension(*it);
        }

        ......
      }

      ......
    }

这个函数定义在文件external/chromium_org/chrome/browser/extensions/extension_service.cc中。

ExtensionService类的成员函数Init首先检查Browser进程的启动参数是否包含有switches::kInstallFromWebstore或者switches::kLimitedInstallFromWebstore选项。如果包含有,那么就只会从Web Store上加载当前用户的Extension。我们假设没有包含这两个选项,那么ExtensionService类的成员函数Init将会从本地加载当前用户的Extension。

ExtensionService类的成员函数Init首先构造一个InstalledLoader对象,然后再调用这个InstalledLoader对象的成员函数LoadAllExtensions加载当前用户安装的所有Extension。这些加载的Extension,即有Enabled的,也有Disabled的。

加载后的Extension会保存在ExtensionService类的成员变量registry_描述的一个Extension Registry中。ExtensionService类的成员函数Init最后会从这个Extension Registry获得那些处于Enabled状态的Extension,并且调用另外一个成员函数EnableExtension启用它们。

接下来,我们主要关注Extension的加载过程。因此,我们继续分析InstalledLoader类的成员函数LoadAllExtensions的实现,如下所示:

void InstalledLoader::LoadAllExtensions() {
      ......

      Profile* profile = extension_service_->profile();
      scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
          extension_prefs_->GetInstalledExtensionsInfo());

      ......

      for (size_t i = 0; i < extensions_info->size(); ++i) {
        if (extensions_info->at(i)->extension_location != Manifest::COMMAND_LINE)
          Load(*extensions_info->at(i), should_write_prefs);
      }

      ......
    }

这个函数定义在文件external/chromium_org/chrome/browser/extensions/installed_loader.cc中。

InstalledLoader类的成员函数LoadAllExtensions首先获得当前用户安装的所有Extension。注意,这些Extension既包括用户在"chrome://extensions"页面中安装的Extension,也包括用户在启动Chromium时通过命令行参数"--load-extension"指定的Extension。不过,InstalledLoader类的成员函数LoadAllExtensions只会加载那些非命令行参数指定的Extension。对于命令行参数指定的Extension,在Extension Service初始化结束后,Extension System会通过另外一个Unpacked Installer来加载它们。

InstalledLoader类的成员函数LoadAllExtensions是通过调用另外一个成员函数Load加载那些非命令行参数指定的Extension的,如下所示:

void InstalledLoader::Load(const ExtensionInfo& info, bool write_to_prefs) {
      std::string error;
      scoped_refptr<const Extension> extension(NULL);
      if (info.extension_manifest) {
        extension = Extension::Create(
            info.extension_path,
            info.extension_location,
            *info.extension_manifest,
            GetCreationFlags(&info),
            &error);
      } else {
        error = errors::kManifestUnreadable;
      }

      ......

      extension_service_->AddExtension(extension.get());
    }

这个函数定义在文件external/chromium_org/chrome/browser/extensions/installed_loader.cc中。

InstalledLoader类的成员函数Load首先是根据参数info描述的Extension Info创建一个Extension对象,这是通过调用Extension类的静态成员函数Create实现的。这个Extension对象最终会交给前面创建的Extension Service处理。当Extension Service处理完毕,参数info描述的Extension就加载完毕。

InstalledLoader类的成员变量extension_service_指向的是一个ExtensionService对象。这个ExtensionService对象描述的就是前面创建的Extension Service。通过调用这个ExtensionService对象的成员函数AddExtension即可以将参数info描述的Extension交给前面创建的Extension Service处理。处理过程如下所示:

void ExtensionService::AddExtension(const Extension* extension) {
      ......

      if (extension_prefs_->IsExtensionBlacklisted(extension->id())) {
        ......
        registry_->AddBlacklisted(extension);
      } else if (!reloading &&
                 extension_prefs_->IsExtensionDisabled(extension->id())) {
        registry_->AddDisabled(extension);
        ......
      } else if (reloading) {
        ......
        EnableExtension(extension->id());
      } else {
        ......
        registry_->AddEnabled(extension);
        ......
        NotifyExtensionLoaded(extension);
      }
      ......
    }

这个函数定义在文件external/chromium_org/chrome/browser/extensions/extension_service.cc中。

ExtensionService类的成员函数AddExtension会判断参数extension描述的Extension的状态,并且执行的操作:

1. 如果它被用户列入黑名单,那么就将它记录在Extension Registry内部的Black List上。

2. 如果它被用户禁用,那么将它记录在Extension Registry内部的Disabled List上。

3. 如果它被重新加载,那么对它执行一个Enable操作。

4. 如果它是第一次加载,那么将它记录在Extension Registry内部的Enabled List上。

接下来我们主要关注第4种情况。这时候ExtensionService类的成员函数AddExtension首先会调用成员变量registry_指向的一个ExtensionRegistry对象的成员函数AddEnabled将参数extension描述的Extension记录在Extension Registry内部的Enabled List上,接下来又调用另外一个成员函数NotifyExtensionLoaded通知其它模块,有一个新的Extension被加载。

接下来我们就继续分析ExtensionRegistry类的成员函数AddEnabled和ExtensionService类的成员函数NotifyExtensionLoaded的实现,以便完整了解Extension的加载过程。

ExtensionRegistry类的成员函数AddEnabled的实现如下所示:

bool ExtensionRegistry::AddEnabled(
        const scoped_refptr<const Extension>& extension) {
      return enabled_extensions_.Insert(extension);
    }

这个函数定义在文件external/chromium_org/extensions/browser/extension_registry.cc中。

ExtensionRegistry类的成员变量enabled_extensions_描述的就是一个Enabled List,因此ExtensionRegistry类的成员函数AddEnabled会将参数extension描述的Extension保存在里面。

ExtensionService类的成员函数NotifyExtensionLoaded的实现如下所示:

void ExtensionService::NotifyExtensionLoaded(const Extension* extension) {
      ......

      registry_->TriggerOnLoaded(extension);

      ......
    }

这个函数定义在文件external/chromium_org/chrome/browser/extensions/extension_service.cc中。

ExtensionService类的成员函数NotifyExtensionLoaded会通过Extension Registry通知其它模块有一个新的Extension被加载,这是通过调用成员变量registry_指向的一个ExtensionRegistry对象的成员函数TriggerOnLoaded实现的,如下所示:

void ExtensionRegistry::TriggerOnLoaded(const Extension* extension) {
      DCHECK(enabled_extensions_.Contains(extension->id()));
      FOR_EACH_OBSERVER(ExtensionRegistryObserver,
                        observers_,
                        OnExtensionLoaded(browser_context_, extension));
    }

这个函数定义在文件external/chromium_org/extensions/browser/extension_registry.cc中。

一个模块如果需要关注新加载的Extension,那么就会注册一个Extension Registry Observer到Extension Registry的内部。这些Extension Registry Observer保存在ExtensionRegistry类的成员变量observers_描述的一个List中。

ExtensionRegistry类的成员函数TriggerOnLoaded所做的事情就是调用每一个注册在Extension Registry中的Extension Registry Observer的成员函数OnExtensionLoaded,分别通知它们有一个新的Extension被加载。在后面的文章中,我们就会看到,负责管理Content Script的User Script Master模块会注册一个Extension Registry Observer到Extension Registry,目的就是获取每一个新加载的Extension指定的Content Script,以便在合适的时候注入到宿主网页中去执行。

至此,我们就分析完成了Chromium加载Extension的过程。这是在Extension Service的初始化过程中执行的。这个Extension Service又是在Chromium为当前用户创建Profile的过程中启动的。最后,为当前用户创建Profile的操作是在Chromium启动的过程中执行的。这意味着Extension是在Chromium启动的时候加载的,也就是在Chromium的Browser进程启动时加载。在接下来的一篇文章中,我们继续分析Extension的Background Page和Popup Page的加载过程,敬请关注!更多的信息也可以关注老罗的新浪微博:http://weibo.com/shengyangluo

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 目录