修改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 进程中