2012-04-27 23:19:06 +02:00
|
|
|
// 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 "libcef/renderer/dom_node_impl.h"
|
|
|
|
#include "libcef/common/tracker.h"
|
|
|
|
#include "libcef/renderer/browser_impl.h"
|
|
|
|
#include "libcef/renderer/dom_document_impl.h"
|
|
|
|
#include "libcef/renderer/dom_event_impl.h"
|
|
|
|
#include "libcef/renderer/thread_util.h"
|
2013-10-29 18:53:18 +01:00
|
|
|
#include "libcef/renderer/webkit_glue.h"
|
2012-04-27 23:19:06 +02:00
|
|
|
|
|
|
|
#include "base/logging.h"
|
2013-06-22 04:06:32 +02:00
|
|
|
#include "base/strings/string_util.h"
|
|
|
|
#include "base/strings/utf_string_conversions.h"
|
|
|
|
#include "third_party/WebKit/public/platform/WebString.h"
|
|
|
|
#include "third_party/WebKit/public/web/WebDocument.h"
|
|
|
|
#include "third_party/WebKit/public/web/WebDOMEvent.h"
|
|
|
|
#include "third_party/WebKit/public/web/WebDOMEventListener.h"
|
|
|
|
#include "third_party/WebKit/public/web/WebElement.h"
|
|
|
|
#include "third_party/WebKit/public/web/WebFrame.h"
|
|
|
|
#include "third_party/WebKit/public/web/WebFormControlElement.h"
|
|
|
|
#include "third_party/WebKit/public/web/WebInputElement.h"
|
|
|
|
#include "third_party/WebKit/public/web/WebNode.h"
|
|
|
|
#include "third_party/WebKit/public/web/WebSelectElement.h"
|
2012-04-27 23:19:06 +02:00
|
|
|
|
2013-11-08 22:28:56 +01:00
|
|
|
using blink::WebDocument;
|
|
|
|
using blink::WebDOMEvent;
|
|
|
|
using blink::WebDOMEventListener;
|
|
|
|
using blink::WebElement;
|
|
|
|
using blink::WebFrame;
|
|
|
|
using blink::WebFormControlElement;
|
|
|
|
using blink::WebInputElement;
|
|
|
|
using blink::WebNode;
|
|
|
|
using blink::WebSelectElement;
|
|
|
|
using blink::WebString;
|
2012-04-27 23:19:06 +02:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Wrapper implementation for WebDOMEventListener.
|
|
|
|
class CefDOMEventListenerWrapper : public WebDOMEventListener,
|
|
|
|
public CefTrackNode {
|
|
|
|
public:
|
|
|
|
CefDOMEventListenerWrapper(CefBrowserImpl* browser, WebFrame* frame,
|
|
|
|
CefRefPtr<CefDOMEventListener> listener)
|
|
|
|
: browser_(browser),
|
|
|
|
frame_(frame),
|
|
|
|
listener_(listener) {
|
|
|
|
// Cause this object to be deleted immediately before the frame is closed.
|
|
|
|
browser->AddFrameObject(frame->identifier(), this);
|
|
|
|
}
|
|
|
|
virtual ~CefDOMEventListenerWrapper() {
|
|
|
|
CEF_REQUIRE_RT();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void handleEvent(const WebDOMEvent& event) {
|
|
|
|
CefRefPtr<CefDOMDocumentImpl> documentImpl;
|
|
|
|
CefRefPtr<CefDOMEventImpl> eventImpl;
|
|
|
|
|
|
|
|
if (!event.isNull()) {
|
|
|
|
// Create CefDOMDocumentImpl and CefDOMEventImpl objects that are valid
|
|
|
|
// only for the scope of this method.
|
|
|
|
const WebDocument& document = frame_->document();
|
|
|
|
if (!document.isNull()) {
|
|
|
|
documentImpl = new CefDOMDocumentImpl(browser_, frame_);
|
|
|
|
eventImpl = new CefDOMEventImpl(documentImpl, event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
listener_->HandleEvent(eventImpl.get());
|
|
|
|
|
|
|
|
if (eventImpl.get())
|
|
|
|
eventImpl->Detach();
|
|
|
|
if (documentImpl.get())
|
|
|
|
documentImpl->Detach();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
CefBrowserImpl* browser_;
|
|
|
|
WebFrame* frame_;
|
|
|
|
CefRefPtr<CefDOMEventListener> listener_;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
CefDOMNodeImpl::CefDOMNodeImpl(CefRefPtr<CefDOMDocumentImpl> document,
|
2013-11-08 22:28:56 +01:00
|
|
|
const blink::WebNode& node)
|
2012-04-27 23:19:06 +02:00
|
|
|
: document_(document),
|
|
|
|
node_(node) {
|
|
|
|
}
|
|
|
|
|
|
|
|
CefDOMNodeImpl::~CefDOMNodeImpl() {
|
|
|
|
CEF_REQUIRE_RT();
|
|
|
|
|
|
|
|
if (document_.get() && !node_.isNull()) {
|
|
|
|
// Remove the node from the document.
|
|
|
|
document_->RemoveNode(node_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CefDOMNodeImpl::Type CefDOMNodeImpl::GetType() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return DOM_NODE_TYPE_UNSUPPORTED;
|
|
|
|
|
|
|
|
switch (node_.nodeType()) {
|
|
|
|
case WebNode::ElementNode:
|
|
|
|
return DOM_NODE_TYPE_ELEMENT;
|
|
|
|
case WebNode::AttributeNode:
|
|
|
|
return DOM_NODE_TYPE_ATTRIBUTE;
|
|
|
|
case WebNode::TextNode:
|
|
|
|
return DOM_NODE_TYPE_TEXT;
|
|
|
|
case WebNode::CDataSectionNode:
|
|
|
|
return DOM_NODE_TYPE_CDATA_SECTION;
|
|
|
|
case WebNode::EntityNode:
|
|
|
|
return DOM_NODE_TYPE_ENTITY;
|
|
|
|
case WebNode::ProcessingInstructionsNode:
|
|
|
|
return DOM_NODE_TYPE_PROCESSING_INSTRUCTIONS;
|
|
|
|
case WebNode::CommentNode:
|
|
|
|
return DOM_NODE_TYPE_COMMENT;
|
|
|
|
case WebNode::DocumentNode:
|
|
|
|
return DOM_NODE_TYPE_DOCUMENT;
|
|
|
|
case WebNode::DocumentTypeNode:
|
|
|
|
return DOM_NODE_TYPE_DOCUMENT_TYPE;
|
|
|
|
case WebNode::DocumentFragmentNode:
|
|
|
|
return DOM_NODE_TYPE_DOCUMENT_FRAGMENT;
|
|
|
|
case WebNode::NotationNode:
|
|
|
|
return DOM_NODE_TYPE_NOTATION;
|
|
|
|
case WebNode::XPathNamespaceNode:
|
|
|
|
return DOM_NODE_TYPE_XPATH_NAMESPACE;
|
|
|
|
default:
|
|
|
|
return DOM_NODE_TYPE_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefDOMNodeImpl::IsText() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return node_.isTextNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefDOMNodeImpl::IsElement() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return node_.isElementNode();
|
|
|
|
}
|
|
|
|
|
2012-06-11 17:52:49 +02:00
|
|
|
// Logic copied from RenderViewImpl::IsEditableNode.
|
|
|
|
bool CefDOMNodeImpl::IsEditable() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (node_.isContentEditable())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (node_.isElementNode()) {
|
|
|
|
const WebElement& element = node_.toConst<WebElement>();
|
|
|
|
if (element.isTextFormControlElement())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Also return true if it has an ARIA role of 'textbox'.
|
|
|
|
for (unsigned i = 0; i < element.attributeCount(); ++i) {
|
|
|
|
if (LowerCaseEqualsASCII(element.attributeLocalName(i), "role")) {
|
|
|
|
if (LowerCaseEqualsASCII(element.attributeValue(i), "textbox"))
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-04-27 23:19:06 +02:00
|
|
|
bool CefDOMNodeImpl::IsFormControlElement() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (node_.isElementNode()) {
|
|
|
|
const WebElement& element = node_.toConst<WebElement>();
|
|
|
|
return element.isFormControlElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefString CefDOMNodeImpl::GetFormControlElementType() {
|
|
|
|
CefString str;
|
|
|
|
if (!VerifyContext())
|
|
|
|
return str;
|
|
|
|
|
|
|
|
if (node_.isElementNode()) {
|
|
|
|
const WebElement& element = node_.toConst<WebElement>();
|
|
|
|
if (element.isFormControlElement()) {
|
|
|
|
// Retrieve the type from the form control element.
|
|
|
|
const WebFormControlElement& formElement =
|
|
|
|
node_.toConst<WebFormControlElement>();
|
|
|
|
|
2013-12-17 23:04:35 +01:00
|
|
|
const base::string16& form_control_type = formElement.formControlType();
|
2012-04-27 23:19:06 +02:00
|
|
|
str = form_control_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefDOMNodeImpl::IsSame(CefRefPtr<CefDOMNode> that) {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
CefDOMNodeImpl* impl = static_cast<CefDOMNodeImpl*>(that.get());
|
|
|
|
if (!impl || !impl->VerifyContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return node_.equals(impl->node_);
|
|
|
|
}
|
|
|
|
|
|
|
|
CefString CefDOMNodeImpl::GetName() {
|
|
|
|
CefString str;
|
|
|
|
if (!VerifyContext())
|
|
|
|
return str;
|
|
|
|
|
|
|
|
const WebString& name = node_.nodeName();
|
|
|
|
if (!name.isNull())
|
|
|
|
str = name;
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefString CefDOMNodeImpl::GetValue() {
|
|
|
|
CefString str;
|
|
|
|
if (!VerifyContext())
|
|
|
|
return str;
|
|
|
|
|
|
|
|
if (node_.isElementNode()) {
|
|
|
|
const WebElement& element = node_.toConst<WebElement>();
|
|
|
|
if (element.isFormControlElement()) {
|
|
|
|
// Retrieve the value from the form control element.
|
|
|
|
const WebFormControlElement& formElement =
|
|
|
|
node_.toConst<WebFormControlElement>();
|
|
|
|
|
2013-12-17 23:04:35 +01:00
|
|
|
base::string16 value;
|
|
|
|
const base::string16& form_control_type = formElement.formControlType();
|
2012-04-27 23:19:06 +02:00
|
|
|
if (form_control_type == ASCIIToUTF16("text")) {
|
|
|
|
const WebInputElement& input_element =
|
|
|
|
formElement.toConst<WebInputElement>();
|
|
|
|
value = input_element.value();
|
|
|
|
} else if (form_control_type == ASCIIToUTF16("select-one")) {
|
|
|
|
const WebSelectElement& select_element =
|
|
|
|
formElement.toConst<WebSelectElement>();
|
|
|
|
value = select_element.value();
|
|
|
|
}
|
|
|
|
|
|
|
|
TrimWhitespace(value, TRIM_LEADING, &value);
|
|
|
|
str = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (str.empty()) {
|
|
|
|
const WebString& value = node_.nodeValue();
|
|
|
|
if (!value.isNull())
|
|
|
|
str = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefDOMNodeImpl::SetValue(const CefString& value) {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (node_.isElementNode())
|
|
|
|
return false;
|
|
|
|
|
2013-12-17 23:04:35 +01:00
|
|
|
return webkit_glue::SetNodeValue(node_, base::string16(value));
|
2012-04-27 23:19:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CefString CefDOMNodeImpl::GetAsMarkup() {
|
|
|
|
CefString str;
|
|
|
|
if (!VerifyContext())
|
|
|
|
return str;
|
|
|
|
|
|
|
|
const WebString& markup = node_.createMarkup();
|
|
|
|
if (!markup.isNull())
|
|
|
|
str = markup;
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefDOMDocument> CefDOMNodeImpl::GetDocument() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return document_.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefDOMNode> CefDOMNodeImpl::GetParent() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return document_->GetOrCreateNode(node_.parentNode());
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefDOMNode> CefDOMNodeImpl::GetPreviousSibling() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return document_->GetOrCreateNode(node_.previousSibling());
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefDOMNode> CefDOMNodeImpl::GetNextSibling() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return document_->GetOrCreateNode(node_.nextSibling());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefDOMNodeImpl::HasChildren() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return node_.hasChildNodes();
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefDOMNode> CefDOMNodeImpl::GetFirstChild() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return document_->GetOrCreateNode(node_.firstChild());
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefDOMNode> CefDOMNodeImpl::GetLastChild() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return document_->GetOrCreateNode(node_.lastChild());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefDOMNodeImpl::AddEventListener(const CefString& eventType,
|
|
|
|
CefRefPtr<CefDOMEventListener> listener,
|
|
|
|
bool useCapture) {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return;
|
|
|
|
|
2013-12-17 23:04:35 +01:00
|
|
|
node_.addEventListener(base::string16(eventType),
|
2012-04-27 23:19:06 +02:00
|
|
|
new CefDOMEventListenerWrapper(document_->GetBrowser(),
|
|
|
|
document_->GetFrame(), listener),
|
|
|
|
useCapture);
|
|
|
|
}
|
|
|
|
|
|
|
|
CefString CefDOMNodeImpl::GetElementTagName() {
|
|
|
|
CefString str;
|
|
|
|
if (!VerifyContext())
|
|
|
|
return str;
|
|
|
|
|
|
|
|
if (!node_.isElementNode()) {
|
|
|
|
NOTREACHED();
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2013-11-08 22:28:56 +01:00
|
|
|
const WebElement& element = node_.toConst<blink::WebElement>();
|
2012-04-27 23:19:06 +02:00
|
|
|
const WebString& tagname = element.tagName();
|
|
|
|
if (!tagname.isNull())
|
|
|
|
str = tagname;
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefDOMNodeImpl::HasElementAttributes() {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!node_.isElementNode()) {
|
|
|
|
NOTREACHED();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-11-08 22:28:56 +01:00
|
|
|
const WebElement& element = node_.toConst<blink::WebElement>();
|
2012-04-27 23:19:06 +02:00
|
|
|
return (element.attributeCount() > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefDOMNodeImpl::HasElementAttribute(const CefString& attrName) {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!node_.isElementNode()) {
|
|
|
|
NOTREACHED();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-11-08 22:28:56 +01:00
|
|
|
const WebElement& element = node_.toConst<blink::WebElement>();
|
2013-12-17 23:04:35 +01:00
|
|
|
return element.hasAttribute(base::string16(attrName));
|
2012-04-27 23:19:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CefString CefDOMNodeImpl::GetElementAttribute(const CefString& attrName) {
|
|
|
|
CefString str;
|
|
|
|
if (!VerifyContext())
|
|
|
|
return str;
|
|
|
|
|
|
|
|
if (!node_.isElementNode()) {
|
|
|
|
NOTREACHED();
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2013-11-08 22:28:56 +01:00
|
|
|
const WebElement& element = node_.toConst<blink::WebElement>();
|
2013-12-17 23:04:35 +01:00
|
|
|
const WebString& attr = element.getAttribute(base::string16(attrName));
|
2012-04-27 23:19:06 +02:00
|
|
|
if (!attr.isNull())
|
|
|
|
str = attr;
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefDOMNodeImpl::GetElementAttributes(AttributeMap& attrMap) {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!node_.isElementNode()) {
|
|
|
|
NOTREACHED();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-08 22:28:56 +01:00
|
|
|
const WebElement& element = node_.toConst<blink::WebElement>();
|
2012-04-27 23:19:06 +02:00
|
|
|
unsigned int len = element.attributeCount();
|
|
|
|
if (len == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < len; ++i) {
|
2013-12-17 23:04:35 +01:00
|
|
|
base::string16 name = element.attributeLocalName(i);
|
|
|
|
base::string16 value = element.attributeValue(i);
|
2012-04-27 23:19:06 +02:00
|
|
|
attrMap.insert(std::make_pair(name, value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefDOMNodeImpl::SetElementAttribute(const CefString& attrName,
|
|
|
|
const CefString& value) {
|
|
|
|
if (!VerifyContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!node_.isElementNode()) {
|
|
|
|
NOTREACHED();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-11-08 22:28:56 +01:00
|
|
|
WebElement element = node_.to<blink::WebElement>();
|
2013-12-17 23:04:35 +01:00
|
|
|
return element.setAttribute(base::string16(attrName),
|
|
|
|
base::string16(value));
|
2012-04-27 23:19:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CefString CefDOMNodeImpl::GetElementInnerText() {
|
|
|
|
CefString str;
|
|
|
|
if (!VerifyContext())
|
|
|
|
return str;
|
|
|
|
|
|
|
|
if (!node_.isElementNode()) {
|
|
|
|
NOTREACHED();
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2013-11-08 22:28:56 +01:00
|
|
|
WebElement element = node_.to<blink::WebElement>();
|
2012-04-27 23:19:06 +02:00
|
|
|
const WebString& text = element.innerText();
|
|
|
|
if (!text.isNull())
|
|
|
|
str = text;
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefDOMNodeImpl::Detach() {
|
|
|
|
document_ = NULL;
|
|
|
|
node_.assign(WebNode());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefDOMNodeImpl::VerifyContext() {
|
|
|
|
if (!document_.get()) {
|
|
|
|
NOTREACHED();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!document_->VerifyContext())
|
|
|
|
return false;
|
|
|
|
if (node_.isNull()) {
|
|
|
|
NOTREACHED();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|