blob: 6144fc91b6b31457033d6c0dfe701386b4576f8f [file] [log] [blame]
// Copyright 2014 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/dom/document.h"
#include <memory>
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/css_parser/parser.h"
#include "cobalt/cssom/css_style_sheet.h"
#include "cobalt/dom/attr.h"
#include "cobalt/dom/comment.h"
#include "cobalt/dom/custom_event.h"
#include "cobalt/dom/dom_exception.h"
#include "cobalt/dom/dom_implementation.h"
#include "cobalt/dom/dom_stat_tracker.h"
#include "cobalt/dom/element.h"
#include "cobalt/dom/global_stats.h"
#include "cobalt/dom/html_body_element.h"
#include "cobalt/dom/html_element_context.h"
#include "cobalt/dom/html_head_element.h"
#include "cobalt/dom/html_html_element.h"
#include "cobalt/dom/html_style_element.h"
#include "cobalt/dom/keyboard_event.h"
#include "cobalt/dom/location.h"
#include "cobalt/dom/message_event.h"
#include "cobalt/dom/mouse_event.h"
#include "cobalt/dom/node_list.h"
#include "cobalt/dom/testing/gtest_workarounds.h"
#include "cobalt/dom/testing/html_collection_testing.h"
#include "cobalt/dom/testing/stub_environment_settings.h"
#include "cobalt/dom/text.h"
#include "cobalt/dom/ui_event.h"
#include "cobalt/script/testing/mock_exception_state.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
namespace dom {
namespace {
using script::testing::MockExceptionState;
using ::testing::_;
using ::testing::SaveArg;
using ::testing::StrictMock;
//////////////////////////////////////////////////////////////////////////
// DocumentTest
//////////////////////////////////////////////////////////////////////////
class DocumentTest : public ::testing::Test {
protected:
DocumentTest();
~DocumentTest() override;
base::MessageLoop message_loop_;
testing::StubEnvironmentSettings environment_settings_;
std::unique_ptr<css_parser::Parser> css_parser_;
std::unique_ptr<DomStatTracker> dom_stat_tracker_;
HTMLElementContext html_element_context_;
};
DocumentTest::DocumentTest()
: css_parser_(css_parser::Parser::Create()),
dom_stat_tracker_(new DomStatTracker("DocumentTest")),
html_element_context_(
&environment_settings_, NULL, NULL, css_parser_.get(), NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
dom_stat_tracker_.get(), "", base::kApplicationStateStarted, NULL,
NULL) {
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
}
DocumentTest::~DocumentTest() {
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
}
//////////////////////////////////////////////////////////////////////////
// Test cases
//////////////////////////////////////////////////////////////////////////
TEST_F(DocumentTest, Create) {
scoped_refptr<Document> document = new Document(&html_element_context_);
ASSERT_TRUE(document);
EXPECT_EQ(Node::kDocumentNode, document->node_type());
EXPECT_EQ("#document", document->node_name());
GURL url("http://a valid url");
document = new Document(&html_element_context_, Document::Options(url));
EXPECT_EQ(url.spec(), document->url());
EXPECT_EQ(url.spec(), document->document_uri());
EXPECT_EQ(url, document->url_as_gurl());
}
TEST_F(DocumentTest, IsNotXMLDocument) {
scoped_refptr<Document> document = new Document(&html_element_context_);
EXPECT_FALSE(document->IsXMLDocument());
}
TEST_F(DocumentTest, DocumentElement) {
scoped_refptr<Document> document = new Document(&html_element_context_);
EXPECT_EQ(NULL, document->document_element().get());
scoped_refptr<Text> text = new Text(document, "test_text");
scoped_refptr<Element> element =
new Element(document, base::Token("element"));
document->AppendChild(text);
document->AppendChild(element);
EXPECT_EQ(element, document->document_element());
}
TEST_F(DocumentTest, CreateElement) {
scoped_refptr<Document> document = new Document(&html_element_context_);
scoped_refptr<Element> element = document->CreateElement("element");
EXPECT_EQ(Node::kElementNode, element->node_type());
EXPECT_EQ("ELEMENT", element->node_name());
EXPECT_EQ(document, element->node_document());
EXPECT_EQ(NULL, element->parent_node());
EXPECT_EQ(NULL, element->first_child());
EXPECT_EQ(NULL, element->last_child());
element = document->CreateElement("ELEMENT");
EXPECT_EQ("ELEMENT", element->node_name());
}
TEST_F(DocumentTest, CreateTextNode) {
scoped_refptr<Document> document = new Document(&html_element_context_);
scoped_refptr<Text> text = document->CreateTextNode("test_text");
EXPECT_EQ(Node::kTextNode, text->node_type());
EXPECT_EQ("#text", text->node_name());
EXPECT_EQ("test_text", text->data());
EXPECT_EQ(document, text->node_document());
}
TEST_F(DocumentTest, CreateComment) {
scoped_refptr<Document> document = new Document(&html_element_context_);
scoped_refptr<Comment> comment = document->CreateComment("test_comment");
EXPECT_EQ(Node::kCommentNode, comment->node_type());
EXPECT_EQ("#comment", comment->node_name());
EXPECT_EQ("test_comment", comment->data());
EXPECT_EQ(document, comment->node_document());
}
TEST_F(DocumentTest, CreateEventEvent) {
StrictMock<MockExceptionState> exception_state;
scoped_refptr<script::ScriptException> exception;
scoped_refptr<Document> document = new Document(&html_element_context_);
// Create an Event, the name is case insensitive.
scoped_refptr<Event> event = document->CreateEvent("EvEnT", &exception_state);
EXPECT_TRUE(event);
EXPECT_FALSE(event->initialized_flag());
event = document->CreateEvent("EvEnTs", &exception_state);
EXPECT_TRUE(event);
EXPECT_FALSE(event->initialized_flag());
EXPECT_FALSE(dynamic_cast<UIEvent*>(event.get()));
EXPECT_FALSE(dynamic_cast<KeyboardEvent*>(event.get()));
EXPECT_FALSE(dynamic_cast<MessageEvent*>(event.get()));
EXPECT_FALSE(dynamic_cast<MouseEvent*>(event.get()));
event = document->CreateEvent("HtMlEvEnTs", &exception_state);
EXPECT_TRUE(event);
EXPECT_FALSE(event->initialized_flag());
}
TEST_F(DocumentTest, CreateEventCustomEvent) {
StrictMock<MockExceptionState> exception_state;
scoped_refptr<script::ScriptException> exception;
scoped_refptr<Document> document = new Document(&html_element_context_);
// Create an Event, the name is case insensitive.
scoped_refptr<Event> event =
document->CreateEvent("CuStOmEvEnT", &exception_state);
EXPECT_TRUE(event);
EXPECT_FALSE(event->initialized_flag());
EXPECT_TRUE(base::polymorphic_downcast<CustomEvent*>(event.get()));
}
TEST_F(DocumentTest, CreateEventUIEvent) {
StrictMock<MockExceptionState> exception_state;
scoped_refptr<script::ScriptException> exception;
scoped_refptr<Document> document = new Document(&html_element_context_);
// Create an Event, the name is case insensitive.
scoped_refptr<Event> event =
document->CreateEvent("UiEvEnT", &exception_state);
EXPECT_TRUE(event);
EXPECT_FALSE(event->initialized_flag());
EXPECT_TRUE(base::polymorphic_downcast<UIEvent*>(event.get()));
event = document->CreateEvent("UiEvEnTs", &exception_state);
EXPECT_TRUE(event);
EXPECT_FALSE(event->initialized_flag());
EXPECT_TRUE(base::polymorphic_downcast<UIEvent*>(event.get()));
}
TEST_F(DocumentTest, CreateEventKeyboardEvent) {
StrictMock<MockExceptionState> exception_state;
scoped_refptr<script::ScriptException> exception;
scoped_refptr<Document> document = new Document(&html_element_context_);
// Create an Event, the name is case insensitive.
scoped_refptr<Event> event =
document->CreateEvent("KeYbOaRdEvEnT", &exception_state);
EXPECT_TRUE(event);
EXPECT_FALSE(event->initialized_flag());
EXPECT_TRUE(base::polymorphic_downcast<KeyboardEvent*>(event.get()));
event = document->CreateEvent("KeYeVeNtS", &exception_state);
EXPECT_TRUE(event);
EXPECT_FALSE(event->initialized_flag());
EXPECT_TRUE(base::polymorphic_downcast<KeyboardEvent*>(event.get()));
}
TEST_F(DocumentTest, CreateEventMessageEvent) {
StrictMock<MockExceptionState> exception_state;
scoped_refptr<script::ScriptException> exception;
scoped_refptr<Document> document = new Document(&html_element_context_);
// Create an Event, the name is case insensitive.
scoped_refptr<Event> event =
document->CreateEvent("MeSsAgEeVeNt", &exception_state);
EXPECT_TRUE(event);
EXPECT_FALSE(event->initialized_flag());
EXPECT_TRUE(base::polymorphic_downcast<MessageEvent*>(event.get()));
}
TEST_F(DocumentTest, CreateEventMouseEvent) {
StrictMock<MockExceptionState> exception_state;
scoped_refptr<script::ScriptException> exception;
scoped_refptr<Document> document = new Document(&html_element_context_);
// Create an Event, the name is case insensitive.
scoped_refptr<Event> event =
document->CreateEvent("MoUsEeVeNt", &exception_state);
EXPECT_TRUE(event);
EXPECT_FALSE(event->initialized_flag());
EXPECT_TRUE(base::polymorphic_downcast<MouseEvent*>(event.get()));
event = document->CreateEvent("MoUsEeVeNtS", &exception_state);
EXPECT_TRUE(event);
EXPECT_FALSE(event->initialized_flag());
EXPECT_TRUE(base::polymorphic_downcast<MouseEvent*>(event.get()));
}
TEST_F(DocumentTest, CreateEventEventNotSupported) {
StrictMock<MockExceptionState> exception_state;
scoped_refptr<script::ScriptException> exception;
scoped_refptr<Document> document = new Document(&html_element_context_);
EXPECT_CALL(exception_state, SetException(_))
.WillOnce(SaveArg<0>(&exception));
scoped_refptr<Event> event =
document->CreateEvent("Event Not Supported", &exception_state);
EXPECT_FALSE(event);
ASSERT_TRUE(exception);
EXPECT_EQ(DOMException::kNotSupportedErr,
base::polymorphic_downcast<DOMException*>(exception.get())->code());
}
TEST_F(DocumentTest, GetElementsByClassName) {
scoped_refptr<Document> document = new Document(&html_element_context_);
testing::TestGetElementsByClassName(document);
}
TEST_F(DocumentTest, GetElementsByTagName) {
scoped_refptr<Document> document = new Document(&html_element_context_);
testing::TestGetElementsByTagName(document);
}
TEST_F(DocumentTest, GetElementById) {
scoped_refptr<Document> document = new Document(&html_element_context_);
// Construct a tree:
// document
// a1
// b1
// c1
// a2
// d1
scoped_refptr<Node> a1 =
document->AppendChild(new Element(document, base::Token("a1")));
scoped_refptr<Node> a2 =
document->AppendChild(new Element(document, base::Token("a2")));
scoped_refptr<Node> b1 =
a1->AppendChild(new Element(document, base::Token("b1")));
scoped_refptr<Node> c1 =
b1->AppendChild(new Element(document, base::Token("c1")));
scoped_refptr<Node> d1 =
a2->AppendChild(new Element(document, base::Token("d1")));
EXPECT_EQ(NULL, document->GetElementById("id").get());
d1->AsElement()->set_id("id");
EXPECT_EQ(d1, document->GetElementById("id"));
c1->AsElement()->set_id("id");
EXPECT_EQ(c1, document->GetElementById("id"));
document->RemoveChild(a1);
EXPECT_EQ(d1, document->GetElementById("id"));
}
TEST_F(DocumentTest, Implementation) {
scoped_refptr<Document> document = new Document(&html_element_context_);
EXPECT_TRUE(document->implementation());
}
TEST_F(DocumentTest, Location) {
scoped_refptr<Document> document = new Document(&html_element_context_);
EXPECT_TRUE(document->location());
}
TEST_F(DocumentTest, StyleSheets) {
scoped_refptr<Document> document = new Document(&html_element_context_);
scoped_refptr<HTMLElement> element1 =
html_element_context_.html_element_factory()->CreateHTMLElement(
document, base::Token(HTMLStyleElement::kTagName));
element1->set_text_content(std::string("body { background-color: #D3D3D3 }"));
document->AppendChild(element1);
scoped_refptr<HTMLElement> element2 =
html_element_context_.html_element_factory()->CreateHTMLElement(
document, base::Token(HTMLStyleElement::kTagName));
element2->set_text_content(std::string("h1 { color: #00F }"));
document->AppendChild(element2);
scoped_refptr<HTMLElement> element3 =
html_element_context_.html_element_factory()->CreateHTMLElement(
document, base::Token(HTMLStyleElement::kTagName));
element3->set_text_content(std::string("p { color: #008000 }"));
document->AppendChild(element3);
EXPECT_TRUE(document->style_sheets());
EXPECT_EQ(3, document->style_sheets()->length());
EXPECT_TRUE(document->style_sheets()->Item(0));
EXPECT_TRUE(document->style_sheets()->Item(1));
EXPECT_TRUE(document->style_sheets()->Item(2));
// Each style sheet should represent the one from the corresponding style
// element.
EXPECT_EQ(document->style_sheets()->Item(0),
element1->AsHTMLStyleElement()->sheet());
EXPECT_EQ(document->style_sheets()->Item(1),
element2->AsHTMLStyleElement()->sheet());
EXPECT_EQ(document->style_sheets()->Item(2),
element3->AsHTMLStyleElement()->sheet());
// Each style sheet should be unique.
EXPECT_NE(document->style_sheets()->Item(0),
document->style_sheets()->Item(1));
EXPECT_NE(document->style_sheets()->Item(0),
document->style_sheets()->Item(2));
EXPECT_NE(document->style_sheets()->Item(1),
document->style_sheets()->Item(2));
}
TEST_F(DocumentTest, StyleSheetsAddedToFront) {
scoped_refptr<Document> document = new Document(&html_element_context_);
scoped_refptr<HTMLElement> element1 =
html_element_context_.html_element_factory()->CreateHTMLElement(
document, base::Token(HTMLStyleElement::kTagName));
element1->set_text_content(std::string("body { background-color: #D3D3D3 }"));
document->AppendChild(element1);
scoped_refptr<HTMLElement> element2 =
html_element_context_.html_element_factory()->CreateHTMLElement(
document, base::Token(HTMLStyleElement::kTagName));
element2->set_text_content(std::string("h1 { color: #00F }"));
document->InsertBefore(element2, element1);
scoped_refptr<HTMLElement> element3 =
html_element_context_.html_element_factory()->CreateHTMLElement(
document, base::Token(HTMLStyleElement::kTagName));
element3->set_text_content(std::string("p { color: #008000 }"));
document->InsertBefore(element3, element2);
EXPECT_TRUE(document->style_sheets());
EXPECT_EQ(3, document->style_sheets()->length());
EXPECT_TRUE(document->style_sheets()->Item(0));
EXPECT_TRUE(document->style_sheets()->Item(1));
EXPECT_TRUE(document->style_sheets()->Item(2));
// Each style sheet should represent the one from the corresponding style
// element.
EXPECT_EQ(document->style_sheets()->Item(0),
element3->AsHTMLStyleElement()->sheet());
EXPECT_EQ(document->style_sheets()->Item(1),
element2->AsHTMLStyleElement()->sheet());
EXPECT_EQ(document->style_sheets()->Item(2),
element1->AsHTMLStyleElement()->sheet());
// Each style sheet should be unique.
EXPECT_NE(document->style_sheets()->Item(0),
document->style_sheets()->Item(1));
EXPECT_NE(document->style_sheets()->Item(0),
document->style_sheets()->Item(2));
EXPECT_NE(document->style_sheets()->Item(1),
document->style_sheets()->Item(2));
}
TEST_F(DocumentTest, HtmlElement) {
scoped_refptr<Document> document = new Document(&html_element_context_);
EXPECT_FALSE(document->html());
scoped_refptr<Node> div =
document->AppendChild(document->CreateElement("div"));
EXPECT_FALSE(document->html());
document->RemoveChild(div);
scoped_refptr<Node> html =
document->AppendChild(document->CreateElement("html"));
EXPECT_EQ(html, document->html());
}
TEST_F(DocumentTest, HeadElement) {
scoped_refptr<Document> document = new Document(&html_element_context_);
EXPECT_FALSE(document->head());
scoped_refptr<Node> html =
document->AppendChild(document->CreateElement("html"));
EXPECT_FALSE(document->head());
scoped_refptr<Node> div = html->AppendChild(document->CreateElement("div"));
EXPECT_FALSE(document->head());
scoped_refptr<Node> head1 =
html->AppendChild(document->CreateElement("head"));
scoped_refptr<Node> head2 =
html->AppendChild(document->CreateElement("head"));
EXPECT_EQ(head1, document->head());
}
TEST_F(DocumentTest, BodyElement) {
scoped_refptr<Document> document = new Document(&html_element_context_);
EXPECT_FALSE(document->body());
scoped_refptr<Node> html =
document->AppendChild(document->CreateElement("html"));
EXPECT_FALSE(document->body());
scoped_refptr<Node> div = html->AppendChild(document->CreateElement("div"));
EXPECT_FALSE(document->body());
scoped_refptr<Node> body1 =
html->AppendChild(document->CreateElement("body"));
scoped_refptr<Node> body2 =
html->AppendChild(document->CreateElement("body"));
EXPECT_EQ(body1, document->body());
}
} // namespace
} // namespace dom
} // namespace cobalt