| // 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/browser/xml_reader_impl.h" |
| #include "base/logging.h" |
| #include "include/cef_stream.h" |
| |
| // Static functions |
| |
| // static |
| CefRefPtr<CefXmlReader> CefXmlReader::Create(CefRefPtr<CefStreamReader> stream, |
| EncodingType encodingType, |
| const CefString& URI) { |
| CefRefPtr<CefXmlReaderImpl> impl(new CefXmlReaderImpl()); |
| if (!impl->Initialize(stream, encodingType, URI)) |
| return nullptr; |
| return impl.get(); |
| } |
| |
| // CefXmlReaderImpl |
| |
| namespace { |
| |
| /** |
| * xmlInputReadCallback: |
| * @context: an Input context |
| * @buffer: the buffer to store data read |
| * @len: the length of the buffer in bytes |
| * |
| * Callback used in the I/O Input API to read the resource |
| * |
| * Returns the number of bytes read or -1 in case of error |
| */ |
| int XMLCALL xml_read_callback(void* context, char* buffer, int len) { |
| CefRefPtr<CefStreamReader> reader(static_cast<CefStreamReader*>(context)); |
| return reader->Read(buffer, 1, len); |
| } |
| |
| /** |
| * xmlTextReaderErrorFunc: |
| * @arg: the user argument |
| * @msg: the message |
| * @severity: the severity of the error |
| * @locator: a locator indicating where the error occured |
| * |
| * Signature of an error callback from a reader parser |
| */ |
| void XMLCALL xml_error_callback(void* arg, |
| const char* msg, |
| xmlParserSeverities severity, |
| xmlTextReaderLocatorPtr locator) { |
| if (!msg) |
| return; |
| |
| std::string error_str(msg); |
| if (!error_str.empty() && error_str[error_str.length() - 1] == '\n') |
| error_str.resize(error_str.length() - 1); |
| |
| std::stringstream ss; |
| ss << error_str << ", line " << xmlTextReaderLocatorLineNumber(locator); |
| |
| LOG(INFO) << ss.str(); |
| |
| CefRefPtr<CefXmlReaderImpl> impl(static_cast<CefXmlReaderImpl*>(arg)); |
| impl->AppendError(ss.str()); |
| } |
| |
| /** |
| * xmlStructuredErrorFunc: |
| * @userData: user provided data for the error callback |
| * @error: the error being raised. |
| * |
| * Signature of the function to use when there is an error and |
| * the module handles the new error reporting mechanism. |
| */ |
| void XMLCALL xml_structured_error_callback(void* userData, xmlErrorPtr error) { |
| if (!error->message) |
| return; |
| |
| std::string error_str(error->message); |
| if (!error_str.empty() && error_str[error_str.length() - 1] == '\n') |
| error_str.resize(error_str.length() - 1); |
| |
| std::stringstream ss; |
| ss << error_str << ", line " << error->line; |
| |
| LOG(INFO) << ss.str(); |
| |
| CefRefPtr<CefXmlReaderImpl> impl(static_cast<CefXmlReaderImpl*>(userData)); |
| impl->AppendError(ss.str()); |
| } |
| |
| CefString xmlCharToString(const xmlChar* xmlStr, bool free) { |
| if (!xmlStr) |
| return CefString(); |
| |
| const char* str = reinterpret_cast<const char*>(xmlStr); |
| CefString wstr = std::string(str); |
| |
| if (free) |
| xmlFree(const_cast<xmlChar*>(xmlStr)); |
| |
| return wstr; |
| } |
| |
| } // namespace |
| |
| CefXmlReaderImpl::CefXmlReaderImpl() |
| : supported_thread_id_(base::PlatformThread::CurrentId()), |
| reader_(nullptr) {} |
| |
| CefXmlReaderImpl::~CefXmlReaderImpl() { |
| if (reader_ != nullptr) { |
| if (!VerifyContext()) { |
| // Close() is supposed to be called directly. We'll try to free the reader |
| // now on the wrong thread but there's no guarantee this call won't crash. |
| xmlFreeTextReader(reader_); |
| } else { |
| Close(); |
| } |
| } |
| } |
| |
| bool CefXmlReaderImpl::Initialize(CefRefPtr<CefStreamReader> stream, |
| EncodingType encodingType, |
| const CefString& URI) { |
| xmlCharEncoding enc = XML_CHAR_ENCODING_NONE; |
| switch (encodingType) { |
| case XML_ENCODING_UTF8: |
| enc = XML_CHAR_ENCODING_UTF8; |
| break; |
| case XML_ENCODING_UTF16LE: |
| enc = XML_CHAR_ENCODING_UTF16LE; |
| break; |
| case XML_ENCODING_UTF16BE: |
| enc = XML_CHAR_ENCODING_UTF16BE; |
| break; |
| case XML_ENCODING_ASCII: |
| enc = XML_CHAR_ENCODING_ASCII; |
| break; |
| default: |
| break; |
| } |
| |
| // Create the input buffer. |
| xmlParserInputBufferPtr input_buffer = xmlAllocParserInputBuffer(enc); |
| if (!input_buffer) |
| return false; |
| |
| input_buffer->context = stream.get(); |
| input_buffer->readcallback = xml_read_callback; |
| |
| // Create the text reader. |
| std::string uriStr = URI; |
| reader_ = xmlNewTextReader(input_buffer, uriStr.c_str()); |
| if (!reader_) { |
| // Free the input buffer. |
| xmlFreeParserInputBuffer(input_buffer); |
| return false; |
| } |
| |
| // Keep a reference to the stream. |
| stream_ = stream; |
| |
| // Register the error callbacks. |
| xmlTextReaderSetErrorHandler(reader_, xml_error_callback, this); |
| xmlTextReaderSetStructuredErrorHandler(reader_, xml_structured_error_callback, |
| this); |
| |
| return true; |
| } |
| |
| bool CefXmlReaderImpl::MoveToNextNode() { |
| if (!VerifyContext()) |
| return false; |
| |
| return xmlTextReaderRead(reader_) == 1 ? true : false; |
| } |
| |
| bool CefXmlReaderImpl::Close() { |
| if (!VerifyContext()) |
| return false; |
| |
| // The input buffer will be freed automatically. |
| xmlFreeTextReader(reader_); |
| reader_ = nullptr; |
| return true; |
| } |
| |
| bool CefXmlReaderImpl::HasError() { |
| if (!VerifyContext()) |
| return false; |
| |
| return !error_buf_.str().empty(); |
| } |
| |
| CefString CefXmlReaderImpl::GetError() { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| return error_buf_.str(); |
| } |
| |
| CefXmlReader::NodeType CefXmlReaderImpl::GetType() { |
| if (!VerifyContext()) |
| return XML_NODE_UNSUPPORTED; |
| |
| switch (xmlTextReaderNodeType(reader_)) { |
| case XML_READER_TYPE_ELEMENT: |
| return XML_NODE_ELEMENT_START; |
| case XML_READER_TYPE_END_ELEMENT: |
| return XML_NODE_ELEMENT_END; |
| case XML_READER_TYPE_ATTRIBUTE: |
| return XML_NODE_ATTRIBUTE; |
| case XML_READER_TYPE_TEXT: |
| return XML_NODE_TEXT; |
| case XML_READER_TYPE_SIGNIFICANT_WHITESPACE: |
| case XML_READER_TYPE_WHITESPACE: |
| return XML_NODE_WHITESPACE; |
| case XML_READER_TYPE_CDATA: |
| return XML_NODE_CDATA; |
| case XML_READER_TYPE_ENTITY_REFERENCE: |
| return XML_NODE_ENTITY_REFERENCE; |
| case XML_READER_TYPE_PROCESSING_INSTRUCTION: |
| return XML_NODE_PROCESSING_INSTRUCTION; |
| case XML_READER_TYPE_COMMENT: |
| return XML_NODE_COMMENT; |
| case XML_READER_TYPE_DOCUMENT_TYPE: |
| return XML_NODE_DOCUMENT_TYPE; |
| default: |
| break; |
| } |
| |
| return XML_NODE_UNSUPPORTED; |
| } |
| |
| int CefXmlReaderImpl::GetDepth() { |
| if (!VerifyContext()) |
| return -1; |
| |
| return xmlTextReaderDepth(reader_); |
| } |
| |
| CefString CefXmlReaderImpl::GetLocalName() { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| return xmlCharToString(xmlTextReaderConstLocalName(reader_), false); |
| } |
| |
| CefString CefXmlReaderImpl::GetPrefix() { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| return xmlCharToString(xmlTextReaderConstPrefix(reader_), false); |
| } |
| |
| CefString CefXmlReaderImpl::GetQualifiedName() { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| return xmlCharToString(xmlTextReaderConstName(reader_), false); |
| } |
| |
| CefString CefXmlReaderImpl::GetNamespaceURI() { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| return xmlCharToString(xmlTextReaderConstNamespaceUri(reader_), false); |
| } |
| |
| CefString CefXmlReaderImpl::GetBaseURI() { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| return xmlCharToString(xmlTextReaderConstBaseUri(reader_), false); |
| } |
| |
| CefString CefXmlReaderImpl::GetXmlLang() { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| return xmlCharToString(xmlTextReaderConstXmlLang(reader_), false); |
| } |
| |
| bool CefXmlReaderImpl::IsEmptyElement() { |
| if (!VerifyContext()) |
| return false; |
| |
| return xmlTextReaderIsEmptyElement(reader_) == 1 ? true : false; |
| } |
| |
| bool CefXmlReaderImpl::HasValue() { |
| if (!VerifyContext()) |
| return false; |
| |
| if (xmlTextReaderNodeType(reader_) == XML_READER_TYPE_ENTITY_REFERENCE) { |
| // Provide special handling to return entity reference values. |
| return true; |
| } else { |
| return xmlTextReaderHasValue(reader_) == 1 ? true : false; |
| } |
| } |
| |
| CefString CefXmlReaderImpl::GetValue() { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| if (xmlTextReaderNodeType(reader_) == XML_READER_TYPE_ENTITY_REFERENCE) { |
| // Provide special handling to return entity reference values. |
| xmlNodePtr node = xmlTextReaderCurrentNode(reader_); |
| if (node->content != nullptr) |
| return xmlCharToString(node->content, false); |
| return CefString(); |
| } else { |
| return xmlCharToString(xmlTextReaderConstValue(reader_), false); |
| } |
| } |
| |
| bool CefXmlReaderImpl::HasAttributes() { |
| if (!VerifyContext()) |
| return false; |
| |
| return xmlTextReaderHasAttributes(reader_) == 1 ? true : false; |
| } |
| |
| size_t CefXmlReaderImpl::GetAttributeCount() { |
| if (!VerifyContext()) |
| return 0; |
| |
| return xmlTextReaderAttributeCount(reader_); |
| } |
| |
| CefString CefXmlReaderImpl::GetAttribute(int index) { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| return xmlCharToString(xmlTextReaderGetAttributeNo(reader_, index), true); |
| } |
| |
| CefString CefXmlReaderImpl::GetAttribute(const CefString& qualifiedName) { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| std::string qualifiedNameStr = qualifiedName; |
| return xmlCharToString( |
| xmlTextReaderGetAttribute(reader_, BAD_CAST qualifiedNameStr.c_str()), |
| true); |
| } |
| |
| CefString CefXmlReaderImpl::GetAttribute(const CefString& localName, |
| const CefString& namespaceURI) { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| std::string localNameStr = localName; |
| std::string namespaceURIStr = namespaceURI; |
| return xmlCharToString( |
| xmlTextReaderGetAttributeNs(reader_, BAD_CAST localNameStr.c_str(), |
| BAD_CAST namespaceURIStr.c_str()), |
| true); |
| } |
| |
| CefString CefXmlReaderImpl::GetInnerXml() { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| return xmlCharToString(xmlTextReaderReadInnerXml(reader_), true); |
| } |
| |
| CefString CefXmlReaderImpl::GetOuterXml() { |
| if (!VerifyContext()) |
| return CefString(); |
| |
| return xmlCharToString(xmlTextReaderReadOuterXml(reader_), true); |
| } |
| |
| int CefXmlReaderImpl::GetLineNumber() { |
| if (!VerifyContext()) |
| return -1; |
| |
| return xmlTextReaderGetParserLineNumber(reader_); |
| } |
| |
| bool CefXmlReaderImpl::MoveToAttribute(int index) { |
| if (!VerifyContext()) |
| return false; |
| |
| return xmlTextReaderMoveToAttributeNo(reader_, index) == 1 ? true : false; |
| } |
| |
| bool CefXmlReaderImpl::MoveToAttribute(const CefString& qualifiedName) { |
| if (!VerifyContext()) |
| return false; |
| |
| std::string qualifiedNameStr = qualifiedName; |
| return xmlTextReaderMoveToAttribute(reader_, |
| BAD_CAST qualifiedNameStr.c_str()) == 1 |
| ? true |
| : false; |
| } |
| |
| bool CefXmlReaderImpl::MoveToAttribute(const CefString& localName, |
| const CefString& namespaceURI) { |
| if (!VerifyContext()) |
| return false; |
| |
| std::string localNameStr = localName; |
| std::string namespaceURIStr = namespaceURI; |
| return xmlTextReaderMoveToAttributeNs(reader_, BAD_CAST localNameStr.c_str(), |
| BAD_CAST namespaceURIStr.c_str()) == 1 |
| ? true |
| : false; |
| } |
| |
| bool CefXmlReaderImpl::MoveToFirstAttribute() { |
| if (!VerifyContext()) |
| return false; |
| |
| return xmlTextReaderMoveToFirstAttribute(reader_) == 1 ? true : false; |
| } |
| |
| bool CefXmlReaderImpl::MoveToNextAttribute() { |
| if (!VerifyContext()) |
| return false; |
| |
| return xmlTextReaderMoveToNextAttribute(reader_) == 1 ? true : false; |
| } |
| |
| bool CefXmlReaderImpl::MoveToCarryingElement() { |
| if (!VerifyContext()) |
| return false; |
| |
| return xmlTextReaderMoveToElement(reader_) == 1 ? true : false; |
| } |
| |
| void CefXmlReaderImpl::AppendError(const CefString& error_str) { |
| if (!error_buf_.str().empty()) |
| error_buf_ << L"\n"; |
| error_buf_ << error_str; |
| } |
| |
| bool CefXmlReaderImpl::VerifyContext() { |
| if (base::PlatformThread::CurrentId() != supported_thread_id_) { |
| // This object should only be accessed from the thread that created it. |
| NOTREACHED(); |
| return false; |
| } |
| |
| return (reader_ != nullptr); |
| } |