chromium之content_shell源代码分析(一)

介绍和入口函数

为了测试chromium和webkit的兼容性,chromium project 中有一个content shell,相对整个项目来说,它简单了很多,如果运行的化,就是一个简单的windows窗口程序,有利于程序员测试webkit的最新特征,和掌握html5等相关的新功能。其入口点在content/shell目录下的shell_main.cc(对于windows系统是如此),打开该文件,可以看到如下代码:

#if defined(OS_WIN)

int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t*, int) {
  sandbox::SandboxInterfaceInfo sandbox_info = {0};
  content::InitializeSandboxInfo(&sandbox_info);
  content::ShellMainDelegate delegate;
  return content::ContentMain(instance, &sandbox_info, &delegate);
}

#else

int main(int argc, const char** argv) {
#if defined(OS_MACOSX)
  // Do the delegate work in shell_content_main to avoid having to export the
  // delegate types.
  return ::ContentMain(argc, argv);
#else
  content::ShellMainDelegate delegate;
  return content::ContentMain(argc, argv, &delegate);
#endif  // OS_MACOSX
}

#endif  // OS_POSIX


和chrome工程的区别

可以看到它和chrome/app/chrome_exe_main_win.cc中的:

int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prev, wchar_t*, int) {
  // Initialize the commandline singleton from the environment.
  CommandLine::Init(0, NULL);
  // The exit manager is in charge of calling the dtors of singletons.
  base::AtExitManager exit_manager;


  MetroDriver metro_driver;
  if (metro_driver.in_metro_mode())
    return metro_driver.RunInMetro(instance, &RunChrome);
  // Not in metro mode, proceed as normal.
  return RunChrome(instance);
}

的不同之处:chrome中的main delegate是ChromeMainDelegate,content shell中的main delegate 是ShellMainDelegate,是content::ContentMainDelegate的不同子类实现版本,这样的设计就决定了上层界面有很大的不同,相对chrome来说,content shell的界面功能简单了很多,有利于大家理解浏览器的程序结构;


进入MainRunner

进入到Content_main.cc中的如下代码后:

namespace content {

#if defined(OS_WIN)
int ContentMain(HINSTANCE instance,
                sandbox::SandboxInterfaceInfo* sandbox_info,
                ContentMainDelegate* delegate) {
#else
int ContentMain(int argc,
                const char** argv,
                ContentMainDelegate* delegate) {
#endif  // OS_WIN

  scoped_ptr<ContentMainRunner> main_runner(ContentMainRunner::Create());

  int exit_code;

#if defined(OS_WIN)
  exit_code = main_runner->Initialize(instance, sandbox_info, delegate);
#else
  exit_code = main_runner->Initialize(argc, argv, delegate);
#endif  // OS_WIN

  if (exit_code >= 0)
    return exit_code;

  exit_code = main_runner->Run();

  main_runner->Shutdown();

  return exit_code;
}

}  // namespace content

在ContentMainRunnerImpl中的Run函数中,调用delegate_(ShellMainDelegate的实例)的RunProcess,就会进入到content/shell/Shell_browser_main.cc中的如下代码:

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

  int exit_code = main_runner_->Initialize(parameters);

  if (exit_code >= 0)
    return exit_code;

  if (CommandLine::ForCurrentProcess()->HasSwitch(
        switches::kCheckLayoutTestSysDeps)) {
    MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
    main_runner_->Run();
    main_runner_->Shutdown();
    return 0;
  }

  bool layout_test_mode =
      CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree);

  if (layout_test_mode) {
    content::WebKitTestController test_controller;
    std::string test_string;
    CommandLine::StringVector args =
        CommandLine::ForCurrentProcess()->GetArgs();
    size_t command_line_position = 0;
    bool ran_at_least_once = false;

#if defined(OS_ANDROID)
    std::cout << "#READY\n";
    std::cout.flush();
#endif

    while (GetNextTest(args, &command_line_position, &test_string)) {
      if (test_string.empty())
        continue;
      if (test_string == "QUIT")
        break;

      bool enable_pixel_dumps;
      std::string pixel_hash;
      FilePath cwd;
      GURL test_url = GetURLForLayoutTest(
          test_string, &cwd, &enable_pixel_dumps, &pixel_hash);
      if (!content::WebKitTestController::Get()->PrepareForLayoutTest(
              test_url, cwd, enable_pixel_dumps, pixel_hash)) {
        break;
      }

      ran_at_least_once = true;
      main_runner_->Run();

      if (!content::WebKitTestController::Get()->ResetAfterLayoutTest())
        break;
    }
    if (!ran_at_least_once) {
      MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
      main_runner_->Run();
    }
    exit_code = 0;
  } else {
    exit_code = main_runner_->Run();
  }

  main_runner_->Shutdown();

  return exit_code;
}


进入MainLoop

这样的话,还是会进入到Browser_main_runner.cc的BrowserMainRunnerImpl类中,并呼叫其实列的Initialize函数和Run函数;在BrowserMainRunnerImpl的Initialize中会看到如下代码:

base::StatisticsRecorder::Initialize();

    notification_service_.reset(new NotificationServiceImpl);

    main_loop_.reset(new BrowserMainLoop(parameters));

    main_loop_->Init();

    main_loop_->EarlyInitialization();

    // Must happen before we try to use a message loop or display any UI.
    main_loop_->InitializeToolkit();

    main_loop_->MainMessageLoopStart();

在BrowserMainLoop的Init函数中,可以看到如下代码:

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

parts_的定义如下:

