mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2024-12-12 17:46:04 +01:00
241941a44a
This change moves the SendProcessMessage method from CefBrowser to CefFrame and adds CefBrowser parameters to OnProcessMessageReceived and OnDraggableRegionsChanged. The internal implementation has changed as follows: - Frame IDs are now a 64-bit combination of the 32-bit render_process_id and render_routing_id values that uniquely identify a RenderFrameHost (RFH). - CefFrameHostImpl objects are now managed by CefBrowserInfo with life span tied to RFH expectations. Specifically, a CefFrameHostImpl object representing a sub-frame will be created when a RenderFrame is created in the renderer process and detached when the associated RenderFrame is deleted or the renderer process in which it runs has died. - The CefFrameHostImpl object representing the main frame will always be valid but the underlying RFH (and associated frame ID) may change over time as a result of cross-origin navigations. Despite these changes calling LoadURL on the main frame object in the browser process will always navigate as expected. - Speculative RFHs, which may be created as a result of a cross-origin navigation and discarded if that navigation is not committed, are now handled correctly (e.g. ignored in most cases until they're committed). - It is less likely, but still possible, to receive a CefFrame object with an invalid frame ID (ID < 0). This can happen in cases where a RFH has not yet been created for a sub-frame. For example, when OnBeforeBrowse is called before initiating navigation in a previously nonexisting sub-frame. To test: All tests pass with NetworkService enabled and disabled.
349 lines
12 KiB
C++
349 lines
12 KiB
C++
// Copyright (c) 2012 The Chromium Embedded Framework 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 "include/cef_dom.h"
|
|
#include "tests/ceftests/test_handler.h"
|
|
#include "tests/gtest/include/gtest/gtest.h"
|
|
#include "tests/shared/renderer/client_app_renderer.h"
|
|
|
|
using client::ClientAppRenderer;
|
|
|
|
namespace {
|
|
|
|
const char* kTestUrl = "http://tests/DOMTest.Test";
|
|
const char* kTestMessage = "DOMTest.Message";
|
|
|
|
enum DOMTestType {
|
|
DOM_TEST_STRUCTURE,
|
|
DOM_TEST_MODIFY,
|
|
};
|
|
|
|
class TestDOMVisitor : public CefDOMVisitor {
|
|
public:
|
|
explicit TestDOMVisitor(CefRefPtr<CefBrowser> browser, DOMTestType test_type)
|
|
: browser_(browser), test_type_(test_type) {}
|
|
|
|
void TestHeadNodeStructure(CefRefPtr<CefDOMNode> headNode) {
|
|
EXPECT_TRUE(headNode.get());
|
|
EXPECT_TRUE(headNode->IsElement());
|
|
EXPECT_FALSE(headNode->IsText());
|
|
EXPECT_EQ(headNode->GetName(), "HEAD");
|
|
EXPECT_EQ(headNode->GetElementTagName(), "HEAD");
|
|
|
|
EXPECT_TRUE(headNode->HasChildren());
|
|
EXPECT_FALSE(headNode->HasElementAttributes());
|
|
|
|
CefRefPtr<CefDOMNode> titleNode = headNode->GetFirstChild();
|
|
EXPECT_TRUE(titleNode.get());
|
|
EXPECT_TRUE(titleNode->IsElement());
|
|
EXPECT_FALSE(titleNode->IsText());
|
|
EXPECT_EQ(titleNode->GetName(), "TITLE");
|
|
EXPECT_EQ(titleNode->GetElementTagName(), "TITLE");
|
|
EXPECT_TRUE(titleNode->GetParent()->IsSame(headNode));
|
|
|
|
EXPECT_FALSE(titleNode->GetNextSibling().get());
|
|
EXPECT_FALSE(titleNode->GetPreviousSibling().get());
|
|
EXPECT_TRUE(titleNode->HasChildren());
|
|
EXPECT_FALSE(titleNode->HasElementAttributes());
|
|
|
|
CefRefPtr<CefDOMNode> textNode = titleNode->GetFirstChild();
|
|
EXPECT_TRUE(textNode.get());
|
|
EXPECT_FALSE(textNode->IsElement());
|
|
EXPECT_TRUE(textNode->IsText());
|
|
EXPECT_EQ(textNode->GetValue(), "The Title");
|
|
EXPECT_TRUE(textNode->GetParent()->IsSame(titleNode));
|
|
|
|
EXPECT_FALSE(textNode->GetNextSibling().get());
|
|
EXPECT_FALSE(textNode->GetPreviousSibling().get());
|
|
EXPECT_FALSE(textNode->HasChildren());
|
|
}
|
|
|
|
void TestBodyNodeStructure(CefRefPtr<CefDOMNode> bodyNode) {
|
|
EXPECT_TRUE(bodyNode.get());
|
|
EXPECT_TRUE(bodyNode->IsElement());
|
|
EXPECT_FALSE(bodyNode->IsText());
|
|
EXPECT_EQ(bodyNode->GetName(), "BODY");
|
|
EXPECT_EQ(bodyNode->GetElementTagName(), "BODY");
|
|
|
|
EXPECT_TRUE(bodyNode->HasChildren());
|
|
EXPECT_FALSE(bodyNode->HasElementAttributes());
|
|
|
|
CefRefPtr<CefDOMNode> h1Node = bodyNode->GetFirstChild();
|
|
EXPECT_TRUE(h1Node.get());
|
|
EXPECT_TRUE(h1Node->IsElement());
|
|
EXPECT_FALSE(h1Node->IsText());
|
|
EXPECT_EQ(h1Node->GetName(), "H1");
|
|
EXPECT_EQ(h1Node->GetElementTagName(), "H1");
|
|
|
|
EXPECT_TRUE(h1Node->GetNextSibling().get());
|
|
EXPECT_FALSE(h1Node->GetPreviousSibling().get());
|
|
EXPECT_TRUE(h1Node->HasChildren());
|
|
EXPECT_FALSE(h1Node->HasElementAttributes());
|
|
|
|
CefRefPtr<CefDOMNode> textNode = h1Node->GetFirstChild();
|
|
EXPECT_TRUE(textNode.get());
|
|
EXPECT_FALSE(textNode->IsElement());
|
|
EXPECT_TRUE(textNode->IsText());
|
|
EXPECT_EQ(textNode->GetValue(), "Hello From");
|
|
|
|
EXPECT_FALSE(textNode->GetPreviousSibling().get());
|
|
EXPECT_FALSE(textNode->HasChildren());
|
|
|
|
CefRefPtr<CefDOMNode> brNode = textNode->GetNextSibling();
|
|
EXPECT_TRUE(brNode.get());
|
|
EXPECT_TRUE(brNode->IsElement());
|
|
EXPECT_FALSE(brNode->IsText());
|
|
EXPECT_EQ(brNode->GetName(), "BR");
|
|
EXPECT_EQ(brNode->GetElementTagName(), "BR");
|
|
|
|
EXPECT_FALSE(brNode->HasChildren());
|
|
|
|
EXPECT_TRUE(brNode->HasElementAttributes());
|
|
EXPECT_TRUE(brNode->HasElementAttribute("class"));
|
|
EXPECT_EQ(brNode->GetElementAttribute("class"), "some_class");
|
|
EXPECT_TRUE(brNode->HasElementAttribute("id"));
|
|
EXPECT_EQ(brNode->GetElementAttribute("id"), "some_id");
|
|
EXPECT_FALSE(brNode->HasElementAttribute("no_existing"));
|
|
|
|
CefDOMNode::AttributeMap map;
|
|
brNode->GetElementAttributes(map);
|
|
ASSERT_EQ(map.size(), (size_t)2);
|
|
EXPECT_EQ(map["class"], "some_class");
|
|
EXPECT_EQ(map["id"], "some_id");
|
|
|
|
// Can also retrieve by ID.
|
|
brNode = bodyNode->GetDocument()->GetElementById("some_id");
|
|
EXPECT_TRUE(brNode.get());
|
|
EXPECT_TRUE(brNode->IsElement());
|
|
EXPECT_FALSE(brNode->IsText());
|
|
EXPECT_EQ(brNode->GetName(), "BR");
|
|
EXPECT_EQ(brNode->GetElementTagName(), "BR");
|
|
|
|
textNode = brNode->GetNextSibling();
|
|
EXPECT_TRUE(textNode.get());
|
|
EXPECT_FALSE(textNode->IsElement());
|
|
EXPECT_TRUE(textNode->IsText());
|
|
EXPECT_EQ(textNode->GetValue(), "Main Frame");
|
|
|
|
EXPECT_FALSE(textNode->GetNextSibling().get());
|
|
EXPECT_FALSE(textNode->HasChildren());
|
|
|
|
CefRefPtr<CefDOMNode> divNode = h1Node->GetNextSibling();
|
|
EXPECT_TRUE(divNode.get());
|
|
EXPECT_TRUE(divNode->IsElement());
|
|
EXPECT_FALSE(divNode->IsText());
|
|
CefRect divRect = divNode->GetElementBounds();
|
|
EXPECT_EQ(divRect.width, 50);
|
|
EXPECT_EQ(divRect.height, 25);
|
|
EXPECT_EQ(divRect.x, 150);
|
|
EXPECT_EQ(divRect.y, 100);
|
|
EXPECT_FALSE(divNode->GetNextSibling().get());
|
|
}
|
|
|
|
// Test document structure by iterating through the DOM tree.
|
|
void TestStructure(CefRefPtr<CefDOMDocument> document) {
|
|
EXPECT_EQ(document->GetTitle(), "The Title");
|
|
EXPECT_EQ(document->GetBaseURL(), kTestUrl);
|
|
EXPECT_EQ(document->GetCompleteURL("foo.html"), "http://tests/foo.html");
|
|
|
|
// Navigate the complete document structure.
|
|
CefRefPtr<CefDOMNode> docNode = document->GetDocument();
|
|
EXPECT_TRUE(docNode.get());
|
|
EXPECT_FALSE(docNode->IsElement());
|
|
EXPECT_FALSE(docNode->IsText());
|
|
|
|
CefRefPtr<CefDOMNode> htmlNode = docNode->GetFirstChild();
|
|
EXPECT_TRUE(htmlNode.get());
|
|
EXPECT_TRUE(htmlNode->IsElement());
|
|
EXPECT_FALSE(htmlNode->IsText());
|
|
EXPECT_EQ(htmlNode->GetName(), "HTML");
|
|
EXPECT_EQ(htmlNode->GetElementTagName(), "HTML");
|
|
|
|
EXPECT_TRUE(htmlNode->HasChildren());
|
|
EXPECT_FALSE(htmlNode->HasElementAttributes());
|
|
|
|
CefRefPtr<CefDOMNode> headNode = htmlNode->GetFirstChild();
|
|
TestHeadNodeStructure(headNode);
|
|
|
|
CefRefPtr<CefDOMNode> bodyNode = headNode->GetNextSibling();
|
|
TestBodyNodeStructure(bodyNode);
|
|
|
|
// Retrieve the head node directly.
|
|
headNode = document->GetHead();
|
|
TestHeadNodeStructure(headNode);
|
|
|
|
// Retrieve the body node directly.
|
|
bodyNode = document->GetBody();
|
|
TestBodyNodeStructure(bodyNode);
|
|
}
|
|
|
|
// Test document modification by changing the H1 tag.
|
|
void TestModify(CefRefPtr<CefDOMDocument> document) {
|
|
CefRefPtr<CefDOMNode> bodyNode = document->GetBody();
|
|
CefRefPtr<CefDOMNode> h1Node = bodyNode->GetFirstChild();
|
|
|
|
ASSERT_EQ(h1Node->GetAsMarkup(),
|
|
"<h1>Hello From<br class=\"some_class\" id=\"some_id\">"
|
|
"Main Frame</h1>");
|
|
|
|
CefRefPtr<CefDOMNode> textNode = h1Node->GetFirstChild();
|
|
ASSERT_EQ(textNode->GetValue(), "Hello From");
|
|
ASSERT_TRUE(textNode->SetValue("A Different Message From"));
|
|
ASSERT_EQ(textNode->GetValue(), "A Different Message From");
|
|
|
|
CefRefPtr<CefDOMNode> brNode = textNode->GetNextSibling();
|
|
EXPECT_EQ(brNode->GetElementAttribute("class"), "some_class");
|
|
EXPECT_TRUE(brNode->SetElementAttribute("class", "a_different_class"));
|
|
EXPECT_EQ(brNode->GetElementAttribute("class"), "a_different_class");
|
|
|
|
ASSERT_EQ(h1Node->GetAsMarkup(),
|
|
"<h1>A Different Message From<br class=\"a_different_class\" "
|
|
"id=\"some_id\">Main Frame</h1>");
|
|
|
|
ASSERT_FALSE(h1Node->SetValue("Something Different"));
|
|
}
|
|
|
|
void Visit(CefRefPtr<CefDOMDocument> document) override {
|
|
if (test_type_ == DOM_TEST_STRUCTURE)
|
|
TestStructure(document);
|
|
else if (test_type_ == DOM_TEST_MODIFY)
|
|
TestModify(document);
|
|
|
|
DestroyTest();
|
|
}
|
|
|
|
protected:
|
|
// Return from the test.
|
|
void DestroyTest() {
|
|
// Check if the test has failed.
|
|
bool result = !TestFailed();
|
|
|
|
// Return the result to the browser process.
|
|
CefRefPtr<CefProcessMessage> return_msg =
|
|
CefProcessMessage::Create(kTestMessage);
|
|
EXPECT_TRUE(return_msg->GetArgumentList()->SetBool(0, result));
|
|
browser_->GetMainFrame()->SendProcessMessage(PID_BROWSER, return_msg);
|
|
}
|
|
|
|
CefRefPtr<CefBrowser> browser_;
|
|
DOMTestType test_type_;
|
|
|
|
IMPLEMENT_REFCOUNTING(TestDOMVisitor);
|
|
};
|
|
|
|
// Used in the render process.
|
|
class DOMRendererTest : public ClientAppRenderer::Delegate {
|
|
public:
|
|
DOMRendererTest() {}
|
|
|
|
bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefProcessId source_process,
|
|
CefRefPtr<CefProcessMessage> message) override {
|
|
if (message->GetName() == kTestMessage) {
|
|
EXPECT_EQ(message->GetArgumentList()->GetSize(), (size_t)1);
|
|
int test_type = message->GetArgumentList()->GetInt(0);
|
|
|
|
browser->GetMainFrame()->VisitDOM(
|
|
new TestDOMVisitor(browser, static_cast<DOMTestType>(test_type)));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
IMPLEMENT_REFCOUNTING(DOMRendererTest);
|
|
};
|
|
|
|
// Used in the browser process.
|
|
class TestDOMHandler : public TestHandler {
|
|
public:
|
|
explicit TestDOMHandler(DOMTestType test) : test_type_(test) {}
|
|
|
|
void RunTest() override {
|
|
std::stringstream mainHtml;
|
|
mainHtml << "<html>"
|
|
"<head><title>The Title</title></head>"
|
|
"<body>"
|
|
"<h1>Hello From<br class=\"some_class\"/ id=\"some_id\"/>"
|
|
"Main Frame</h1>"
|
|
"<div id=\"sized_element\" style=\"width: 50px; height: 25px; "
|
|
"position: fixed; top: 100px; left: 150px;\"/>"
|
|
"</body>"
|
|
"</html>";
|
|
|
|
AddResource(kTestUrl, mainHtml.str(), "text/html");
|
|
CreateBrowser(kTestUrl);
|
|
|
|
// Time out the test after a reasonable period of time.
|
|
SetTestTimeout();
|
|
}
|
|
|
|
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int httpStatusCode) override {
|
|
if (frame->IsMain()) {
|
|
// Start the test in the render process.
|
|
CefRefPtr<CefProcessMessage> message(
|
|
CefProcessMessage::Create(kTestMessage));
|
|
message->GetArgumentList()->SetInt(0, test_type_);
|
|
frame->SendProcessMessage(PID_RENDERER, message);
|
|
}
|
|
}
|
|
|
|
bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefProcessId source_process,
|
|
CefRefPtr<CefProcessMessage> message) override {
|
|
EXPECT_STREQ(message->GetName().ToString().c_str(), kTestMessage);
|
|
|
|
got_message_.yes();
|
|
|
|
if (message->GetArgumentList()->GetBool(0))
|
|
got_success_.yes();
|
|
|
|
// Test is complete.
|
|
DestroyTest();
|
|
|
|
return true;
|
|
}
|
|
|
|
DOMTestType test_type_;
|
|
TrackCallback got_message_;
|
|
TrackCallback got_success_;
|
|
|
|
IMPLEMENT_REFCOUNTING(TestDOMHandler);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// Test DOM structure reading.
|
|
TEST(DOMTest, Read) {
|
|
CefRefPtr<TestDOMHandler> handler = new TestDOMHandler(DOM_TEST_STRUCTURE);
|
|
handler->ExecuteTest();
|
|
|
|
EXPECT_TRUE(handler->got_message_);
|
|
EXPECT_TRUE(handler->got_success_);
|
|
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Test DOM modifications.
|
|
TEST(DOMTest, Modify) {
|
|
CefRefPtr<TestDOMHandler> handler = new TestDOMHandler(DOM_TEST_MODIFY);
|
|
handler->ExecuteTest();
|
|
|
|
EXPECT_TRUE(handler->got_message_);
|
|
EXPECT_TRUE(handler->got_success_);
|
|
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Entry point for creating DOM renderer test objects.
|
|
// Called from client_app_delegates.cc.
|
|
void CreateDOMRendererTests(ClientAppRenderer::DelegateSet& delegates) {
|
|
delegates.insert(new DOMRendererTest);
|
|
}
|