// 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/blink_glue.h" #include "libcef/renderer/browser_impl.h" #include "libcef/renderer/dom_document_impl.h" #include "libcef/renderer/thread_util.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/public/web/web_document.h" #include "third_party/blink/public/web/web_dom_event.h" #include "third_party/blink/public/web/web_element.h" #include "third_party/blink/public/web/web_form_control_element.h" #include "third_party/blink/public/web/web_input_element.h" #include "third_party/blink/public/web/web_node.h" #include "third_party/blink/public/web/web_select_element.h" using blink::WebDocument; using blink::WebDOMEvent; using blink::WebElement; using blink::WebFormControlElement; using blink::WebInputElement; using blink::WebNode; using blink::WebSelectElement; using blink::WebString; using FormControlType = WebFormControlElement::Type; namespace { cef_dom_form_control_type_t GetCefFormControlType(FormControlType type) { switch (type) { case FormControlType::kButtonButton: return DOM_FORM_CONTROL_TYPE_BUTTON_BUTTON; case FormControlType::kButtonSubmit: return DOM_FORM_CONTROL_TYPE_BUTTON_SUBMIT; case FormControlType::kButtonReset: return DOM_FORM_CONTROL_TYPE_BUTTON_RESET; case FormControlType::kButtonSelectList: return DOM_FORM_CONTROL_TYPE_BUTTON_SELECT_LIST; case FormControlType::kFieldset: return DOM_FORM_CONTROL_TYPE_FIELDSET; case FormControlType::kInputButton: return DOM_FORM_CONTROL_TYPE_INPUT_BUTTON; case FormControlType::kInputCheckbox: return DOM_FORM_CONTROL_TYPE_INPUT_CHECKBOX; case FormControlType::kInputColor: return DOM_FORM_CONTROL_TYPE_INPUT_COLOR; case FormControlType::kInputDate: return DOM_FORM_CONTROL_TYPE_INPUT_DATE; case FormControlType::kInputDatetimeLocal: return DOM_FORM_CONTROL_TYPE_INPUT_DATETIME_LOCAL; case FormControlType::kInputEmail: return DOM_FORM_CONTROL_TYPE_INPUT_EMAIL; case FormControlType::kInputFile: return DOM_FORM_CONTROL_TYPE_INPUT_FILE; case FormControlType::kInputHidden: return DOM_FORM_CONTROL_TYPE_INPUT_HIDDEN; case FormControlType::kInputImage: return DOM_FORM_CONTROL_TYPE_INPUT_IMAGE; case FormControlType::kInputMonth: return DOM_FORM_CONTROL_TYPE_INPUT_MONTH; case FormControlType::kInputNumber: return DOM_FORM_CONTROL_TYPE_INPUT_NUMBER; case FormControlType::kInputPassword: return DOM_FORM_CONTROL_TYPE_INPUT_PASSWORD; case FormControlType::kInputRadio: return DOM_FORM_CONTROL_TYPE_INPUT_RADIO; case FormControlType::kInputRange: return DOM_FORM_CONTROL_TYPE_INPUT_RANGE; case FormControlType::kInputReset: return DOM_FORM_CONTROL_TYPE_INPUT_RESET; case FormControlType::kInputSearch: return DOM_FORM_CONTROL_TYPE_INPUT_SEARCH; case FormControlType::kInputSubmit: return DOM_FORM_CONTROL_TYPE_INPUT_SUBMIT; case FormControlType::kInputTelephone: return DOM_FORM_CONTROL_TYPE_INPUT_TELEPHONE; case FormControlType::kInputText: return DOM_FORM_CONTROL_TYPE_INPUT_TEXT; case FormControlType::kInputTime: return DOM_FORM_CONTROL_TYPE_INPUT_TIME; case FormControlType::kInputUrl: return DOM_FORM_CONTROL_TYPE_INPUT_URL; case FormControlType::kInputWeek: return DOM_FORM_CONTROL_TYPE_INPUT_WEEK; case FormControlType::kOutput: return DOM_FORM_CONTROL_TYPE_OUTPUT; case FormControlType::kSelectOne: return DOM_FORM_CONTROL_TYPE_SELECT_ONE; case FormControlType::kSelectMultiple: return DOM_FORM_CONTROL_TYPE_SELECT_MULTIPLE; case FormControlType::kSelectList: return DOM_FORM_CONTROL_TYPE_SELECT_LIST; case FormControlType::kTextArea: return DOM_FORM_CONTROL_TYPE_TEXT_AREA; } return DOM_FORM_CONTROL_TYPE_UNSUPPORTED; } } // namespace CefDOMNodeImpl::CefDOMNodeImpl(CefRefPtr document, const blink::WebNode& node) : 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; } return blink_glue::GetNodeType(node_); } bool CefDOMNodeImpl::IsText() { if (!VerifyContext()) { return false; } return node_.IsTextNode(); } bool CefDOMNodeImpl::IsElement() { if (!VerifyContext()) { return false; } return node_.IsElementNode(); } // Logic copied from RenderViewImpl::IsEditableNode. bool CefDOMNodeImpl::IsEditable() { if (!VerifyContext()) { return false; } if (node_.IsContentEditable()) { return true; } if (node_.IsElementNode()) { const WebElement& element = node_.To(); if (blink_glue::IsTextControlElement(element)) { return true; } // Also return true if it has an ARIA role of 'textbox'. for (unsigned i = 0; i < element.AttributeCount(); ++i) { if (base::EqualsCaseInsensitiveASCII(element.AttributeLocalName(i).Utf8(), "role")) { if (base::EqualsCaseInsensitiveASCII(element.AttributeValue(i).Utf8(), "textbox")) { return true; } break; } } } return false; } bool CefDOMNodeImpl::IsFormControlElement() { if (!VerifyContext()) { return false; } if (node_.IsElementNode()) { const WebElement& element = node_.To(); return element.IsFormControlElement(); } return false; } CefDOMNodeImpl::FormControlType CefDOMNodeImpl::GetFormControlElementType() { if (!VerifyContext()) { return DOM_FORM_CONTROL_TYPE_UNSUPPORTED; } if (node_.IsElementNode()) { const WebElement& element = node_.To(); if (element.IsFormControlElement()) { // Retrieve the type from the form control element. const WebFormControlElement& formElement = node_.To(); return GetCefFormControlType(formElement.FormControlType()); } } return DOM_FORM_CONTROL_TYPE_UNSUPPORTED; } bool CefDOMNodeImpl::IsSame(CefRefPtr that) { if (!VerifyContext()) { return false; } CefDOMNodeImpl* impl = static_cast(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 = blink_glue::GetNodeName(node_); if (!name.IsNull()) { str = name.Utf16(); } return str; } CefString CefDOMNodeImpl::GetValue() { CefString str; if (!VerifyContext()) { return str; } if (node_.IsElementNode()) { const WebElement& element = node_.To(); if (element.IsFormControlElement()) { // Retrieve the value from the form control element. const WebFormControlElement& formElement = node_.To(); std::u16string value = formElement.Value().Utf16(); base::TrimWhitespace(value, base::TRIM_LEADING, &value); str = value; } } if (str.empty()) { const WebString& value = node_.NodeValue(); if (!value.IsNull()) { str = value.Utf16(); } } return str; } bool CefDOMNodeImpl::SetValue(const CefString& value) { if (!VerifyContext()) { return false; } if (node_.IsElementNode()) { return false; } return blink_glue::SetNodeValue(node_, WebString::FromUTF16(value.ToString16())); } CefString CefDOMNodeImpl::GetAsMarkup() { CefString str; if (!VerifyContext()) { return str; } const WebString& markup = blink_glue::CreateNodeMarkup(node_); if (!markup.IsNull()) { str = markup.Utf16(); } return str; } CefRefPtr CefDOMNodeImpl::GetDocument() { if (!VerifyContext()) { return nullptr; } return document_.get(); } CefRefPtr CefDOMNodeImpl::GetParent() { if (!VerifyContext()) { return nullptr; } return document_->GetOrCreateNode(node_.ParentNode()); } CefRefPtr CefDOMNodeImpl::GetPreviousSibling() { if (!VerifyContext()) { return nullptr; } return document_->GetOrCreateNode(node_.PreviousSibling()); } CefRefPtr CefDOMNodeImpl::GetNextSibling() { if (!VerifyContext()) { return nullptr; } return document_->GetOrCreateNode(node_.NextSibling()); } bool CefDOMNodeImpl::HasChildren() { if (!VerifyContext()) { return false; } return !node_.FirstChild().IsNull(); } CefRefPtr CefDOMNodeImpl::GetFirstChild() { if (!VerifyContext()) { return nullptr; } return document_->GetOrCreateNode(node_.FirstChild()); } CefRefPtr CefDOMNodeImpl::GetLastChild() { if (!VerifyContext()) { return nullptr; } return document_->GetOrCreateNode(node_.LastChild()); } CefString CefDOMNodeImpl::GetElementTagName() { CefString str; if (!VerifyContext()) { return str; } if (!node_.IsElementNode()) { DCHECK(false); return str; } const WebElement& element = node_.To(); const WebString& tagname = element.TagName(); if (!tagname.IsNull()) { str = tagname.Utf16(); } return str; } bool CefDOMNodeImpl::HasElementAttributes() { if (!VerifyContext()) { return false; } if (!node_.IsElementNode()) { DCHECK(false); return false; } const WebElement& element = node_.To(); return (element.AttributeCount() > 0); } bool CefDOMNodeImpl::HasElementAttribute(const CefString& attrName) { if (!VerifyContext()) { return false; } if (!node_.IsElementNode()) { DCHECK(false); return false; } const WebElement& element = node_.To(); return element.HasAttribute(WebString::FromUTF16(attrName.ToString16())); } CefString CefDOMNodeImpl::GetElementAttribute(const CefString& attrName) { CefString str; if (!VerifyContext()) { return str; } if (!node_.IsElementNode()) { DCHECK(false); return str; } const WebElement& element = node_.To(); const WebString& attr = element.GetAttribute(WebString::FromUTF16(attrName.ToString16())); if (!attr.IsNull()) { str = attr.Utf16(); } return str; } void CefDOMNodeImpl::GetElementAttributes(AttributeMap& attrMap) { if (!VerifyContext()) { return; } if (!node_.IsElementNode()) { DCHECK(false); return; } const WebElement& element = node_.To(); unsigned int len = element.AttributeCount(); if (len == 0) { return; } for (unsigned int i = 0; i < len; ++i) { std::u16string name = element.AttributeLocalName(i).Utf16(); std::u16string value = element.AttributeValue(i).Utf16(); attrMap.insert(std::make_pair(name, value)); } } bool CefDOMNodeImpl::SetElementAttribute(const CefString& attrName, const CefString& value) { if (!VerifyContext()) { return false; } if (!node_.IsElementNode()) { DCHECK(false); return false; } WebElement element = node_.To(); element.SetAttribute(WebString::FromUTF16(attrName.ToString16()), WebString::FromUTF16(value.ToString16())); return true; } CefString CefDOMNodeImpl::GetElementInnerText() { CefString str; if (!VerifyContext()) { return str; } if (!node_.IsElementNode()) { DCHECK(false); return str; } WebElement element = node_.To(); const WebString& text = element.TextContent(); if (!text.IsNull()) { str = text.Utf16(); } return str; } CefRect CefDOMNodeImpl::GetElementBounds() { CefRect rect; if (!VerifyContext()) { return rect; } if (!node_.IsElementNode()) { DCHECK(false); return rect; } WebElement element = node_.To(); const auto& rc = element.BoundsInWidget(); rect.Set(rc.x(), rc.y(), rc.width(), rc.height()); return rect; } void CefDOMNodeImpl::Detach() { document_ = nullptr; node_.Assign(WebNode()); } bool CefDOMNodeImpl::VerifyContext() { if (!document_.get()) { DCHECK(false); return false; } if (!document_->VerifyContext()) { return false; } if (node_.IsNull()) { DCHECK(false); return false; } return true; }