scoped_ptr<BrowserMainParts> parts_;

函数GetContentClient()在content/public/common/content_client.cc中定义如下:

void SetContentClient(ContentClient* client) {
  g_client = client;

  // Set the default user agent as provided by the client. We need to make
  // sure this is done before webkit_glue::GetUserAgent() is called (so that
  // the UA doesn't change).
  if (client) {
    webkit_glue::SetUserAgent(client->GetUserAgent(), false);
  }
}

ContentClient* GetContentClient() {
  return g_client;
}

和它对应的有个SetContentClient()函数;

让我们再回过头来看看前面的ContentMainRunnerImpl的Initialize函数中的部分代码:

if (!GetContentClient())
      SetContentClient(&empty_content_client_);
    ContentClientInitializer::Set(process_type, delegate_);

这段代码有点技巧在里面,好奇的人可以去阅读下这段代码中的相关函数就明白了,下面我们来看看ContentClientInitializer::Set函数:

 

 public:
  static void Set(const std::string& process_type,
                  ContentMainDelegate* delegate) {
    ContentClient* content_client = GetContentClient();
    if (process_type.empty()) {
      if (delegate)
        content_client->browser_ = delegate->CreateContentBrowserClient();
      if (!content_client->browser_)
        content_client->browser_ = &g_empty_content_browser_client.Get();
    }

#if !defined(OS_IOS)
    if (process_type == switches::kPluginProcess ||
        process_type == switches::kPpapiPluginProcess) {
      if (delegate)
        content_client->plugin_ = delegate->CreateContentPluginClient();
      if (!content_client->plugin_)
        content_client->plugin_ = &g_empty_content_plugin_client.Get();
    } else if (process_type == switches::kRendererProcess ||
               CommandLine::ForCurrentProcess()->HasSwitch(
                   switches::kSingleProcess)) {
      if (delegate)
        content_client->renderer_ = delegate->CreateContentRendererClient();
      if (!content_client->renderer_)
        content_client->renderer_ = &g_empty_content_renderer_client.Get();
    } else if (process_type == switches::kUtilityProcess) {
      if (delegate)
        content_client->utility_ = delegate->CreateContentUtilityClient();
      if (!content_client->utility_)
        content_client->utility_ = &g_empty_content_utility_client.Get();
    }
#endif  // !OS_IOS
  }

我们关注的地方在:

ContentClient* content_client = GetContentClient();
    if (process_type.empty()) {
      if (delegate)
        content_client->browser_ = delegate->CreateContentBrowserClient();
      if (!content_client->browser_)
        content_client->browser_ = &g_empty_content_browser_client.Get();
    }
    

      if (delegate)
        content_client->renderer_ = delegate->CreateContentRendererClient();
      if (!content_client->renderer_)
        content_client->renderer_ = &g_empty_content_renderer_client.Get();

这样就设置好了ShellContentClient的两个成员变量:

// The embedder API for participating in browser logic.
  ContentBrowserClient* browser_;
// The embedder API for participating in renderer logic.
  ContentRendererClient* renderer_;

分别为ShellMainDelegate的两个函数返回的结果:

ContentBrowserClient* ShellMainDelegate::CreateContentBrowserClient() {
  browser_client_.reset(new ShellContentBrowserClient);
  return browser_client_.get();
}
ContentRendererClient* ShellMainDelegate::CreateContentRendererClient() {
  renderer_client_.reset(new ShellContentRendererClient);
  return renderer_client_.get();
}


主要类图


理解了以上过程,对解释BrowserMainLoop的运行过程有帮助。

下面我把content_shell启动过程中涉及的几个主要类图贴上来,希望对理解有帮助


ContentMainDelegate类的子类有ChromeMainDelegate和ShellMainDelegate等,其实例一般通过Conten::Main()传入,我们一般关注BasicStartupComplete和RunProcess两个接口,负责浏览器的系统初始化和进入浏览器主循环的入口。

(完)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要调试 Chromium for Android 源代码,需要使用 Android Studio 和 Chromium for Android 源代码。以下是步骤: 1. 下载 Chromium for Android 源代码Chromium 的官方网站上下载 Chromium for Android 源代码。下载完成后,解压到本地。 2. 安装 Android Studio 从 Android Studio 的官方网站上下载最新版本的 Android Studio。安装完成后,打开 Android Studio。 3. 导入 Chromium for Android 项目 在 Android Studio 中,选择 File -> New -> Import Project。在弹出的窗口中,选择 Chromium for Android 源代码所在的文件夹。然后按照向导完成项目导入。 4. 配置 Chromium for Android 项目 在 Android Studio 中,打开 Project Structure 窗口。选择 app 模块,并在 Dependencies 标签页中添加 Chromium for Android 的依赖库。然后在 Build Variants 标签页中选择正确的 Build Variant。 5. 配置 Chromium for Android 运行环境 在 Android Studio 中,打开 Run/Debug Configurations 窗口。点击左上角的加号,添加一个新的 Android Application 配置。在 Configuration 标签页中选择正确的模块和启动 Activity。然后在 Profiling 标签页中选择正确的调试方式。最后点击 OK 保存配置。 6. 启动调试 在 Android Studio 中,点击 Run 按钮启动调试。如果一切正常,就会在设备或模拟器上启动 Chromium for Android 应用,并进入调试模式。 以上就是调试 Chromium for Android 源代码的步骤。需要注意的是,由于 Chromium for Android 是一个庞大而复杂的项目,调试起来可能比较困难,需要具备一定的 Android 开发和调试经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值