Commit 546c3d80 authored by Vladimir Levin's avatar Vladimir Levin Committed by Commit Bot

DOM: Hoist plugin disposal to past CreateAndAttachShadowRoot.

This patch ensures that we don't dispose plugins that are scheduled for
disposal during CreateAndAttachShadowRoot (which can happen via a call
to RemovedFromFlatTree). Since this function is acting in the script-
forbidden state, and plugins can run script on disposal (see referenced
bug), it causes a CHECK in certain cases. The fix here is to hoist
the disposal up to post forbidden scope state in CreateAndAttachShadowRoot
by suspending the disposal prior to suspending script.

This is consistent with things like UpdateStyleAndLayoutTree,
UpdateStyle, and Document::Shutdown, all of which suspend plugin
disposal prior to forbidding script.

R=masonfreed@chromium.org

Bug: 988318
Change-Id: Ib6363ce48eeb49f64691a519b8b542e805aa6527
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1728714Reviewed-by: default avatarMason Freed <masonfreed@chromium.org>
Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Commit-Queue: vmpstr <vmpstr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#683274}
parent 95b27a66
......@@ -117,4 +117,7 @@ specific_include_rules = {
"html_media_element_test.cc": [
"+base/test/gtest_util.h",
],
"element_test.cc": [
"+third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
]
}
......@@ -2799,6 +2799,7 @@ ShadowRoot& Element::CreateAndAttachShadowRoot(ShadowRootType type) {
NestingLevelIncrementer slot_assignment_recalc_forbidden_scope(
GetDocument().SlotAssignmentRecalcForbiddenRecursionDepth());
#endif
HTMLFrameOwnerElement::PluginDisposeSuspendScope suspend_plugin_dispose;
EventDispatchForbiddenScope assert_no_event_dispatch;
ScriptForbiddenScope forbid_script;
......
......@@ -5,17 +5,22 @@
#include "third_party/blink/renderer/core/dom/element.h"
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/web/web_plugin.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/dom_token_list.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/editing/testing/editing_test_base.h"
#include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/geometry/dom_rect.h"
#include "third_party/blink/renderer/core/html/html_html_element.h"
#include "third_party/blink/renderer/core/html/html_plugin_element.h"
#include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
namespace blink {
......@@ -464,4 +469,75 @@ TEST_F(ElementTest, OptionElementDisplayNoneComputedStyle) {
EXPECT_FALSE(document.getElementById("inner-option")->GetComputedStyle());
}
template <>
struct DowncastTraits<HTMLPlugInElement> {
static bool AllowFrom(const Node& n) { return IsHTMLPlugInElement(n); }
};
// A fake plugin which will assert that script is allowed in Destroy.
class ScriptOnDestroyPlugin : public GarbageCollected<ScriptOnDestroyPlugin>,
public WebPlugin {
public:
bool Initialize(WebPluginContainer* container) override {
container_ = container;
return true;
}
void Destroy() override {
destroy_called_ = true;
ASSERT_FALSE(ScriptForbiddenScope::IsScriptForbidden());
}
WebPluginContainer* Container() const override { return container_; }
void UpdateAllLifecyclePhases(WebWidget::LifecycleUpdateReason) override {}
void Paint(cc::PaintCanvas*, const WebRect&) override {}
void UpdateGeometry(const WebRect&,
const WebRect&,
const WebRect&,
bool) override {}
void UpdateFocus(bool, WebFocusType) override {}
void UpdateVisibility(bool) override {}
WebInputEventResult HandleInputEvent(const WebCoalescedInputEvent&,
WebCursorInfo&) override {
return {};
}
void DidReceiveResponse(const WebURLResponse&) override {}
void DidReceiveData(const char* data, size_t data_length) override {}
void DidFinishLoading() override {}
void DidFailLoading(const WebURLError&) override {}
void Trace(blink::Visitor*) {}
bool DestroyCalled() const { return destroy_called_; }
private:
WebPluginContainer* container_;
bool destroy_called_ = false;
};
TEST_F(ElementTest, CreateAndAttachShadowRootSuspendsPluginDisposal) {
Document& document = GetDocument();
SetBodyContent(R"HTML(
<div id=target>
<embed id=plugin type=application/x-blink-text-plugin></embed>
</div>
)HTML");
// Set the plugin element up to have the ScriptOnDestroy plugin.
auto* plugin_element =
DynamicTo<HTMLPlugInElement>(document.getElementById("plugin"));
ASSERT_TRUE(plugin_element);
auto* plugin = MakeGarbageCollected<ScriptOnDestroyPlugin>();
auto* plugin_container =
MakeGarbageCollected<WebPluginContainerImpl>(*plugin_element, plugin);
plugin->Initialize(plugin_container);
plugin_element->SetEmbeddedContentView(plugin_container);
// Now create a shadow root on target, which should cause the plugin to be
// destroyed. Test passes if we pass the script forbidden check in the plugin.
auto* target = document.getElementById("target");
target->CreateUserAgentShadowRoot();
ASSERT_TRUE(plugin->DestroyCalled());
}
} // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment