blob: ce7fd1e75ab222c6f79c5583326ec704db71b3d5 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/dom/Node.h"
#include "bindings/core/v8/V8BindingForCore.h"
#include "core/css/resolver/StyleResolver.h"
#include "core/dom/ShadowRootInit.h"
#include "core/editing/EditingTestBase.h"
namespace blink {
class NodeTest : public EditingTestBase {
protected:
ShadowRoot* AttachShadowTo(Element* element) {
ShadowRootInit shadow_root_init;
shadow_root_init.setMode("open");
return element->attachShadow(
ToScriptStateForMainWorld(GetDocument().GetFrame()), shadow_root_init,
ASSERT_NO_EXCEPTION);
}
LayoutObject* ReattachLayoutTreeForNode(Node& node) {
GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
PushSelectorFilterAncestors(
GetDocument().EnsureStyleResolver().GetSelectorFilter(), node);
Node::AttachContext context;
node.ReattachLayoutTree(context);
return context.previous_in_flow;
}
private:
void PushSelectorFilterAncestors(SelectorFilter& filter, Node& node) {
if (Element* parent = FlatTreeTraversal::ParentElement(node)) {
PushSelectorFilterAncestors(filter, *parent);
filter.PushParent(*parent);
}
}
};
TEST_F(NodeTest, canStartSelection) {
const char* body_content =
"<a id=one href='http://www.msn.com'>one</a><b id=two>two</b>";
SetBodyContent(body_content);
Node* one = GetDocument().getElementById("one");
Node* two = GetDocument().getElementById("two");
EXPECT_FALSE(one->CanStartSelection());
EXPECT_FALSE(one->firstChild()->CanStartSelection());
EXPECT_TRUE(two->CanStartSelection());
EXPECT_TRUE(two->firstChild()->CanStartSelection());
}
TEST_F(NodeTest, canStartSelectionWithShadowDOM) {
const char* body_content = "<div id=host><span id=one>one</span></div>";
const char* shadow_content =
"<a href='http://www.msn.com'><content></content></a>";
SetBodyContent(body_content);
SetShadowContent(shadow_content, "host");
Node* one = GetDocument().getElementById("one");
EXPECT_FALSE(one->CanStartSelection());
EXPECT_FALSE(one->firstChild()->CanStartSelection());
}
TEST_F(NodeTest, customElementState) {
const char* body_content = "<div id=div></div>";
SetBodyContent(body_content);
Element* div = GetDocument().getElementById("div");
EXPECT_EQ(CustomElementState::kUncustomized, div->GetCustomElementState());
EXPECT_TRUE(div->IsDefined());
EXPECT_EQ(Node::kV0NotCustomElement, div->GetV0CustomElementState());
div->SetCustomElementState(CustomElementState::kUndefined);
EXPECT_EQ(CustomElementState::kUndefined, div->GetCustomElementState());
EXPECT_FALSE(div->IsDefined());
EXPECT_EQ(Node::kV0NotCustomElement, div->GetV0CustomElementState());
div->SetCustomElementState(CustomElementState::kCustom);
EXPECT_EQ(CustomElementState::kCustom, div->GetCustomElementState());
EXPECT_TRUE(div->IsDefined());
EXPECT_EQ(Node::kV0NotCustomElement, div->GetV0CustomElementState());
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_TextRoot) {
SetBodyContent("Text");
Node* root = GetDocument().body()->firstChild();
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_TRUE(previous_in_flow);
EXPECT_EQ(root->GetLayoutObject(), previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_InlineRoot) {
SetBodyContent("<span id=root>Text <span></span></span>");
Element* root = GetDocument().getElementById("root");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_TRUE(previous_in_flow);
EXPECT_EQ(root->GetLayoutObject(), previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_BlockRoot) {
SetBodyContent("<div id=root>Text <span></span></div>");
Element* root = GetDocument().getElementById("root");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_TRUE(previous_in_flow);
EXPECT_EQ(root->GetLayoutObject(), previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_FloatRoot) {
SetBodyContent("<div id=root style='float:left'><span></span></div>");
Element* root = GetDocument().getElementById("root");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_FALSE(previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_AbsoluteRoot) {
SetBodyContent("<div id=root style='position:absolute'><span></span></div>");
Element* root = GetDocument().getElementById("root");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_FALSE(previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_Text) {
SetBodyContent("<div id=root style='display:contents'>Text</div>");
Element* root = GetDocument().getElementById("root");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_TRUE(previous_in_flow);
EXPECT_EQ(root->firstChild()->GetLayoutObject(), previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_Inline) {
SetBodyContent("<div id=root style='display:contents'><span></span></div>");
Element* root = GetDocument().getElementById("root");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_TRUE(previous_in_flow);
EXPECT_EQ(root->firstChild()->GetLayoutObject(), previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_Block) {
SetBodyContent("<div id=root style='display:contents'><div></div></div>");
Element* root = GetDocument().getElementById("root");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_TRUE(previous_in_flow);
EXPECT_EQ(root->firstChild()->GetLayoutObject(), previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_Float) {
SetBodyContent(
"<style>"
" #root { display:contents }"
" .float { float:left }"
"</style>"
"<div id=root><div class=float></div></div>");
Element* root = GetDocument().getElementById("root");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_FALSE(previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_AbsolutePositioned) {
SetBodyContent(
"<style>"
" #root { display:contents }"
" .abs { position:absolute }"
"</style>"
"<div id=root><div class=abs></div></div>");
Element* root = GetDocument().getElementById("root");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_FALSE(previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_SkipAbsolute) {
SetBodyContent(
"<style>"
" #root { display:contents }"
" .abs { position:absolute }"
"</style>"
"<div id=root>"
"<div class=abs></div><span id=inline></span><div class=abs></div>"
"</div>");
Element* root = GetDocument().getElementById("root");
Element* span = GetDocument().getElementById("inline");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_TRUE(previous_in_flow);
EXPECT_EQ(span->GetLayoutObject(), previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_SkipFloats) {
SetBodyContent(
"<style>"
" #root { display:contents }"
" .float { float:left }"
"</style>"
"<div id=root>"
"<div class=float></div>"
"<span id=inline></span>"
"<div class=float></div>"
"</div>");
Element* root = GetDocument().getElementById("root");
Element* span = GetDocument().getElementById("inline");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_TRUE(previous_in_flow);
EXPECT_EQ(span->GetLayoutObject(), previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_InsideDisplayContents) {
SetBodyContent(
"<style>"
" #root, .contents { display:contents }"
" .float { float:left }"
"</style>"
"<div id=root>"
"<span></span><div class=contents><span id=inline></span></div>"
"</div>");
Element* root = GetDocument().getElementById("root");
Element* span = GetDocument().getElementById("inline");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_TRUE(previous_in_flow);
EXPECT_EQ(span->GetLayoutObject(), previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_Slotted) {
SetBodyContent("<div id=host><span id=inline></span></div>");
ShadowRoot* shadow_root =
AttachShadowTo(GetDocument().getElementById("host"));
shadow_root->setInnerHTML(
"<div id=root style='display:contents'><span></span><slot></slot></div>");
GetDocument().View()->UpdateAllLifecyclePhases();
Element* root = shadow_root->getElementById("root");
Element* span = GetDocument().getElementById("inline");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_TRUE(previous_in_flow);
EXPECT_EQ(span->GetLayoutObject(), previous_in_flow);
}
TEST_F(NodeTest, AttachContext_PreviousInFlow_V0Content) {
SetBodyContent("<div id=host><span id=inline></span></div>");
ShadowRoot* shadow_root = CreateShadowRootForElementWithIDAndSetInnerHTML(
GetDocument(), "host",
"<div id=root style='display:contents'><span></span><content /></div>");
Element* root = shadow_root->getElementById("root");
Element* span = GetDocument().getElementById("inline");
LayoutObject* previous_in_flow = ReattachLayoutTreeForNode(*root);
EXPECT_TRUE(previous_in_flow);
EXPECT_EQ(span->GetLayoutObject(), previous_in_flow);
}
} // namespace blink