修改args.gn

enable_ppapi = true
enable_plugins = true

  • 打开 ppapi 开关:
    • render_frame_impl.cc 中,有#if BUILDFLAG(ENABLE_PPAPI) 宏定义
    • ./ppapi/buildflags/BUILD.gn 中,有 "ENABLE_PPAPI=$enable_ppapi" 的定义
  • ppapi/buildflags 配置:
    • buildflags.gni 中,明确如下:
enable_plugins = !is_android && !is_ios && !is_castos
enable_ppapi = enable_plugins && !is_fuchsia

编译plugin:

autoninja -C out/Default plugin_name  // plugin name 在对应的ppapi的plugin sample中,找BUILD.gn

修改相关的白名单策略:

  • plugin想要通过command line进行启动的话,需要修改对应的mimetype的名单。
  • pepper_plugin_list.cc

// Returns true if the plugin can be registered from the command line.
bool IsAllowedFromCommandLine(const ContentPluginInfo& plugin) {
  // TODO(crbug.com/1134683): Empty the allowlist.
  static constexpr auto kMimeTypeAllowlist =
      base::MakeFixedFlatSet<base::StringPiece>({
          "application/x-blink-deprecated-test-plugin",
          "application/x-blink-test-plugin",
          "application/x-ppapi-example-media-stream-video",
          "application/x-ppapi-example-gles2-spinning-cube",
          "application/x-ppapi-example-2d",
          "application/x-ppapi-example-2d-scaling",
          "application/x-ppapi-tests",
      });

  bool allowed = true;

  DCHECK_GE(plugin.mime_types.size(), 1u);
  for (const auto& mime_type : plugin.mime_types) {
    if (!kMimeTypeAllowlist.contains(mime_type.mime_type)) {
      allowed = false;
      DVLOG(1) << "MIME type not allowed: " << mime_type.mime_type;
    }
  }

  return allowed;
}

全量编译:

autoninja -C out/Default chrome

运行plugin:

./out/Default/chrome --allow-command-line-plugins --enable-logging --v=1 --use-gpu-in-tests --register-pepper-plugins="/home/xuanweihong/Solid/Chromium/LinuxChromium/src/out/Default/libppapi_example_paint_manager.so;application/x-ppapi-example-2d" file:///home/xuanweihong/Solid/Chromium/LinuxChromium/src/ppapi/examples/2d/2d.html

参数描述:

  • --allow-command-line-plugins
    • 允许命令行plugin
  • --use-gpu-in-tests
    • 开启gpu渲染
  • --register-pepper-plugins
    • 注册插件
  • -enable-logging --v=1
    • 开启log,终端运行时log会输出,verbose级别为:1

This Plugin is not supported:

  • 怀疑google关闭了所有webPlugin load的能力。

溯源如下:

  • 对应字符串在这里:
./components/components_strings.grd:436:      <message name="IDS_PLUGIN_NOT_SUPPORTED" desc="The placeholder text for an unsupported plugin.">

  • 字符串对应的code在这里:
//./chrome/renderer/plugins/chrome_plugin_placeholder.cc:134:  values.Set("message", l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED));

// TODO(bauerb): Move this method to NonLoadablePluginPlaceholder?
// static
ChromePluginPlaceholder* ChromePluginPlaceholder::CreateLoadableMissingPlugin(
    content::RenderFrame* render_frame,
    const blink::WebPluginParams& params) {
  std::string template_html =
      ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
          IDR_BLOCKED_PLUGIN_HTML);

  base::Value::Dict values;
  values.Set("name", "");
  values.Set("message", l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED));

  std::string html_data = webui::GetI18nTemplateHtml(template_html, values);

  // Will destroy itself when its WebViewPlugin is going away.
  return new ChromePluginPlaceholder(render_frame, params, html_data,
                                     params.mime_type.Utf16());
}

  • 调用CreateLoadableMissingPlugin()函数的地方:
