blob: bcdd737a87e2ea94fcc5d126ad21ba19fe497469 [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/plugins/renderer/plugin_placeholder.h"
#include "base/metrics/user_metrics_action.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/v8_value_converter.h"
#include "gin/object_template_builder.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
#include "third_party/blink/public/web/web_dom_message_event.h"
#include "third_party/blink/public/web/web_element.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_plugin_container.h"
#include "third_party/blink/public/web/web_script_source.h"
#include "third_party/blink/public/web/web_serialized_script_value.h"
#include "third_party/re2/src/re2/re2.h"
#include "v8/include/cppgc/allocation.h"
#include "v8/include/v8-cppgc.h"
namespace plugins {
// The placeholder is loaded in normal web renderer processes, so it should not
// have a chrome:// scheme that might let it be confused with a WebUI page.
const char kPluginPlaceholderDataURL[] = "data:text/html,pluginplaceholderdata";
PluginPlaceholderBase::PluginPlaceholderBase(
content::RenderFrame* render_frame,
const blink::WebPluginParams& params)
: content::RenderFrameObserver(render_frame), plugin_params_(params) {}
PluginPlaceholderBase::~PluginPlaceholderBase() = default;
void PluginPlaceholderBase::Init(const std::string& html_data) {
CHECK(!plugin_);
auto* frame = render_frame();
// The `WebViewPlugin::Delegate` represented by `this` can get called during
// the Create method, so this can't be in the constructor.
plugin_ = WebViewPlugin::Create(
frame->GetWebFrame()->View(), this,
frame ? frame->GetBlinkPreferences() : blink::web_pref::WebPreferences(),
html_data, GURL(kPluginPlaceholderDataURL));
}
const blink::WebPluginParams& PluginPlaceholderBase::GetPluginParams() const {
return plugin_params_;
}
void PluginPlaceholderBase::ShowContextMenu(const blink::WebMouseEvent& event) {
// Does nothing by default. Will be overridden if a specific browser wants
// a context menu.
return;
}
void PluginPlaceholderBase::PluginDestroyed() {
plugin_ = nullptr;
}
v8::Local<v8::Object> PluginPlaceholderBase::GetV8ScriptableObject(
v8::Isolate* isolate) const {
return v8::Local<v8::Object>();
}
void PluginPlaceholderBase::HidePlugin() {
hidden_ = true;
if (!plugin())
return;
blink::WebPluginContainer* container = plugin()->Container();
blink::WebElement element = container->GetElement();
element.SetAttribute("style", "display: none;");
// If we have a width and height, search for a parent (often <div>) with the
// same dimensions. If we find such a parent, hide that as well.
// This makes much more uncovered page content usable (including clickable)
// as opposed to merely visible.
// TODO(cevans) -- it's a foul heuristic but we're going to tolerate it for
// now for these reasons:
// 1) Makes the user experience better.
// 2) Foulness is encapsulated within this single function.
// 3) Confidence in no fasle positives.
// 4) Seems to have a good / low false negative rate at this time.
if (element.HasAttribute("width") && element.HasAttribute("height")) {
std::string width_str("width:[\\s]*");
width_str += element.GetAttribute("width").Utf8();
if (base::EndsWith(width_str, "px", base::CompareCase::INSENSITIVE_ASCII)) {
width_str = width_str.substr(0, width_str.length() - 2);
}
base::TrimWhitespaceASCII(width_str, base::TRIM_TRAILING, &width_str);
width_str += "[\\s]*px";
std::string height_str("height:[\\s]*");
height_str += element.GetAttribute("height").Utf8();
if (base::EndsWith(height_str, "px",
base::CompareCase::INSENSITIVE_ASCII)) {
height_str = height_str.substr(0, height_str.length() - 2);
}
base::TrimWhitespaceASCII(height_str, base::TRIM_TRAILING, &height_str);
height_str += "[\\s]*px";
blink::WebNode parent = element;
while (!parent.ParentNode().IsNull()) {
parent = parent.ParentNode();
if (!parent.IsElementNode())
continue;
element = parent.To<blink::WebElement>();
if (element.HasAttribute("style")) {
std::string style_str = element.GetAttribute("style").Utf8();
if (RE2::PartialMatch(style_str, width_str) &&
RE2::PartialMatch(style_str, height_str))
element.SetAttribute("style", "display: none;");
}
}
}
}
void PluginPlaceholderBase::HideCallback() {
content::RenderThread::Get()->RecordAction(
base::UserMetricsAction("Plugin_Hide_Click"));
HidePlugin();
}
void PluginPlaceholderBase::NotifyPlaceholderReadyForTestingCallback() {
if (!plugin())
return;
// Set an attribute and post an event, so browser tests can wait for the
// placeholder to be ready to receive simulated user input.
blink::WebElement element = plugin()->Container()->GetElement();
element.SetAttribute("placeholderReady", "true");
blink::WebLocalFrame* frame = element.GetDocument().GetFrame();
base::Value value("placeholderReady");
blink::WebSerializedScriptValue message_data =
blink::WebSerializedScriptValue::Serialize(
frame->GetAgentGroupScheduler()->Isolate(),
content::V8ValueConverter::Create()->ToV8Value(
value, frame->MainWorldScriptContext()));
blink::WebDOMMessageEvent msg_event(message_data);
plugin()->Container()->EnqueueMessageEvent(msg_event);
}
void PluginPlaceholderBase::OnDestruct() {}
PluginPlaceholder::PluginPlaceholder(content::RenderFrame* render_frame,
const blink::WebPluginParams& params)
: PluginPlaceholderBase(render_frame, params) {
self_ = this;
}
PluginPlaceholder::~PluginPlaceholder() {
}
const gin::WrapperInfo* PluginPlaceholder::wrapper_info() const {
return &kWrapperInfo;
}
v8::Local<v8::Value> PluginPlaceholder::GetV8Handle(v8::Isolate* isolate) {
return GetWrapper(isolate).ToLocalChecked();
}
bool PluginPlaceholderBase::IsErrorPlaceholder() {
return false;
}
void PluginPlaceholder::OnDestruct() {
PluginPlaceholderBase::OnDestruct();
self_.Clear();
}
gin::ObjectTemplateBuilder PluginPlaceholder::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return gin::Wrappable<PluginPlaceholder>::GetObjectTemplateBuilder(isolate)
.SetMethod<void (plugins::PluginPlaceholder::*)()>(
"hide", &PluginPlaceholder::HideCallback);
}
// static
PluginPlaceholder* PluginPlaceholder::Create(
content::RenderFrame* render_frame,
const blink::WebPluginParams& params,
const std::string& html_data) {
auto* placeholder = cppgc::MakeGarbageCollected<PluginPlaceholder>(
render_frame->GetWebFrame()
->GetAgentGroupScheduler()
->Isolate()
->GetCppHeap()
->GetAllocationHandle(),
render_frame, params);
placeholder->Init(html_data);
return placeholder;
}
} // namespace plugins