diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2009-06-15 09:06:43 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-06-15 09:31:31 (GMT) |
commit | c411f16870f112c3407c28c22b617f613a82cff4 (patch) | |
tree | 29a1bcd590c8b31af2aab445bfe8a978dc5bf582 /src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp | |
parent | 3d77b56b32a0c53ec0bbfaa07236fedb900ff336 (diff) | |
download | Qt-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.cpp | 1603 |
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. +*/ |