./chrome/renderer/chrome_content_renderer_client.cc:955:      placeholder = ChromePluginPlaceholder::CreateLoadableMissingPlugin(

ebPlugin* ChromeContentRendererClient::CreatePlugin(
    content::RenderFrame* render_frame,
    const WebPluginParams& original_params,
    const chrome::mojom::PluginInfo& plugin_info) {
  const WebPluginInfo& info = plugin_info.plugin;
  const std::string& actual_mime_type = plugin_info.actual_mime_type;
  const std::u16string& group_name = plugin_info.group_name;
  const std::string& identifier = plugin_info.group_identifier;
  chrome::mojom::PluginStatus status = plugin_info.status;
  GURL url(original_params.url);
  std::string orig_mime_type = original_params.mime_type.Utf8();
  ChromePluginPlaceholder* placeholder = nullptr;

  // If the browser plugin is to be enabled, this should be handled by the
  // renderer, so the code won't reach here due to the early exit in
  // OverrideCreatePlugin.
  if (status == chrome::mojom::PluginStatus::kNotFound ||
      orig_mime_type == content::kBrowserPluginMimeType) {
    // Flash has been thoroughly removed in M88+, so we need to have a special
    // case here to display a deprecated message instead of a generic
    // plugin-missing message.
    if (orig_mime_type == "application/x-shockwave-flash" ||
        orig_mime_type == "application/futuresplash") {
      return NonLoadablePluginPlaceholder::CreateFlashDeprecatedPlaceholder(
                 render_frame, original_params)
          ->plugin();
    } else {
      PluginUMAReporter::GetInstance()->ReportPluginMissing(orig_mime_type,
                                                            url);
      placeholder = ChromePluginPlaceholder::CreateLoadableMissingPlugin(
          render_frame, original_params);
    }
  } else {
      //... other code
  }
}

// 关键log: status 命中了chrome::mojom::PluginStatus::kNotFound ,
// 需要看下CreatePlugin() 的入参 ->  const chrome::mojom::PluginInfo& plugin_info

  • CreatePlugin()调用:
    • 来自OverrideCreatePlugin()
bool ChromeContentRendererClient::OverrideCreatePlugin(
    content::RenderFrame* render_frame,
    const WebPluginParams& params,
    WebPlugin** plugin) {
  ...
#if BUILDFLAG(ENABLE_PLUGINS)
  mojo::AssociatedRemote<chrome::mojom::PluginInfoHost> plugin_info_host;
  render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
      &plugin_info_host);

  chrome::mojom::PluginInfoPtr plugin_info = chrome::mojom::PluginInfo::New();
  plugin_info_host->GetPluginInfo(
      url, render_frame->GetWebFrame()->Top()->GetSecurityOrigin(),
      orig_mime_type, &plugin_info);
  *plugin = CreatePlugin(render_frame, params, *plugin_info);
...
}
  • 关键在plugin_info_host→GetPluginInfo(),PluginInfo是数据来自它:

    • chrome::mojom::PluginInfoHost→GetPluginInfo()
  • 这里比较巧妙:

    //chrome::mojom::PluginInfoHost -> PluginInfoHostImpl
    
    //chrome::mojom::PluginInfoHost在out目录下,是编译生成的,它继承了PluginInfoHostInterfaceBase接口
    //具体有代码如下:
    
    class PluginInfoHost
        : public PluginInfoHostInterfaceBase {
    ...
    	virtual bool GetPluginInfo(const ::GURL& url, const ::url::Origin& origin, const std::string& mime_type, PluginInfoPtr* out_plugin_info);
    
      using GetPluginInfoCallback = base::OnceCallback<void(PluginInfoPtr)>;
    
      virtual void GetPluginInfo(const ::GURL& url, const ::url::Origin& origin, const std::string& mime_type, GetPluginInfoCallback callback) = 0;
    ...
    //所以从上面来看,因为callback其实相当于OnceCallback,那也就是调用最终会走到callback的函数中去
    //PluginInfoHostImpl正好也override了对应的带有GetPluginInfoCallback的函数,实现如下:
    
    void PluginInfoHostImpl::GetPluginInfo(const GURL& url,
                                           const url::Origin& origin,
                                           const std::string& mime_type,
                                           GetPluginInfoCallback callback) {
      DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
      GetPluginInfo_Params params = {url, origin, mime_type};
    	//params是在这里构造的,应该是用来获取对应Plugin
      PluginService::GetInstance()->GetPlugins(
          base::BindOnce(&PluginInfoHostImpl::PluginsLoaded,
                         weak_factory_.GetWeakPtr(), params, std::move(callback)));
    	//关键还是看GetPlugins()
    }
    
  • PluginService:

    • 代码在这里content/public/browser/plugin_service.h

      class CONTENT_EXPORT PluginService {
      	virtual void GetPlugins(GetPluginsCallback callback) = 0;
      }
      
    • 实现类是:content/browser/plugin_service_impl.cc

      void PluginServiceImpl::GetPlugins(GetPluginsCallback callback) {
        DCHECK_CURRENTLY_ON(BrowserThread::UI);
      
        // Run `callback` later, to stay compatible with prior behavior.
        base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
            FROM_HERE, base::BindOnce(std::move(callback), GetPluginsSynchronous()));//关键还是GetPluginsSynchronous
      }
      
      std::vector<WebPluginInfo> PluginServiceImpl::GetPluginsSynchronous() {
        DCHECK_CURRENTLY_ON(BrowserThread::UI);
        std::vector<WebPluginInfo> plugins;
        PluginList::Singleton()->GetPlugins(&plugins);
        return plugins;
      }
      
  • 接下来是PluginList了:

    • header:

      • content/browser/plugin_list.h
      class CONTENT_EXPORT PluginList{
      // Get all the plugins synchronously, loading them if necessary.
        void GetPlugins(std::vector<WebPluginInfo>* plugins);
      }
      
    • impl:

      • content/browser/plugin_list.cc
      void PluginList::GetPlugins(std::vector<WebPluginInfo>* plugins) {
        DCHECK_CURRENTLY_ON(BrowserThread::UI);
        LoadPlugins();//首先是load
        plugins->insert(plugins->end(), plugins_list_.begin(), plugins_list_.end());//然后是insert结果
      }
      
      void PluginList::LoadPlugins() {
        DCHECK_CURRENTLY_ON(BrowserThread::UI);
      
        if (!PrepareForPluginLoading())
          return;
      
        std::vector<WebPluginInfo> new_plugins;
        std::vector<base::FilePath> plugin_paths;
        GetPluginPathsToLoad(&plugin_paths);//根据plugin的path,去load Plugin
      
        for (const base::FilePath& path : plugin_paths) {
          WebPluginInfo plugin_info;
          LoadPluginIntoPluginList(path, &new_plugins, &plugin_info);
        }
      
        SetPlugins(new_plugins);
      }
      
      void PluginList::GetPluginPathsToLoad(
          std::vector<base::FilePath>* plugin_paths) {
        DCHECK_CURRENTLY_ON(BrowserThread::UI);
      
        for (const base::FilePath& path : extra_plugin_paths_) {
          if (base::Contains(*plugin_paths, path))
            continue;
          plugin_paths->push_back(path);
        }
      }
      

安全问题:

  • native client 允许开发者编写 C/C++ plugin
  • 通过 IPC 和 JS 通信
  • 可以通过 PPAPI 使用浏览器提供的功能,比如 browser 的 OpenGL 能力
  • 编译 PPAPI 的 plugin 需要借助 PPAPI 工具链
  • 因此 plugin 可以调用的 os api,是通过 PPAPI 锁死的
  • PPAPI 的 plugin 有 3 种运行模式:
    • 受限独立 process
    • 非受限独立进程
    • render 进程中

PPAPI 打印log


 评论