summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2009-06-15 09:06:43 (GMT)
committerSimon Hausmann <simon.hausmann@nokia.com>2009-06-15 09:31:31 (GMT)
commitc411f16870f112c3407c28c22b617f613a82cff4 (patch)
tree29a1bcd590c8b31af2aab445bfe8a978dc5bf582 /src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp
parent3d77b56b32a0c53ec0bbfaa07236fedb900ff336 (diff)
downloadQt-c411f16870f112c3407c28c22b617f613a82cff4.zip
Qt-c411f16870f112c3407c28c22b617f613a82cff4.tar.gz
Qt-c411f16870f112c3407c28c22b617f613a82cff4.tar.bz2
Updated WebKit from /home/shausman/src/webkit/trunk to qtwebkit-4.6-snapshot-15062009 ( 65232bf00dc494ebfd978f998c88f58d18ecce1e )
Diffstat (limited to 'src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp')
-rw-r--r--src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp1603
1 files changed, 1603 insertions, 0 deletions
diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp b/src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp
new file mode 100644
index 0000000..ff0463b
--- /dev/null
+++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp
@@ -0,0 +1,1603 @@
+/*
+ Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+#include "qwebelement.h"
+
+#include "CSSComputedStyleDeclaration.h"
+#include "CSSMutableStyleDeclaration.h"
+#include "CSSParser.h"
+#include "CSSRuleList.h"
+#include "CSSRule.h"
+#include "CSSStyleRule.h"
+#include "CString.h"
+#include "Document.h"
+#include "DocumentFragment.h"
+#include "FrameView.h"
+#include "HTMLElement.h"
+#include "JSGlobalObject.h"
+#include "JSHTMLElement.h"
+#include "JSObject.h"
+#include "NodeList.h"
+#include "PropertyNameArray.h"
+#include "ScriptFunctionCall.h"
+#include "StaticNodeList.h"
+#include "qt_runtime.h"
+#include "qwebframe.h"
+#include "qwebframe_p.h"
+#include "runtime_root.h"
+#include <wtf/Vector.h>
+
+using namespace WebCore;
+
+class QWebElementPrivate
+{
+public:
+};
+
+/*!
+ \class QWebElement
+ \since 4.6
+ \brief The QWebElement class provides convenience access to DOM elements in a QWebFrame.
+ \preliminary
+
+ QWebElement is the main class to provide easy access to the document model.
+ The document model is represented by a tree-like structure of DOM elements.
+ The root of the tree is called the document element and can be accessed using QWebFrame::documentElement().
+
+ You can reach specific elements by using the findAll() and findFirst() functions, which
+ allow the use of CSS selectors to identify elements.
+
+ \snippet webkitsnippets/webelement/main.cpp FindAll
+
+ The first list contains all span elements in the document. The second list contains
+ only the span elements that are children of the paragraph that is classified
+ as "intro" paragraph.
+
+ Alternatively you can manually traverse the document using firstChild() and nextSibling():
+
+ \snippet webkitsnippets/webelement/main.cpp Traversing with QWebElement
+
+ The underlying content of QWebElement is explicitly shared. Creating a copy of a QWebElement
+ does not create a copy of the content, both instances point to the same underlying element.
+
+ The element's attributes can be read using attribute() and changed using setAttribute().
+
+ The content of the child elements can be converted to plain text using toPlainText() and to
+ x(html) using toXml(), and it is possible to replace the content using setPlainText() and setXml().
+
+ Depending on the type of the underlying element there may be extra functionality available, not
+ covered through QWebElement's API. For example a HTML form element can be triggered to submit the
+ entire form. These list of these functions is available through functions() and they can be called
+ directly using callFunction().
+*/
+
+/*!
+ Constructs a null web element.
+*/
+QWebElement::QWebElement()
+ : d(0)
+ , m_element(0)
+{
+}
+
+/*!
+ \internal
+*/
+QWebElement::QWebElement(WebCore::Element* domElement)
+ : d(0)
+ , m_element(domElement)
+{
+ if (m_element)
+ m_element->ref();
+}
+
+/*!
+ \internal
+*/
+QWebElement::QWebElement(WebCore::Node* node)
+ : d(0)
+ , m_element(0)
+{
+ if (node && node->isHTMLElement()) {
+ m_element = static_cast<HTMLElement*>(node);
+ m_element->ref();
+ }
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QWebElement::QWebElement(const QWebElement &other)
+ : d(0)
+ , m_element(other.m_element)
+{
+ if (m_element)
+ m_element->ref();
+}
+
+/*!
+ Assigns \a other to this element and returns a reference to this element.
+*/
+QWebElement &QWebElement::operator=(const QWebElement &other)
+{
+ // ### handle "d" assignment
+ if (this != &other) {
+ Element *otherElement = other.m_element;
+ if (otherElement)
+ otherElement->ref();
+ if (m_element)
+ m_element->deref();
+ m_element = otherElement;
+ }
+ return *this;
+}
+
+/*!
+ Destroys the element. The underlying DOM element is not destroyed.
+*/
+QWebElement::~QWebElement()
+{
+ delete d;
+ if (m_element)
+ m_element->deref();
+}
+
+bool QWebElement::operator==(const QWebElement& o) const
+{
+ return m_element == o.m_element;
+}
+
+bool QWebElement::operator!=(const QWebElement& o) const
+{
+ return m_element != o.m_element;
+}
+
+/*!
+ Returns true if the element is a null element; false otherwise.
+*/
+bool QWebElement::isNull() const
+{
+ return !m_element;
+}
+
+/*!
+ Returns a new collection of elements that are children of this element
+ and that match the given CSS selector \a selectorQuery.
+
+ The query is specified using \l{http://www.w3.org/TR/REC-CSS2/selector.html#q1}{standard CSS2 selectors}.
+*/
+QList<QWebElement> QWebElement::findAll(const QString &selectorQuery) const
+{
+ QList<QWebElement> elements;
+ if (!m_element)
+ return elements;
+
+ ExceptionCode exception = 0; // ###
+ RefPtr<NodeList> nodes = m_element->querySelectorAll(selectorQuery, exception);
+ if (!nodes)
+ return elements;
+
+ for (int i = 0; i < nodes->length(); ++i) {
+ WebCore::Node* n = nodes->item(i);
+ elements.append(QWebElement(static_cast<Element*>(n)));
+ }
+
+ return elements;
+}
+
+/*!
+ Returns the first child element that matches the given CSS selector \a selectorQuery.
+
+ This function is equivalent to calling findAll() and taking only the
+ first element in the returned collection of elements. However calling
+ this function is more efficient.
+*/
+QWebElement QWebElement::findFirst(const QString &selectorQuery) const
+{
+ if (!m_element)
+ return QWebElement();
+ ExceptionCode exception = 0; // ###
+ return QWebElement(m_element->querySelector(selectorQuery, exception).get());
+}
+
+/*!
+ Replaces the existing content of this element with \a text.
+
+ This is equivalent to setting the HTML innerText property.
+*/
+void QWebElement::setPlainText(const QString &text)
+{
+ if (!m_element || !m_element->isHTMLElement())
+ return;
+ ExceptionCode exception = 0;
+ static_cast<HTMLElement*>(m_element)->setInnerText(text, exception);
+}
+
+/*!
+ Returns the text between the start and the end tag of this
+ element.
+
+ This is equivalent to reading the HTML innerText property.
+*/
+QString QWebElement::toPlainText() const
+{
+ if (!m_element || !m_element->isHTMLElement())
+ return QString();
+ return static_cast<HTMLElement*>(m_element)->innerText();
+}
+
+/*!
+ Replaces the contents of this element as well as its own tag with \a markup.
+ The string may contain HTML or XML tags, which is parsed and formatted
+ before insertion into the document.
+
+ \note This is currently only implemented for (X)HTML elements.
+*/
+void QWebElement::setOuterXml(const QString &markup)
+{
+ if (!m_element || !m_element->isHTMLElement())
+ return;
+
+ ExceptionCode exception = 0;
+
+ static_cast<HTMLElement*>(m_element)->setOuterHTML(markup, exception);
+}
+
+/*!
+ Returns this element converted to XML, including the start and the end
+ tag of this element and its attributes.
+
+ \note This is currently only implemented for (X)HTML elements.
+*/
+QString QWebElement::toOuterXml() const
+{
+ if (!m_element || !m_element->isHTMLElement())
+ return QString();
+
+ return static_cast<HTMLElement*>(m_element)->outerHTML();
+}
+
+/*!
+ Replaces the content of this element with \a markup.
+ The string may contain HTML or XML tags, which is parsed and formatted
+ before insertion into the document.
+
+ \note This is currently only implemented for (X)HTML elements.
+*/
+void QWebElement::setInnerXml(const QString &markup)
+{
+ if (!m_element || !m_element->isHTMLElement())
+ return;
+
+ ExceptionCode exception = 0;
+
+ static_cast<HTMLElement*>(m_element)->setInnerHTML(markup, exception);
+}
+
+/*!
+ Returns the XML between the start and the end tag of this
+ element.
+
+ \note This is currently only implemented for (X)HTML elements.
+*/
+QString QWebElement::toInnerXml() const
+{
+ if (!m_element || !m_element->isHTMLElement())
+ return QString();
+
+ return static_cast<HTMLElement*>(m_element)->innerHTML();
+}
+
+/*!
+ Adds an attribute called \a name with the value \a value. If an attribute
+ with the same name exists, its value is replaced by \a value.
+*/
+void QWebElement::setAttribute(const QString &name, const QString &value)
+{
+ if (!m_element)
+ return;
+ ExceptionCode exception = 0;
+ m_element->setAttribute(name, value, exception);
+}
+
+/*!
+ Adds an attribute called \a name in the namespace described with \a namespaceUri
+ with the value \a value. If an attribute with the same name exists, its value is
+ replaced by \a value.
+*/
+void QWebElement::setAttributeNS(const QString &namespaceUri, const QString &name, const QString &value)
+{
+ if (!m_element)
+ return;
+ WebCore::ExceptionCode exception = 0;
+ m_element->setAttributeNS(namespaceUri, name, value, exception);
+}
+
+/*!
+ Returns the attributed called \a name. If the attribute does not exist \a defaultValue is
+ returned.
+*/
+QString QWebElement::attribute(const QString &name, const QString &defaultValue) const
+{
+ if (!m_element)
+ return QString();
+ if (m_element->hasAttribute(name))
+ return m_element->getAttribute(name);
+ else
+ return defaultValue;
+}
+
+/*!
+ Returns the attributed called \a name in the namespace described with \a namespaceUri.
+ If the attribute does not exist \a defaultValue is returned.
+*/
+QString QWebElement::attributeNS(const QString &namespaceUri, const QString &name, const QString &defaultValue) const
+{
+ if (!m_element)
+ return QString();
+ if (m_element->hasAttributeNS(namespaceUri, name))
+ return m_element->getAttributeNS(namespaceUri, name);
+ else
+ return defaultValue;
+}
+
+/*!
+ Returns true if this element has an attribute called \a name; otherwise returns false.
+*/
+bool QWebElement::hasAttribute(const QString &name) const
+{
+ if (!m_element)
+ return false;
+ return m_element->hasAttribute(name);
+}
+
+/*!
+ Returns true if this element has an attribute called \a name in the namespace described
+ with \a namespaceUri; otherwise returns false.
+*/
+bool QWebElement::hasAttributeNS(const QString &namespaceUri, const QString &name) const
+{
+ if (!m_element)
+ return false;
+ return m_element->hasAttributeNS(namespaceUri, name);
+}
+
+/*!
+ Removes the attribute called \a name from this element.
+*/
+void QWebElement::removeAttribute(const QString &name)
+{
+ if (!m_element)
+ return;
+ ExceptionCode exception = 0;
+ m_element->removeAttribute(name, exception);
+}
+
+/*!
+ Removes the attribute called \a name in the namespace described with \a namespaceUri
+ from this element.
+*/
+void QWebElement::removeAttributeNS(const QString &namespaceUri, const QString &name)
+{
+ if (!m_element)
+ return;
+ WebCore::ExceptionCode exception = 0;
+ m_element->removeAttributeNS(namespaceUri, name, exception);
+}
+
+/*!
+ Returns true if the element has any attributes defined; otherwise returns false;
+*/
+bool QWebElement::hasAttributes() const
+{
+ if (!m_element)
+ return false;
+ return m_element->hasAttributes();
+}
+
+/*!
+ Returns the geometry of this element, relative to its containing frame.
+*/
+QRect QWebElement::geometry() const
+{
+ if (!m_element)
+ return QRect();
+ return m_element->getRect();
+}
+
+/*!
+ Returns the tag name of this element.
+*/
+QString QWebElement::tagName() const
+{
+ if (!m_element)
+ return QString();
+ return m_element->tagName();
+}
+
+/*!
+ Returns the namespace prefix of the element or an empty string if the element has no namespace prefix.
+*/
+QString QWebElement::prefix() const
+{
+ if (!m_element)
+ return QString();
+ return m_element->prefix();
+}
+
+/*!
+ If the element uses namespaces, this function returns the local name of the element;
+ otherwise it returns an empty string.
+*/
+QString QWebElement::localName() const
+{
+ if (!m_element)
+ return QString();
+ return m_element->localName();
+}
+
+/*!
+ Returns the namespace URI of this element or an empty string if the element has no namespace URI.
+*/
+QString QWebElement::namespaceUri() const
+{
+ if (!m_element)
+ return QString();
+ return m_element->namespaceURI();
+}
+
+/*!
+ Returns the parent element of this element or a null element if this element
+ is the root document element.
+*/
+QWebElement QWebElement::parent() const
+{
+ if (m_element)
+ return QWebElement(m_element->parentElement());
+ return QWebElement();
+}
+
+/*!
+ Returns the first child element of this element.
+
+ \sa lastChild() previousSibling() nextSibling()
+*/
+QWebElement QWebElement::firstChild() const
+{
+ if (!m_element)
+ return QWebElement();
+ for (Node* child = m_element->firstChild(); child; child = child->nextSibling()) {
+ if (!child->isElementNode())
+ continue;
+ Element* e = static_cast<Element*>(child);
+ return QWebElement(e);
+ }
+ return QWebElement();
+}
+
+/*!
+ Returns the last child element of this element.
+
+ \sa firstChild() previousSibling() nextSibling()
+*/
+QWebElement QWebElement::lastChild() const
+{
+ if (!m_element)
+ return QWebElement();
+ for (Node* child = m_element->lastChild(); child; child = child->previousSibling()) {
+ if (!child->isElementNode())
+ continue;
+ Element* e = static_cast<Element*>(child);
+ return QWebElement(e);
+ }
+ return QWebElement();
+}
+
+/*!
+ Returns the next sibling element of this element.
+
+ \sa firstChild() previousSibling() lastChild()
+*/
+QWebElement QWebElement::nextSibling() const
+{
+ if (!m_element)
+ return QWebElement();
+ for (Node* sib = m_element->nextSibling(); sib; sib = sib->nextSibling()) {
+ if (!sib->isElementNode())
+ continue;
+ Element* e = static_cast<Element*>(sib);
+ return QWebElement(e);
+ }
+ return QWebElement();
+}
+
+/*!
+ Returns the previous sibling element of this element.
+
+ \sa firstChild() nextSibling() lastChild()
+*/
+QWebElement QWebElement::previousSibling() const
+{
+ if (!m_element)
+ return QWebElement();
+ for (Node* sib = m_element->previousSibling(); sib; sib = sib->previousSibling()) {
+ if (!sib->isElementNode())
+ continue;
+ Element* e = static_cast<Element*>(sib);
+ return QWebElement(e);
+ }
+ return QWebElement();
+}
+
+/*!
+ Returns the document this element belongs to.
+*/
+QWebElement QWebElement::document() const
+{
+ if (!m_element)
+ return QWebElement();
+ Document* document = m_element->document();
+ if (!document)
+ return QWebElement();
+ return QWebElement(document->documentElement());
+}
+
+/*!
+ Returns the web frame this elements is a part of. If the element is
+ a null element null is returned.
+*/
+QWebFrame *QWebElement::webFrame() const
+{
+ if (!m_element)
+ return 0;
+
+ Document* document = m_element->document();
+ if (!document)
+ return 0;
+
+ Frame* frame = document->frame();
+ if (!frame)
+ return 0;
+ return QWebFramePrivate::kit(frame);
+}
+
+static bool setupScriptContext(WebCore::Element* element, JSC::JSValue& thisValue, ScriptState*& state, ScriptController*& scriptController)
+{
+ if (!element)
+ return false;
+
+ Document* document = element->document();
+ if (!document)
+ return false;
+
+ Frame* frame = document->frame();
+ if (!frame)
+ return false;
+
+ scriptController = frame->script();
+ if (!scriptController)
+ return false;
+
+ state = scriptController->globalObject()->globalExec();
+ if (!state)
+ return false;
+
+ thisValue = toJS(state, element);
+ if (!thisValue)
+ return false;
+
+ return true;
+}
+
+
+static bool setupScriptObject(WebCore::Element* element, ScriptObject& object, ScriptState*& state, ScriptController*& scriptController)
+{
+ if (!element)
+ return false;
+
+ Document* document = element->document();
+ if (!document)
+ return false;
+
+ Frame* frame = document->frame();
+ if (!frame)
+ return false;
+
+ scriptController = frame->script();
+
+ state = scriptController->globalObject()->globalExec();
+
+ JSC::JSValue thisValue = toJS(state, element);
+ if (!thisValue)
+ return false;
+
+ JSC::JSObject* thisObject = thisValue.toObject(state);
+ if (!thisObject)
+ return false;
+
+ object = ScriptObject(thisObject);
+ return true;
+}
+
+/*!
+ Executes the \a scriptSource with this element as the `this' object.
+
+ \sa callFunction()
+*/
+QVariant QWebElement::evaluateScript(const QString& scriptSource)
+{
+ if (scriptSource.isEmpty())
+ return QVariant();
+
+ ScriptState* state = 0;
+ JSC::JSValue thisValue;
+ ScriptController* scriptController = 0;
+
+ if (!setupScriptContext(m_element, thisValue, state, scriptController))
+ return QVariant();
+
+ JSC::ScopeChain& scopeChain = state->dynamicGlobalObject()->globalScopeChain();
+ JSC::UString script((const ushort*)scriptSource.data(), scriptSource.length());
+ JSC::Completion completion = JSC::evaluate(state, scopeChain, JSC::makeSource(script), thisValue);
+ if ((completion.complType() != JSC::ReturnValue) && (completion.complType() != JSC::Normal))
+ return QVariant();
+
+ JSC::JSValue result = completion.value();
+ if (!result)
+ return QVariant();
+
+ int distance = 0;
+ return JSC::Bindings::convertValueToQVariant(state, result, QMetaType::Void, &distance);
+}
+
+/*!
+ Calls the function with the given \a name and \a arguments.
+
+ The underlying DOM element that QWebElement wraps may have dedicated functions depending
+ on its type. For example a form element can have the "submit" function, that would submit
+ the form to the destination specified in the HTML.
+
+ \sa functions()
+*/
+QVariant QWebElement::callFunction(const QString &name, const QVariantList &arguments)
+{
+ ScriptState* state = 0;
+ ScriptObject thisObject;
+ ScriptController* scriptController = 0;
+
+ if (!setupScriptObject(m_element, thisObject, state, scriptController))
+ return QVariant();
+
+ ScriptFunctionCall functionCall(state, thisObject, name);
+
+ for (QVariantList::ConstIterator it = arguments.constBegin(), end = arguments.constEnd();
+ it != end; ++it)
+ functionCall.appendArgument(JSC::Bindings::convertQVariantToValue(state, scriptController->bindingRootObject(), *it));
+
+ bool hadException = false;
+ ScriptValue result = functionCall.call(hadException);
+ if (hadException)
+ return QVariant();
+
+ int distance = 0;
+ return JSC::Bindings::convertValueToQVariant(state, result.jsValue(), QMetaType::Void, &distance);
+}
+
+/*!
+ Returns a list of function names this element supports.
+
+ The function names returned are the same functions that are callable from the DOM
+ element's JavaScript binding.
+
+ \sa callFunction()
+*/
+QStringList QWebElement::functions() const
+{
+ ScriptState* state = 0;
+ ScriptObject thisObject;
+ ScriptController* scriptController = 0;
+
+ if (!setupScriptObject(m_element, thisObject, state, scriptController))
+ return QStringList();
+
+ JSC::JSObject* object = thisObject.jsObject();
+ if (!object)
+ return QStringList();
+
+ QStringList names;
+
+ // Enumerate the contents of the object
+ JSC::PropertyNameArray properties(state);
+ object->getPropertyNames(state, properties);
+ for (JSC::PropertyNameArray::const_iterator it = properties.begin();
+ it != properties.end(); ++it) {
+
+ JSC::JSValue property = object->get(state, *it);
+ if (!property)
+ continue;
+
+ JSC::JSObject* function = property.toObject(state);
+ if (!function)
+ continue;
+
+ JSC::CallData callData;
+ JSC::CallType callType = function->getCallData(callData);
+ if (callType == JSC::CallTypeNone)
+ continue;
+
+ JSC::UString ustring = (*it).ustring();
+ names << QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
+ }
+
+ if (state->hadException())
+ state->clearException();
+
+ return names;
+}
+
+/*!
+ Returns the value of the element's \a name property.
+
+ If no such property exists, the returned variant is invalid.
+
+ The return property has the same value as the corresponding property
+ in the element's JavaScript binding with the same name.
+
+ Information about all available properties is provided through scriptProperties().
+
+ \sa setScriptableProperty(), scriptableProperties()
+*/
+QVariant QWebElement::scriptableProperty(const QString &name) const
+{
+ ScriptState* state = 0;
+ ScriptObject thisObject;
+ ScriptController *scriptController = 0;
+
+ if (!setupScriptObject(m_element, thisObject, state, scriptController))
+ return QVariant();
+
+ String wcName(name);
+ JSC::JSValue property = thisObject.jsObject()->get(state, JSC::Identifier(state, wcName));
+
+ // ###
+ if (state->hadException())
+ state->clearException();
+
+ int distance = 0;
+ return JSC::Bindings::convertValueToQVariant(state, property, QMetaType::Void, &distance);
+}
+
+/*!
+ Sets the value of the element's \a name property to \a value.
+
+ Information about all available properties is provided through scriptProperties().
+
+ Setting the property will affect the corresponding property
+ in the element's JavaScript binding with the same name.
+
+ \sa scriptableProperty(), scriptableProperties()
+*/
+void QWebElement::setScriptableProperty(const QString &name, const QVariant &value)
+{
+ ScriptState* state = 0;
+ ScriptObject thisObject;
+ ScriptController* scriptController = 0;
+
+ if (!setupScriptObject(m_element, thisObject, state, scriptController))
+ return;
+
+ JSC::JSValue jsValue = JSC::Bindings::convertQVariantToValue(state, scriptController->bindingRootObject(), value);
+ if (!jsValue)
+ return;
+
+ String wcName(name);
+ JSC::PutPropertySlot slot;
+ thisObject.jsObject()->put(state, JSC::Identifier(state, wcName), jsValue, slot);
+ if (state->hadException())
+ state->clearException();
+}
+
+/*!
+ Returns a list of property names this element supports.
+
+ The function names returned are the same properties that are accessible from the DOM
+ element's JavaScript binding.
+
+ \sa setScriptableProperty(), scriptableProperty()
+*/
+QStringList QWebElement::scriptableProperties() const
+{
+ if (!m_element)
+ return QStringList();
+
+ Document* document = m_element->document();
+ if (!document)
+ return QStringList();
+
+ Frame* frame = document->frame();
+ if (!frame)
+ return QStringList();
+
+ ScriptController* script = frame->script();
+ JSC::ExecState* exec = script->globalObject()->globalExec();
+
+ JSC::JSValue thisValue = toJS(exec, m_element);
+ if (!thisValue)
+ return QStringList();
+
+ JSC::JSObject* object = thisValue.toObject(exec);
+ if (!object)
+ return QStringList();
+
+ QStringList names;
+
+ // Enumerate the contents of the object
+ JSC::PropertyNameArray properties(exec);
+ object->getPropertyNames(exec, properties);
+ for (JSC::PropertyNameArray::const_iterator it = properties.begin();
+ it != properties.end(); ++it) {
+
+ JSC::JSValue property = object->get(exec, *it);
+ if (!property)
+ continue;
+
+ JSC::JSObject* function = property.toObject(exec);
+ if (!function)
+ continue;
+
+ JSC::CallData callData;
+ JSC::CallType callType = function->getCallData(callData);
+ if (callType != JSC::CallTypeNone)
+ continue;
+
+ JSC::UString ustring = (*it).ustring();
+ names << QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
+ }
+
+ if (exec->hadException())
+ exec->clearException();
+
+ return names;
+}
+
+/*!
+ \enum QWebElement::ResolveRule
+ \since 4.6
+
+ This enum describes how QWebElement's styleProperty resolves the given
+ property name.
+
+ \value IgnoreCascadingStyles Return the property value as it is defined
+ in the element, without respecting style inheritance and other CSS rules.
+ \value RespectCascadingStyles The property's value is determined using
+ the inheritance and importance rules defined in the document's stylesheet.
+*/
+
+/*!
+ \enum QWebElement::StylePriority
+ \since 4.6
+
+ This enum describes the priority newly set CSS properties should have when
+ set using QWebElement::setStyleProperty().
+
+ \value NormalStylePriority Define the property without important
+ priority even if "!important" is explicitly set in \a value.
+ \value DeclaredStylePriority Define the property respecting the
+ priority specified in \a value.
+ \value ImportantStylePriority Define the property to have
+ an important priority, this is equal to appending "!important" to the value.
+*/
+
+/*!
+ Returns the value of the style named \a name or an empty string if such one
+ does not exist.
+
+ If \a rule is IgnoreCascadingStyles, the value defined inside the element
+ (inline in CSS terminology) is returned.
+
+ if \a rule is RespectCascadingStyles, the actual style applied to the
+ element is returned.
+
+ In CSS, the cascading part has to do with which CSS rule has priority and
+ is thus applied. Generally speaking, the last defined rule has priority,
+ thus an inline style rule has priority over an embedded block style rule,
+ which in return has priority over an external style rule.
+
+ If the !important declaration is set on one of those, the declaration gets
+ highest priority, unless other declarations also use the !important
+ declaration, in which the last !important declaration takes predecence.
+*/
+QString QWebElement::styleProperty(const QString &name, ResolveRule rule) const
+{
+ if (!m_element || !m_element->isStyledElement())
+ return QString();
+
+ int propID = cssPropertyID(name);
+
+ if (!propID)
+ return QString();
+
+ CSSStyleDeclaration* style = static_cast<StyledElement*>(m_element)->style();
+
+ if (rule == IgnoreCascadingStyles)
+ return style->getPropertyValue(propID);
+
+ if (rule == RespectCascadingStyles) {
+ if (style->getPropertyPriority(propID))
+ return style->getPropertyValue(propID);
+
+ // We are going to resolve the style property by walking through the
+ // list of non-inline matched CSS rules for the element, looking for
+ // the highest priority definition.
+
+ // Get an array of matched CSS rules for the given element sorted
+ // by importance and inheritance order. This include external CSS
+ // declarations, as well as embedded and inline style declarations.
+
+ DOMWindow* domWindow = m_element->document()->frame()->domWindow();
+ if (RefPtr<CSSRuleList> rules = domWindow->getMatchedCSSRules(m_element, "")) {
+ for (int i = rules->length(); i > 0; --i) {
+ CSSStyleRule* rule = static_cast<CSSStyleRule*>(rules->item(i - 1));
+
+ if (rule->style()->getPropertyPriority(propID))
+ return rule->style()->getPropertyValue(propID);
+
+ if (style->getPropertyValue(propID).isEmpty())
+ style = rule->style();
+ }
+ }
+
+ return style->getPropertyValue(propID);
+ }
+
+ return QString();
+}
+
+/*!
+ Sets the value of the style named \a name to \a value.
+
+ Setting a value, doesn't necessarily mean that it will become the applied
+ value, due to the fact that the style property's value might have been set
+ earlier with priority in external or embedded style declarations.
+
+ In order to ensure that the value will be applied, ImportantStylePriority
+ should be used as \a priority.
+
+ Following the CSS syntax for property values, this is equal to appending
+ "!important" to the value.
+
+ This syntax is supported when using DeclaredStylePriority as \a priority.
+
+ Using NormalStylePriority as \a priority, the property will have normal
+ priority, and any "!important" declaration will be ignored. On the other
+ hand, using ImportantStylePriority sets the important priority even when
+ not explicit passed in \a value.
+ By using DeclaredStylePriority as \a priority the property will respect the
+ priority specified in \a value.
+*/
+void QWebElement::setStyleProperty(const QString &name, const QString &value, StylePriority priority)
+{
+ if (!m_element || !m_element->isStyledElement())
+ return;
+
+ int propID = cssPropertyID(name);
+ CSSStyleDeclaration* style = static_cast<StyledElement*>(m_element)->style();
+ if (!propID || !style)
+ return;
+
+ ExceptionCode exception = 0;
+
+ const QRegExp hasImportantTest(QLatin1String("!\\s*important"));
+ int index = value.indexOf(hasImportantTest);
+
+ QString newValue = (index != -1) ? value.left(index - 1) : value;
+
+ switch (priority) {
+ case NormalStylePriority:
+ style->setProperty(name, newValue, "", exception);
+ break;
+ case DeclaredStylePriority:
+ style->setProperty(name, newValue, (index != -1) ? "important" : "", exception);
+ break;
+ case ImportantStylePriority:
+ style->setProperty(name, newValue, "important", exception);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ Returns the computed value for style named \a name or an empty string if the style has no such name.
+*/
+QString QWebElement::computedStyleProperty(const QString &name) const
+{
+ if (!m_element || !m_element->isStyledElement())
+ return QString();
+
+ int propID = cssPropertyID(name);
+
+ RefPtr<CSSComputedStyleDeclaration> style = computedStyle(m_element);
+ if (!propID || !style)
+ return QString();
+
+ return style->getPropertyValue(propID);
+}
+
+/*!
+ Returns the list of classes of this element.
+*/
+QStringList QWebElement::classes() const
+{
+ if (!hasAttribute("class"))
+ return QStringList();
+
+ QStringList classes = attribute("class").simplified().split(' ', QString::SkipEmptyParts);
+#if QT_VERSION >= 0x040500
+ classes.removeDuplicates();
+#else
+ int n = classes.size();
+ int j = 0;
+ QSet<QString> seen;
+ seen.reserve(n);
+ for (int i = 0; i < n; ++i) {
+ const QString& s = classes.at(i);
+ if (seen.contains(s))
+ continue;
+ seen.insert(s);
+ if (j != i)
+ classes[j] = s;
+ ++j;
+ }
+ if (n != j)
+ classes.erase(classes.begin() + j, classes.end());
+#endif
+ return classes;
+}
+
+/*!
+ Returns true if this element has a class called \a name; otherwise returns false.
+*/
+bool QWebElement::hasClass(const QString &name) const
+{
+ QStringList list = classes();
+ return list.contains(name);
+}
+
+/*!
+ Adds the specified class \a name to the element.
+*/
+void QWebElement::addClass(const QString &name)
+{
+ QStringList list = classes();
+ if (!list.contains(name)) {
+ list.append(name);
+ QString value = list.join(" ");
+ setAttribute("class", value);
+ }
+}
+
+/*!
+ Removes the specified class \a name from the element.
+*/
+void QWebElement::removeClass(const QString &name)
+{
+ QStringList list = classes();
+ if (list.contains(name)) {
+ list.removeAll(name);
+ QString value = list.join(" ");
+ setAttribute("class", value);
+ }
+}
+
+/*!
+ Adds the specified class \a name if it is not present,
+ removes it if it is already present.
+*/
+void QWebElement::toggleClass(const QString &name)
+{
+ QStringList list = classes();
+ if (list.contains(name))
+ list.removeAll(name);
+ else
+ list.append(name);
+
+ QString value = list.join(" ");
+ setAttribute("class", value);
+}
+
+/*!
+ Appends \a element as the element's last child.
+
+ If \a element is the child of another element, it is re-parented
+ to this element. If \a element is a child of this element, then
+ its position in the list of children is changed.
+
+ Calling this function on a null element does nothing.
+
+ \sa prependInside(), prependOutside(), appendOutside()
+*/
+void QWebElement::appendInside(const QWebElement &element)
+{
+ if (!m_element || element.isNull())
+ return;
+
+ ExceptionCode exception = 0;
+ m_element->appendChild(element.m_element, exception);
+}
+
+/*!
+ Appends the result of parsing \a markup as the element's last child.
+
+ Calling this function on a null element does nothing.
+
+ \sa prependInside(), prependOutside(), appendOutside()
+*/
+void QWebElement::appendInside(const QString &markup)
+{
+ if (!m_element)
+ return;
+
+ if (!m_element->isHTMLElement())
+ return;
+
+ HTMLElement* htmlElement = static_cast<HTMLElement*>(m_element);
+ RefPtr<DocumentFragment> fragment = htmlElement->createContextualFragment(markup);
+
+ ExceptionCode exception = 0;
+ m_element->appendChild(fragment, exception);
+}
+
+/*!
+ Prepends \a element as the element's first child.
+
+ If \a element is the child of another element, it is re-parented
+ to this element. If \a element is a child of this element, then
+ its position in the list of children is changed.
+
+ Calling this function on a null element does nothing.
+
+ \sa appendInside(), prependOutside(), appendOutside()
+*/
+void QWebElement::prependInside(const QWebElement &element)
+{
+ if (!m_element || element.isNull())
+ return;
+
+ ExceptionCode exception = 0;
+
+ if (m_element->hasChildNodes())
+ m_element->insertBefore(element.m_element, m_element->firstChild(), exception);
+ else
+ m_element->appendChild(element.m_element, exception);
+}
+
+/*!
+ Prepends the result of parsing \a markup as the element's first child.
+
+ Calling this function on a null element does nothing.
+
+ \sa appendInside(), prependOutside(), appendOutside()
+*/
+void QWebElement::prependInside(const QString &markup)
+{
+ if (!m_element)
+ return;
+
+ if (!m_element->isHTMLElement())
+ return;
+
+ HTMLElement* htmlElement = static_cast<HTMLElement*>(m_element);
+ RefPtr<DocumentFragment> fragment = htmlElement->createContextualFragment(markup);
+
+ ExceptionCode exception = 0;
+
+ if (m_element->hasChildNodes())
+ m_element->insertBefore(fragment, m_element->firstChild(), exception);
+ else
+ m_element->appendChild(fragment, exception);
+}
+
+
+/*!
+ Inserts \a element before this element.
+
+ If \a element is the child of another element, it is re-parented
+ to the parent of this element.
+
+ Calling this function on a null element does nothing.
+
+ \sa appendInside(), prependInside(), appendOutside()
+*/
+void QWebElement::prependOutside(const QWebElement &element)
+{
+ if (!m_element || element.isNull())
+ return;
+
+ if (!m_element->parent())
+ return;
+
+ ExceptionCode exception = 0;
+ m_element->parent()->insertBefore(element.m_element, m_element, exception);
+}
+
+/*!
+ Inserts the result of parsing \a markup before this element.
+
+ Calling this function on a null element does nothing.
+
+ \sa appendInside(), prependInside(), appendOutside()
+*/
+void QWebElement::prependOutside(const QString &markup)
+{
+ if (!m_element)
+ return;
+
+ if (!m_element->parent())
+ return;
+
+ if (!m_element->isHTMLElement())
+ return;
+
+ HTMLElement* htmlElement = static_cast<HTMLElement*>(m_element);
+ RefPtr<DocumentFragment> fragment = htmlElement->createContextualFragment(markup);
+
+ ExceptionCode exception = 0;
+ m_element->parent()->insertBefore(fragment, m_element, exception);
+}
+
+/*!
+ Inserts \a element after this element.
+
+ If \a element is the child of another element, it is re-parented
+ to the parent of this element.
+
+ Calling this function on a null element does nothing.
+
+ \sa appendInside(), prependInside(), prependOutside()
+*/
+void QWebElement::appendOutside(const QWebElement &element)
+{
+ if (!m_element || element.isNull())
+ return;
+
+ if (!m_element->parent())
+ return;
+
+ ExceptionCode exception = 0;
+ if (!m_element->nextSibling())
+ m_element->parent()->appendChild(element.m_element, exception);
+ else
+ m_element->parent()->insertBefore(element.m_element, m_element->nextSibling(), exception);
+}
+
+/*!
+ Inserts the result of parsing \a markup after this element.
+
+ Calling this function on a null element does nothing.
+
+ \sa appendInside(), prependInside(), prependOutside()
+*/
+void QWebElement::appendOutside(const QString &markup)
+{
+ if (!m_element)
+ return;
+
+ if (!m_element->parent())
+ return;
+
+ if (!m_element->isHTMLElement())
+ return;
+
+ HTMLElement* htmlElement = static_cast<HTMLElement*>(m_element);
+ RefPtr<DocumentFragment> fragment = htmlElement->createContextualFragment(markup);
+
+ ExceptionCode exception = 0;
+ if (!m_element->nextSibling())
+ m_element->parent()->appendChild(fragment, exception);
+ else
+ m_element->parent()->insertBefore(fragment, m_element->nextSibling(), exception);
+}
+
+/*!
+ Returns a clone of this element.
+
+ The clone may be inserted at any point in the document.
+
+ \sa appendInside(), prependInside(), prependOutside(), appendOutside()
+*/
+QWebElement QWebElement::clone() const
+{
+ if (!m_element)
+ return QWebElement();
+
+ return QWebElement(m_element->cloneElementWithChildren().get());
+}
+
+/*!
+ Removes this element from the document and returns a reference
+ to this.
+
+ The element is still valid after removal, and can be inserted into
+ other parts of the document.
+
+ \sa removeChildren(), removeFromDocument()
+*/
+QWebElement &QWebElement::takeFromDocument()
+{
+ if (!m_element)
+ return *this;
+
+ ExceptionCode exception = 0;
+ m_element->remove(exception);
+
+ return *this;
+}
+
+/*!
+ Removes this element from the document and makes this
+ a null element.
+
+ \sa removeChildren(), takeFromDocument()
+*/
+void QWebElement::removeFromDocument()
+{
+ if (!m_element)
+ return;
+
+ ExceptionCode exception = 0;
+ m_element->remove(exception);
+ m_element->deref();
+ m_element = 0;
+}
+
+/*!
+ Removes all children from this element.
+
+ \sa removeFromDocument(), takeFromDocument()
+*/
+void QWebElement::removeChildren()
+{
+ if (!m_element)
+ return;
+
+ m_element->removeAllChildren();
+}
+
+static RefPtr<Node> findInsertionPoint(PassRefPtr<Node> root)
+{
+ RefPtr<Node> node = root;
+
+ // Go as far down the tree as possible.
+ while (node->hasChildNodes() && node->firstChild()->isElementNode())
+ node = node->firstChild();
+
+ // TODO: Implement SVG support
+ if (node->isHTMLElement()) {
+ HTMLElement* element = static_cast<HTMLElement*>(node.get());
+
+ // The insert point could be a non-enclosable tag and it can thus
+ // never have children, so go one up. Get the parent element, and not
+ // note as a root note will always exist.
+ if (element->endTagRequirement() == TagStatusForbidden)
+ node = node->parentElement();
+ }
+
+ return node;
+}
+
+/*!
+ Enclose the contents of this element in \a element as the child
+ of the deepest descendant element within the structure of the
+ first element provided.
+
+ \sa encloseWith()
+*/
+void QWebElement::encloseContentsWith(const QWebElement &element)
+{
+ if (!m_element || element.isNull())
+ return;
+
+ RefPtr<Node> insertionPoint = findInsertionPoint(element.m_element);
+
+ if (!insertionPoint)
+ return;
+
+ ExceptionCode exception = 0;
+
+ // reparent children
+ for (RefPtr<Node> child = m_element->firstChild(); child;) {
+ RefPtr<Node> next = child->nextSibling();
+ insertionPoint->appendChild(child, exception);
+ child = next;
+ }
+
+ if (m_element->hasChildNodes())
+ m_element->insertBefore(element.m_element, m_element->firstChild(), exception);
+ else
+ m_element->appendChild(element.m_element, exception);
+}
+
+/*!
+ Enclose the contents of this element in the result of parsing
+ \a markup as the child of the deepest descendant element within
+ the structure of the first element provided.
+
+ \sa encloseWith()
+*/
+void QWebElement::encloseContentsWith(const QString &markup)
+{
+ if (!m_element)
+ return;
+
+ if (!m_element->parent())
+ return;
+
+ if (!m_element->isHTMLElement())
+ return;
+
+ HTMLElement* htmlElement = static_cast<HTMLElement*>(m_element);
+ RefPtr<DocumentFragment> fragment = htmlElement->createContextualFragment(markup);
+
+ if (!fragment || !fragment->firstChild())
+ return;
+
+ RefPtr<Node> insertionPoint = findInsertionPoint(fragment->firstChild());
+
+ if (!insertionPoint)
+ return;
+
+ ExceptionCode exception = 0;
+
+ // reparent children
+ for (RefPtr<Node> child = m_element->firstChild(); child;) {
+ RefPtr<Node> next = child->nextSibling();
+ insertionPoint->appendChild(child, exception);
+ child = next;
+ }
+
+ if (m_element->hasChildNodes())
+ m_element->insertBefore(fragment, m_element->firstChild(), exception);
+ else
+ m_element->appendChild(fragment, exception);
+}
+
+/*!
+ Enclose this element in \a element as the child of the deepest
+ descendant element within the structure of the first element
+ provided.
+
+ \sa replace()
+*/
+void QWebElement::encloseWith(const QWebElement &element)
+{
+ if (!m_element || element.isNull())
+ return;
+
+ RefPtr<Node> insertionPoint = findInsertionPoint(element.m_element);
+
+ if (!insertionPoint)
+ return;
+
+ // Keep reference to these two nodes before pulling out this element and
+ // wrapping it in the fragment. The reason for doing it in this order is
+ // that once the fragment has been added to the document it is empty, so
+ // we no longer have access to the nodes it contained.
+ Node* parentNode = m_element->parent();
+ Node* siblingNode = m_element->nextSibling();
+
+ ExceptionCode exception = 0;
+ insertionPoint->appendChild(m_element, exception);
+
+ if (!siblingNode)
+ parentNode->appendChild(element.m_element, exception);
+ else
+ parentNode->insertBefore(element.m_element, siblingNode, exception);
+}
+
+/*!
+ Enclose this element in the result of parsing \a markup,
+ as the last child.
+
+ \sa replace()
+*/
+void QWebElement::encloseWith(const QString &markup)
+{
+ if (!m_element)
+ return;
+
+ if (!m_element->parent())
+ return;
+
+ if (!m_element->isHTMLElement())
+ return;
+
+ HTMLElement* htmlElement = static_cast<HTMLElement*>(m_element);
+ RefPtr<DocumentFragment> fragment = htmlElement->createContextualFragment(markup);
+
+ if (!fragment || !fragment->firstChild())
+ return;
+
+ RefPtr<Node> insertionPoint = findInsertionPoint(fragment->firstChild());
+
+ if (!insertionPoint)
+ return;
+
+ // Keep reference to these two nodes before pulling out this element and
+ // wrapping it in the fragment. The reason for doing it in this order is
+ // that once the fragment has been added to the document it is empty, so
+ // we no longer have access to the nodes it contained.
+ Node* parentNode = m_element->parent();
+ Node* siblingNode = m_element->nextSibling();
+
+ ExceptionCode exception = 0;
+ insertionPoint->appendChild(m_element, exception);
+
+ if (!siblingNode)
+ parentNode->appendChild(fragment, exception);
+ else
+ parentNode->insertBefore(fragment, siblingNode, exception);
+}
+
+/*!
+ Replaces this element with \a element.
+
+ It is not possible to replace the <html>, <head>, or <body>
+ elements using this method.
+
+ \sa encloseWith()
+*/
+void QWebElement::replace(const QWebElement &element)
+{
+ if (!m_element || element.isNull())
+ return;
+
+ appendOutside(element);
+ takeFromDocument();
+}
+
+/*!
+ Replaces this element with the result of parsing \a markup.
+
+ It is not possible to replace the <html>, <head>, or <body>
+ elements using this method.
+
+ \sa encloseWith()
+*/
+void QWebElement::replace(const QString &markup)
+{
+ if (!m_element)
+ return;
+
+ appendOutside(markup);
+ takeFromDocument();
+}
+
+/*!
+ \fn inline bool QWebElement::operator==(const QWebElement& o) const;
+
+ Returns true if this element points to the same underlying DOM object than \a o; otherwise returns false.
+*/
+
+/*!
+ \fn inline bool QWebElement::operator!=(const QWebElement& o) const;
+
+ Returns true if this element points to a different underlying DOM object than \a o; otherwise returns false.
+*/