1 /*
2     Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
3 
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Library General Public
6     License as published by the Free Software Foundation; either
7     version 2 of the License, or (at your option) any later version.
8 
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     Library General Public License for more details.
13 
14     You should have received a copy of the GNU Library General Public License
15     along with this library; see the file COPYING.LIB.  If not, write to
16     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17     Boston, MA 02110-1301, USA.
18 */
19 
20 #include "config.h"
21 #include "qwebelement.h"
22 
23 #include "CSSComputedStyleDeclaration.h"
24 #include "CSSMutableStyleDeclaration.h"
25 #include "CSSParser.h"
26 #include "CSSRule.h"
27 #include "CSSRuleList.h"
28 #include "CSSStyleRule.h"
29 #include "CSSStyleSelector.h"
30 #include "Document.h"
31 #include "DocumentFragment.h"
32 #include "FrameView.h"
33 #include "GraphicsContext.h"
34 #include "HTMLElement.h"
35 #if USE(JSC)
36 #include "JSGlobalObject.h"
37 #include "JSHTMLElement.h"
38 #include "JSObject.h"
39 #include "PropertyNameArray.h"
40 #include <parser/SourceCode.h>
41 #include "qt_runtime.h"
42 #elif USE(V8)
43 #include "V8DOMWindow.h"
44 #include "V8Binding.h"
45 #include "NotImplemented.h"
46 #endif
47 #include "NodeList.h"
48 #include "RenderImage.h"
49 #include "StaticNodeList.h"
50 #include "qwebframe.h"
51 #include "qwebframe_p.h"
52 #if USE(JSC)
53 #include "runtime_root.h"
54 #endif
55 #include <wtf/Vector.h>
56 #include <wtf/text/CString.h>
57 
58 #include <QPainter>
59 
60 #if USE(V8)
61 using namespace V8::Bindings;
62 #endif
63 
64 using namespace WebCore;
65 
66 class QWebElementPrivate {
67 public:
68 };
69 
70 /*!
71     \class QWebElement
72     \since 4.6
73     \brief The QWebElement class provides convenient access to DOM elements in
74     a QWebFrame.
75     \inmodule QtWebKit
76 
77     A QWebElement object allows easy access to the document model, represented
78     by a tree-like structure of DOM elements. The root of the tree is called
79     the document element and can be accessed using
80     QWebFrame::documentElement().
81 
82     Specific elements can be accessed using findAll() and findFirst(). These
83     elements are identified using CSS selectors. The code snippet below
84     demonstrates the use of findAll().
85 
86     \snippet webkitsnippets/webelement/main.cpp FindAll
87 
88     The first list contains all \c span elements in the document. The second
89     list contains \c span elements that are children of \c p, classified with
90     \c intro.
91 
92     Using findFirst() is more efficient than calling findAll(), and extracting
93     the first element only in the list returned.
94 
95     Alternatively you can traverse the document manually using firstChild() and
96     nextSibling():
97 
98     \snippet webkitsnippets/webelement/main.cpp Traversing with QWebElement
99 
100     Individual elements can be inspected or changed using methods such as attribute()
101     or setAttribute(). For examle, to capture the user's input in a text field for later
102     use (auto-completion), a browser could do something like this:
103 
104     \snippet webkitsnippets/webelement/main.cpp autocomplete1
105 
106     When the same page is later revisited, the browser can fill in the text field automatically
107     by modifying the value attribute of the input element:
108 
109     \snippet webkitsnippets/webelement/main.cpp autocomplete2
110 
111     Another use case is to emulate a click event on an element. The following
112     code snippet demonstrates how to call the JavaScript DOM method click() of
113     a submit button:
114 
115     \snippet webkitsnippets/webelement/main.cpp Calling a DOM element method
116 
117     The underlying content of QWebElement is explicitly shared. Creating a copy
118     of a QWebElement does not create a copy of the content. Instead, both
119     instances point to the same element.
120 
121     The contents of child elements can be converted to plain text with
122     toPlainText(); to XHTML using toInnerXml(). To include the element's tag in
123     the output, use toOuterXml().
124 
125     It is possible to replace the contents of child elements using
126     setPlainText() and setInnerXml(). To replace the element itself and its
127     contents, use setOuterXml().
128 
129     \section1 Examples
130 
131     The \l{DOM Traversal Example} shows one way to traverse documents in a running
132     example.
133 
134     The \l{Simple Selector Example} can be used to experiment with the searching
135     features of this class and provides sample code you can start working with.
136 */
137 
138 /*!
139     Constructs a null web element.
140 */
QWebElement()141 QWebElement::QWebElement()
142     : d(0)
143     , m_element(0)
144 {
145 }
146 
147 /*!
148     \internal
149 */
QWebElement(WebCore::Element * domElement)150 QWebElement::QWebElement(WebCore::Element* domElement)
151     : d(0)
152     , m_element(domElement)
153 {
154     if (m_element)
155         m_element->ref();
156 }
157 
158 /*!
159     \internal
160 */
QWebElement(WebCore::Node * node)161 QWebElement::QWebElement(WebCore::Node* node)
162     : d(0)
163     , m_element(0)
164 {
165     if (node && node->isHTMLElement()) {
166         m_element = static_cast<HTMLElement*>(node);
167         m_element->ref();
168     }
169 }
170 
171 /*!
172     Constructs a copy of \a other.
173 */
QWebElement(const QWebElement & other)174 QWebElement::QWebElement(const QWebElement &other)
175     : d(0)
176     , m_element(other.m_element)
177 {
178     if (m_element)
179         m_element->ref();
180 }
181 
182 /*!
183     Assigns \a other to this element and returns a reference to this element.
184 */
operator =(const QWebElement & other)185 QWebElement &QWebElement::operator=(const QWebElement &other)
186 {
187     // ### handle "d" assignment
188     if (this != &other) {
189         Element *otherElement = other.m_element;
190         if (otherElement)
191             otherElement->ref();
192         if (m_element)
193             m_element->deref();
194         m_element = otherElement;
195     }
196     return *this;
197 }
198 
199 /*!
200     Destroys the element. However, the underlying DOM element is not destroyed.
201 */
~QWebElement()202 QWebElement::~QWebElement()
203 {
204     delete d;
205     if (m_element)
206         m_element->deref();
207 }
208 
operator ==(const QWebElement & o) const209 bool QWebElement::operator==(const QWebElement& o) const
210 {
211     return m_element == o.m_element;
212 }
213 
operator !=(const QWebElement & o) const214 bool QWebElement::operator!=(const QWebElement& o) const
215 {
216     return m_element != o.m_element;
217 }
218 
219 /*!
220     Returns true if the element is a null element; otherwise returns false.
221 */
isNull() const222 bool QWebElement::isNull() const
223 {
224     return !m_element;
225 }
226 
227 /*!
228     Returns a new list of child elements matching the given CSS selector
229     \a selectorQuery. If there are no matching elements, an empty list is
230     returned.
231 
232     \l{Standard CSS2 selector} syntax is used for the query.
233 
234     \note This search is performed recursively.
235 
236     \sa findFirst()
237 */
findAll(const QString & selectorQuery) const238 QWebElementCollection QWebElement::findAll(const QString &selectorQuery) const
239 {
240     return QWebElementCollection(*this, selectorQuery);
241 }
242 
243 /*!
244     Returns the first child element that matches the given CSS selector
245     \a selectorQuery.
246 
247     \l{Standard CSS2 selector} syntax is used for the query.
248 
249     \note This search is performed recursively.
250 
251     \sa findAll()
252 */
findFirst(const QString & selectorQuery) const253 QWebElement QWebElement::findFirst(const QString &selectorQuery) const
254 {
255     if (!m_element)
256         return QWebElement();
257     ExceptionCode exception = 0; // ###
258     return QWebElement(m_element->querySelector(selectorQuery, exception).get());
259 }
260 
261 /*!
262     Replaces the existing content of this element with \a text.
263 
264     This is equivalent to setting the HTML innerText property.
265 
266     \sa toPlainText()
267 */
setPlainText(const QString & text)268 void QWebElement::setPlainText(const QString &text)
269 {
270     if (!m_element || !m_element->isHTMLElement())
271         return;
272     ExceptionCode exception = 0;
273     static_cast<HTMLElement*>(m_element)->setInnerText(text, exception);
274 }
275 
276 /*!
277     Returns the text between the start and the end tag of this
278     element.
279 
280     This is equivalent to reading the HTML innerText property.
281 
282     \sa setPlainText()
283 */
toPlainText() const284 QString QWebElement::toPlainText() const
285 {
286     if (!m_element || !m_element->isHTMLElement())
287         return QString();
288     return static_cast<HTMLElement*>(m_element)->innerText();
289 }
290 
291 /*!
292     Replaces the contents of this element as well as its own tag with
293     \a markup. The string may contain HTML or XML tags, which is parsed and
294     formatted before insertion into the document.
295 
296     \note This is currently only implemented for (X)HTML elements.
297 
298     \sa toOuterXml(), toInnerXml(), setInnerXml()
299 */
setOuterXml(const QString & markup)300 void QWebElement::setOuterXml(const QString &markup)
301 {
302     if (!m_element || !m_element->isHTMLElement())
303         return;
304 
305     ExceptionCode exception = 0;
306 
307     static_cast<HTMLElement*>(m_element)->setOuterHTML(markup, exception);
308 }
309 
310 /*!
311     Returns this element converted to XML, including the start and the end
312     tags as well as its attributes.
313 
314     \note This is currently implemented for (X)HTML elements only.
315 
316     \note The format of the markup returned will obey the namespace of the
317     document containing the element. This means the return value will obey XML
318     formatting rules, such as self-closing tags, only if the document is
319     'text/xhtml+xml'.
320 
321     \sa setOuterXml(), setInnerXml(), toInnerXml()
322 */
toOuterXml() const323 QString QWebElement::toOuterXml() const
324 {
325     if (!m_element || !m_element->isHTMLElement())
326         return QString();
327 
328     return static_cast<HTMLElement*>(m_element)->outerHTML();
329 }
330 
331 /*!
332     Replaces the contents of this element with \a markup. The string may
333     contain HTML or XML tags, which is parsed and formatted before insertion
334     into the document.
335 
336     \note This is currently implemented for (X)HTML elements only.
337 
338     \sa toInnerXml(), toOuterXml(), setOuterXml()
339 */
setInnerXml(const QString & markup)340 void QWebElement::setInnerXml(const QString &markup)
341 {
342     if (!m_element || !m_element->isHTMLElement())
343         return;
344 
345     ExceptionCode exception = 0;
346 
347     static_cast<HTMLElement*>(m_element)->setInnerHTML(markup, exception);
348 }
349 
350 /*!
351     Returns the XML content between the element's start and end tags.
352 
353     \note This is currently implemented for (X)HTML elements only.
354 
355     \note The format of the markup returned will obey the namespace of the
356     document containing the element. This means the return value will obey XML
357     formatting rules, such as self-closing tags, only if the document is
358     'text/xhtml+xml'.
359 
360     \sa setInnerXml(), setOuterXml(), toOuterXml()
361 */
toInnerXml() const362 QString QWebElement::toInnerXml() const
363 {
364     if (!m_element || !m_element->isHTMLElement())
365         return QString();
366 
367     return static_cast<HTMLElement*>(m_element)->innerHTML();
368 }
369 
370 /*!
371     Adds an attribute with the given \a name and \a value. If an attribute with
372     the same name exists, its value is replaced by \a value.
373 
374     \sa attribute(), attributeNS(), setAttributeNS()
375 */
setAttribute(const QString & name,const QString & value)376 void QWebElement::setAttribute(const QString &name, const QString &value)
377 {
378     if (!m_element)
379         return;
380     ExceptionCode exception = 0;
381     m_element->setAttribute(name, value, exception);
382 }
383 
384 /*!
385     Adds an attribute with the given \a name in \a namespaceUri with \a value.
386     If an attribute with the same name exists, its value is replaced by
387     \a value.
388 
389     \sa attributeNS(), attribute(), setAttribute()
390 */
setAttributeNS(const QString & namespaceUri,const QString & name,const QString & value)391 void QWebElement::setAttributeNS(const QString &namespaceUri, const QString &name, const QString &value)
392 {
393     if (!m_element)
394         return;
395     WebCore::ExceptionCode exception = 0;
396     m_element->setAttributeNS(namespaceUri, name, value, exception);
397 }
398 
399 /*!
400     Returns the attribute with the given \a name. If the attribute does not
401     exist, \a defaultValue is returned.
402 
403     \sa setAttribute(), setAttributeNS(), attributeNS()
404 */
attribute(const QString & name,const QString & defaultValue) const405 QString QWebElement::attribute(const QString &name, const QString &defaultValue) const
406 {
407     if (!m_element)
408         return QString();
409     if (m_element->hasAttribute(name))
410         return m_element->getAttribute(name);
411     else
412         return defaultValue;
413 }
414 
415 /*!
416     Returns the attribute with the given \a name in \a namespaceUri. If the
417     attribute does not exist, \a defaultValue is returned.
418 
419     \sa setAttributeNS(), setAttribute(), attribute()
420 */
attributeNS(const QString & namespaceUri,const QString & name,const QString & defaultValue) const421 QString QWebElement::attributeNS(const QString &namespaceUri, const QString &name, const QString &defaultValue) const
422 {
423     if (!m_element)
424         return QString();
425     if (m_element->hasAttributeNS(namespaceUri, name))
426         return m_element->getAttributeNS(namespaceUri, name);
427     else
428         return defaultValue;
429 }
430 
431 /*!
432     Returns true if this element has an attribute with the given \a name;
433     otherwise returns false.
434 
435     \sa attribute(), setAttribute()
436 */
hasAttribute(const QString & name) const437 bool QWebElement::hasAttribute(const QString &name) const
438 {
439     if (!m_element)
440         return false;
441     return m_element->hasAttribute(name);
442 }
443 
444 /*!
445     Returns true if this element has an attribute with the given \a name, in
446     \a namespaceUri; otherwise returns false.
447 
448     \sa attributeNS(), setAttributeNS()
449 */
hasAttributeNS(const QString & namespaceUri,const QString & name) const450 bool QWebElement::hasAttributeNS(const QString &namespaceUri, const QString &name) const
451 {
452     if (!m_element)
453         return false;
454     return m_element->hasAttributeNS(namespaceUri, name);
455 }
456 
457 /*!
458     Removes the attribute with the given \a name from this element.
459 
460     \sa attribute(), setAttribute(), hasAttribute()
461 */
removeAttribute(const QString & name)462 void QWebElement::removeAttribute(const QString &name)
463 {
464     if (!m_element)
465         return;
466     ExceptionCode exception = 0;
467     m_element->removeAttribute(name, exception);
468 }
469 
470 /*!
471     Removes the attribute with the given \a name, in \a namespaceUri, from this
472     element.
473 
474     \sa attributeNS(), setAttributeNS(), hasAttributeNS()
475 */
removeAttributeNS(const QString & namespaceUri,const QString & name)476 void QWebElement::removeAttributeNS(const QString &namespaceUri, const QString &name)
477 {
478     if (!m_element)
479         return;
480     WebCore::ExceptionCode exception = 0;
481     m_element->removeAttributeNS(namespaceUri, name, exception);
482 }
483 
484 /*!
485     Returns true if the element has any attributes defined; otherwise returns
486     false;
487 
488     \sa attribute(), setAttribute()
489 */
hasAttributes() const490 bool QWebElement::hasAttributes() const
491 {
492     if (!m_element)
493         return false;
494     return m_element->hasAttributes();
495 }
496 
497 /*!
498     Return the list of attributes for the namespace given as \a namespaceUri.
499 
500     \sa attribute(), setAttribute()
501 */
attributeNames(const QString & namespaceUri) const502 QStringList QWebElement::attributeNames(const QString& namespaceUri) const
503 {
504     if (!m_element)
505         return QStringList();
506 
507     QStringList attributeNameList;
508     const NamedNodeMap* const attrs = m_element->attributes(/* read only = */ true);
509     if (attrs) {
510         const String namespaceUriString(namespaceUri); // convert QString -> String once
511         const unsigned attrsCount = attrs->length();
512         for (unsigned i = 0; i < attrsCount; ++i) {
513             const Attribute* const attribute = attrs->attributeItem(i);
514             if (namespaceUriString == attribute->namespaceURI())
515                 attributeNameList.append(attribute->localName());
516         }
517     }
518     return attributeNameList;
519 }
520 
521 /*!
522     Returns true if the element has keyboard input focus; otherwise, returns false
523 
524     \sa setFocus()
525 */
hasFocus() const526 bool QWebElement::hasFocus() const
527 {
528     if (!m_element)
529         return false;
530     if (m_element->document())
531         return m_element == m_element->document()->focusedNode();
532     return false;
533 }
534 
535 /*!
536     Gives keyboard input focus to this element
537 
538     \sa hasFocus()
539 */
setFocus()540 void QWebElement::setFocus()
541 {
542     if (!m_element)
543         return;
544     if (m_element->document() && m_element->isFocusable())
545         m_element->document()->setFocusedNode(m_element);
546 }
547 
548 /*!
549     Returns the geometry of this element, relative to its containing frame.
550 
551     \sa tagName()
552 */
geometry() const553 QRect QWebElement::geometry() const
554 {
555     if (!m_element)
556         return QRect();
557     return m_element->getRect();
558 }
559 
560 /*!
561     Returns the tag name of this element.
562 
563     \sa geometry()
564 */
tagName() const565 QString QWebElement::tagName() const
566 {
567     if (!m_element)
568         return QString();
569     return m_element->tagName();
570 }
571 
572 /*!
573     Returns the namespace prefix of the element. If the element has no\
574     namespace prefix, empty string is returned.
575 */
prefix() const576 QString QWebElement::prefix() const
577 {
578     if (!m_element)
579         return QString();
580     return m_element->prefix();
581 }
582 
583 /*!
584     Returns the local name of the element. If the element does not use
585     namespaces, an empty string is returned.
586 */
localName() const587 QString QWebElement::localName() const
588 {
589     if (!m_element)
590         return QString();
591     return m_element->localName();
592 }
593 
594 /*!
595     Returns the namespace URI of this element. If the element has no namespace
596     URI, an empty string is returned.
597 */
namespaceUri() const598 QString QWebElement::namespaceUri() const
599 {
600     if (!m_element)
601         return QString();
602     return m_element->namespaceURI();
603 }
604 
605 /*!
606     Returns the parent element of this elemen. If this element is the root
607     document element, a null element is returned.
608 */
parent() const609 QWebElement QWebElement::parent() const
610 {
611     if (m_element)
612         return QWebElement(m_element->parentElement());
613     return QWebElement();
614 }
615 
616 /*!
617     Returns the element's first child.
618 
619     \sa lastChild(), previousSibling(), nextSibling()
620 */
firstChild() const621 QWebElement QWebElement::firstChild() const
622 {
623     if (!m_element)
624         return QWebElement();
625     for (Node* child = m_element->firstChild(); child; child = child->nextSibling()) {
626         if (!child->isElementNode())
627             continue;
628         Element* e = static_cast<Element*>(child);
629         return QWebElement(e);
630     }
631     return QWebElement();
632 }
633 
634 /*!
635     Returns the element's last child.
636 
637     \sa firstChild(), previousSibling(), nextSibling()
638 */
lastChild() const639 QWebElement QWebElement::lastChild() const
640 {
641     if (!m_element)
642         return QWebElement();
643     for (Node* child = m_element->lastChild(); child; child = child->previousSibling()) {
644         if (!child->isElementNode())
645             continue;
646         Element* e = static_cast<Element*>(child);
647         return QWebElement(e);
648     }
649     return QWebElement();
650 }
651 
652 /*!
653     Returns the element's next sibling.
654 
655     \sa firstChild(), previousSibling(), lastChild()
656 */
nextSibling() const657 QWebElement QWebElement::nextSibling() const
658 {
659     if (!m_element)
660         return QWebElement();
661     for (Node* sib = m_element->nextSibling(); sib; sib = sib->nextSibling()) {
662         if (!sib->isElementNode())
663             continue;
664         Element* e = static_cast<Element*>(sib);
665         return QWebElement(e);
666     }
667     return QWebElement();
668 }
669 
670 /*!
671     Returns the element's previous sibling.
672 
673     \sa firstChild(), nextSibling(), lastChild()
674 */
previousSibling() const675 QWebElement QWebElement::previousSibling() const
676 {
677     if (!m_element)
678         return QWebElement();
679     for (Node* sib = m_element->previousSibling(); sib; sib = sib->previousSibling()) {
680         if (!sib->isElementNode())
681             continue;
682         Element* e = static_cast<Element*>(sib);
683         return QWebElement(e);
684     }
685     return QWebElement();
686 }
687 
688 /*!
689     Returns the document which this element belongs to.
690 */
document() const691 QWebElement QWebElement::document() const
692 {
693     if (!m_element)
694         return QWebElement();
695     Document* document = m_element->document();
696     if (!document)
697         return QWebElement();
698     return QWebElement(document->documentElement());
699 }
700 
701 /*!
702     Returns the web frame which this element is a part of. If the element is a
703     null element, null is returned.
704 */
webFrame() const705 QWebFrame *QWebElement::webFrame() const
706 {
707     if (!m_element)
708         return 0;
709 
710     Document* document = m_element->document();
711     if (!document)
712         return 0;
713 
714     Frame* frame = document->frame();
715     if (!frame)
716         return 0;
717     return QWebFramePrivate::kit(frame);
718 }
719 
720 #if USE(JSC)
setupScriptContext(WebCore::Element * element,JSC::JSValue & thisValue,ScriptState * & state,ScriptController * & scriptController)721 static bool setupScriptContext(WebCore::Element* element, JSC::JSValue& thisValue, ScriptState*& state, ScriptController*& scriptController)
722 {
723     if (!element)
724         return false;
725 
726     Document* document = element->document();
727     if (!document)
728         return false;
729 
730     Frame* frame = document->frame();
731     if (!frame)
732         return false;
733 
734     scriptController = frame->script();
735     if (!scriptController)
736         return false;
737 
738     state = scriptController->globalObject(mainThreadNormalWorld())->globalExec();
739     if (!state)
740         return false;
741 
742     thisValue = toJS(state, deprecatedGlobalObjectForPrototype(state), element);
743     if (!thisValue)
744         return false;
745 
746     return true;
747 }
748 #elif USE(V8)
setupScriptContext(WebCore::Element * element,v8::Handle<v8::Value> & thisValue,ScriptState * & state,ScriptController * & scriptController)749 static bool setupScriptContext(WebCore::Element* element, v8::Handle<v8::Value>& thisValue, ScriptState*& state, ScriptController*& scriptController)
750 {
751     if (!element)
752         return false;
753 
754     Document* document = element->document();
755     if (!document)
756         return false;
757 
758     Frame* frame = document->frame();
759     if (!frame)
760         return false;
761 
762     state = mainWorldScriptState(frame);
763     // Get V8 wrapper for DOM element
764     thisValue = toV8(frame->domWindow());
765     return true;
766 }
767 #endif
768 
769 
770 /*!
771     Executes \a scriptSource with this element as \c this object.
772 */
evaluateJavaScript(const QString & scriptSource)773 QVariant QWebElement::evaluateJavaScript(const QString& scriptSource)
774 {
775     if (scriptSource.isEmpty())
776         return QVariant();
777 
778     ScriptState* state = 0;
779 #if USE(JSC)
780     JSC::JSValue thisValue;
781 #elif USE(V8)
782     v8::Handle<v8::Value> thisValue;
783 #endif
784     ScriptController* scriptController = 0;
785 
786     if (!setupScriptContext(m_element, thisValue, state, scriptController))
787         return QVariant();
788 #if USE(JSC)
789     JSC::ScopeChainNode* scopeChain = state->dynamicGlobalObject()->globalScopeChain();
790     JSC::UString script(reinterpret_cast_ptr<const UChar*>(scriptSource.data()), scriptSource.length());
791     JSC::Completion completion = JSC::evaluate(state, scopeChain, JSC::makeSource(script), thisValue);
792     if ((completion.complType() != JSC::ReturnValue) && (completion.complType() != JSC::Normal))
793         return QVariant();
794 
795     JSC::JSValue result = completion.value();
796     if (!result)
797         return QVariant();
798 
799     int distance = 0;
800     return JSC::Bindings::convertValueToQVariant(state, result, QMetaType::Void, &distance);
801 #elif USE(V8)
802     notImplemented();
803     return QVariant();
804 #endif
805 }
806 
807 /*!
808     \enum QWebElement::StyleResolveStrategy
809 
810     This enum describes how QWebElement's styleProperty resolves the given
811     property name.
812 
813     \value InlineStyle Return the property value as it is defined in
814            the element, without respecting style inheritance and other CSS
815            rules.
816     \value CascadedStyle The property's value is determined using the
817            inheritance and importance rules defined in the document's
818            stylesheet.
819     \value ComputedStyle The property's value is the absolute value
820            of the style property resolved from the environment.
821 */
822 
823 /*!
824     Returns the value of the style with the given \a name using the specified
825     \a strategy. If a style with \a name does not exist, an empty string is
826     returned.
827 
828     In CSS, the cascading part depends on which CSS rule has priority and is
829     thus applied. Generally, the last defined rule has priority. Thus, an
830     inline style rule has priority over an embedded block style rule, which
831     in return has priority over an external style rule.
832 
833     If the "!important" declaration is set on one of those, the declaration
834     receives highest priority, unless other declarations also use the
835     "!important" declaration. Then, the last "!important" declaration takes
836     predecence.
837 
838     \sa setStyleProperty()
839 */
840 
styleProperty(const QString & name,StyleResolveStrategy strategy) const841 QString QWebElement::styleProperty(const QString &name, StyleResolveStrategy strategy) const
842 {
843     if (!m_element || !m_element->isStyledElement())
844         return QString();
845 
846     int propID = cssPropertyID(name);
847 
848     if (!propID)
849         return QString();
850 
851     CSSStyleDeclaration* style = static_cast<StyledElement*>(m_element)->style();
852 
853     if (strategy == InlineStyle)
854         return style->getPropertyValue(propID);
855 
856     if (strategy == CascadedStyle) {
857         if (style->getPropertyPriority(propID))
858             return style->getPropertyValue(propID);
859 
860         // We are going to resolve the style property by walking through the
861         // list of non-inline matched CSS rules for the element, looking for
862         // the highest priority definition.
863 
864         // Get an array of matched CSS rules for the given element sorted
865         // by importance and inheritance order. This include external CSS
866         // declarations, as well as embedded and inline style declarations.
867 
868         Document* doc = m_element->document();
869         if (RefPtr<CSSRuleList> rules = doc->styleSelector()->styleRulesForElement(m_element, /*authorOnly*/ true)) {
870             for (int i = rules->length(); i > 0; --i) {
871                 CSSStyleRule* rule = static_cast<CSSStyleRule*>(rules->item(i - 1));
872 
873                 if (rule->style()->getPropertyPriority(propID))
874                     return rule->style()->getPropertyValue(propID);
875 
876                 if (style->getPropertyValue(propID).isEmpty())
877                     style = rule->style();
878             }
879         }
880 
881         return style->getPropertyValue(propID);
882     }
883 
884     if (strategy == ComputedStyle) {
885         if (!m_element || !m_element->isStyledElement())
886             return QString();
887 
888         int propID = cssPropertyID(name);
889 
890         RefPtr<CSSComputedStyleDeclaration> style = computedStyle(m_element, true);
891         if (!propID || !style)
892             return QString();
893 
894         return style->getPropertyValue(propID);
895     }
896 
897     return QString();
898 }
899 
900 /*!
901     Sets the value of the inline style with the given \a name to \a value.
902 
903     Setting a value, does not necessarily mean that it will become the applied
904     value, due to the fact that the style property's value might have been set
905     earlier with a higher priority in external or embedded style declarations.
906 
907     In order to ensure that the value will be applied, you may have to append
908     "!important" to the value.
909 */
setStyleProperty(const QString & name,const QString & value)910 void QWebElement::setStyleProperty(const QString &name, const QString &value)
911 {
912     if (!m_element || !m_element->isStyledElement())
913         return;
914 
915     int propID = cssPropertyID(name);
916     CSSStyleDeclaration* style = static_cast<StyledElement*>(m_element)->style();
917     if (!propID || !style)
918         return;
919 
920     ExceptionCode exception = 0;
921     style->setProperty(name, value, exception);
922 }
923 
924 /*!
925     Returns the list of classes of this element.
926 */
classes() const927 QStringList QWebElement::classes() const
928 {
929     if (!hasAttribute(QLatin1String("class")))
930         return QStringList();
931 
932     QStringList classes =  attribute(QLatin1String("class")).simplified().split(QLatin1Char(' '), QString::SkipEmptyParts);
933     classes.removeDuplicates();
934     return classes;
935 }
936 
937 /*!
938     Returns true if this element has a class with the given \a name; otherwise
939     returns false.
940 */
hasClass(const QString & name) const941 bool QWebElement::hasClass(const QString &name) const
942 {
943     QStringList list = classes();
944     return list.contains(name);
945 }
946 
947 /*!
948     Adds the specified class with the given \a name to the element.
949 */
addClass(const QString & name)950 void QWebElement::addClass(const QString &name)
951 {
952     QStringList list = classes();
953     if (!list.contains(name)) {
954         list.append(name);
955         QString value = list.join(QLatin1String(" "));
956         setAttribute(QLatin1String("class"), value);
957     }
958 }
959 
960 /*!
961     Removes the specified class with the given \a name from the element.
962 */
removeClass(const QString & name)963 void QWebElement::removeClass(const QString &name)
964 {
965     QStringList list = classes();
966     if (list.contains(name)) {
967         list.removeAll(name);
968         QString value = list.join(QLatin1String(" "));
969         setAttribute(QLatin1String("class"), value);
970     }
971 }
972 
973 /*!
974     Adds the specified class with the given \a name if it is not present. If
975     the class is already present, it will be removed.
976 */
toggleClass(const QString & name)977 void QWebElement::toggleClass(const QString &name)
978 {
979     QStringList list = classes();
980     if (list.contains(name))
981         list.removeAll(name);
982     else
983         list.append(name);
984 
985     QString value = list.join(QLatin1String(" "));
986     setAttribute(QLatin1String("class"), value);
987 }
988 
989 /*!
990     Appends the given \a element as the element's last child.
991 
992     If \a element is the child of another element, it is re-parented to this
993     element. If \a element is a child of this element, then its position in
994     the list of children is changed.
995 
996     Calling this function on a null element does nothing.
997 
998     \sa prependInside(), prependOutside(), appendOutside()
999 */
appendInside(const QWebElement & element)1000 void QWebElement::appendInside(const QWebElement &element)
1001 {
1002     if (!m_element || element.isNull())
1003         return;
1004 
1005     ExceptionCode exception = 0;
1006     m_element->appendChild(element.m_element, exception);
1007 }
1008 
1009 /*!
1010     Appends the result of parsing \a markup as the element's last child.
1011 
1012     Calling this function on a null element does nothing.
1013 
1014     \sa prependInside(), prependOutside(), appendOutside()
1015 */
appendInside(const QString & markup)1016 void QWebElement::appendInside(const QString &markup)
1017 {
1018     if (!m_element)
1019         return;
1020 
1021     if (!m_element->isHTMLElement())
1022         return;
1023 
1024     HTMLElement* htmlElement = static_cast<HTMLElement*>(m_element);
1025     RefPtr<DocumentFragment> fragment = htmlElement->Element::deprecatedCreateContextualFragment(markup);
1026 
1027     ExceptionCode exception = 0;
1028     m_element->appendChild(fragment, exception);
1029 }
1030 
1031 /*!
1032     Prepends \a element as the element's first child.
1033 
1034     If \a element is the child of another element, it is re-parented to this
1035     element. If \a element is a child of this element, then its position in
1036     the list of children is changed.
1037 
1038     Calling this function on a null element does nothing.
1039 
1040     \sa appendInside(), prependOutside(), appendOutside()
1041 */
prependInside(const QWebElement & element)1042 void QWebElement::prependInside(const QWebElement &element)
1043 {
1044     if (!m_element || element.isNull())
1045         return;
1046 
1047     ExceptionCode exception = 0;
1048 
1049     if (m_element->hasChildNodes())
1050         m_element->insertBefore(element.m_element, m_element->firstChild(), exception);
1051     else
1052         m_element->appendChild(element.m_element, exception);
1053 }
1054 
1055 /*!
1056     Prepends the result of parsing \a markup as the element's first child.
1057 
1058     Calling this function on a null element does nothing.
1059 
1060     \sa appendInside(), prependOutside(), appendOutside()
1061 */
prependInside(const QString & markup)1062 void QWebElement::prependInside(const QString &markup)
1063 {
1064     if (!m_element)
1065         return;
1066 
1067     if (!m_element->isHTMLElement())
1068         return;
1069 
1070     HTMLElement* htmlElement = static_cast<HTMLElement*>(m_element);
1071     RefPtr<DocumentFragment> fragment = htmlElement->deprecatedCreateContextualFragment(markup);
1072 
1073     ExceptionCode exception = 0;
1074 
1075     if (m_element->hasChildNodes())
1076         m_element->insertBefore(fragment, m_element->firstChild(), exception);
1077     else
1078         m_element->appendChild(fragment, exception);
1079 }
1080 
1081 
1082 /*!
1083     Inserts the given \a element before this element.
1084 
1085     If \a element is the child of another element, it is re-parented to the
1086     parent of this element.
1087 
1088     Calling this function on a null element does nothing.
1089 
1090     \sa appendInside(), prependInside(), appendOutside()
1091 */
prependOutside(const QWebElement & element)1092 void QWebElement::prependOutside(const QWebElement &element)
1093 {
1094     if (!m_element || element.isNull())
1095         return;
1096 
1097     if (!m_element->parentNode())
1098         return;
1099 
1100     ExceptionCode exception = 0;
1101     m_element->parentNode()->insertBefore(element.m_element, m_element, exception);
1102 }
1103 
1104 /*!
1105     Inserts the result of parsing \a markup before this element.
1106 
1107     Calling this function on a null element does nothing.
1108 
1109     \sa appendInside(), prependInside(), appendOutside()
1110 */
prependOutside(const QString & markup)1111 void QWebElement::prependOutside(const QString &markup)
1112 {
1113     if (!m_element)
1114         return;
1115 
1116     if (!m_element->parentNode())
1117         return;
1118 
1119     if (!m_element->isHTMLElement())
1120         return;
1121 
1122     HTMLElement* htmlElement = static_cast<HTMLElement*>(m_element);
1123     RefPtr<DocumentFragment> fragment = htmlElement->deprecatedCreateContextualFragment(markup);
1124 
1125     ExceptionCode exception = 0;
1126     m_element->parentNode()->insertBefore(fragment, m_element, exception);
1127 }
1128 
1129 /*!
1130     Inserts the given \a element after this element.
1131 
1132     If \a element is the child of another element, it is re-parented to the
1133     parent of this element.
1134 
1135     Calling this function on a null element does nothing.
1136 
1137     \sa appendInside(), prependInside(), prependOutside()
1138 */
appendOutside(const QWebElement & element)1139 void QWebElement::appendOutside(const QWebElement &element)
1140 {
1141     if (!m_element || element.isNull())
1142         return;
1143 
1144     if (!m_element->parentNode())
1145         return;
1146 
1147     ExceptionCode exception = 0;
1148     if (!m_element->nextSibling())
1149         m_element->parentNode()->appendChild(element.m_element, exception);
1150     else
1151         m_element->parentNode()->insertBefore(element.m_element, m_element->nextSibling(), exception);
1152 }
1153 
1154 /*!
1155     Inserts the result of parsing \a markup after this element.
1156 
1157     Calling this function on a null element does nothing.
1158 
1159     \sa appendInside(), prependInside(), prependOutside()
1160 */
appendOutside(const QString & markup)1161 void QWebElement::appendOutside(const QString &markup)
1162 {
1163     if (!m_element)
1164         return;
1165 
1166     if (!m_element->parentNode())
1167         return;
1168 
1169     if (!m_element->isHTMLElement())
1170         return;
1171 
1172     HTMLElement* htmlElement = static_cast<HTMLElement*>(m_element);
1173     RefPtr<DocumentFragment> fragment = htmlElement->deprecatedCreateContextualFragment(markup);
1174 
1175     ExceptionCode exception = 0;
1176     if (!m_element->nextSibling())
1177         m_element->parentNode()->appendChild(fragment, exception);
1178     else
1179         m_element->parentNode()->insertBefore(fragment, m_element->nextSibling(), exception);
1180 }
1181 
1182 /*!
1183     Returns a clone of this element.
1184 
1185     The clone may be inserted at any point in the document.
1186 
1187     \sa appendInside(), prependInside(), prependOutside(), appendOutside()
1188 */
clone() const1189 QWebElement QWebElement::clone() const
1190 {
1191     if (!m_element)
1192         return QWebElement();
1193 
1194     return QWebElement(m_element->cloneElementWithChildren().get());
1195 }
1196 
1197 /*!
1198     Removes this element from the document and returns a reference to it.
1199 
1200     The element is still valid after removal, and can be inserted into other
1201     parts of the document.
1202 
1203     \sa removeAllChildren(), removeFromDocument()
1204 */
takeFromDocument()1205 QWebElement &QWebElement::takeFromDocument()
1206 {
1207     if (!m_element)
1208         return *this;
1209 
1210     ExceptionCode exception = 0;
1211     m_element->remove(exception);
1212 
1213     return *this;
1214 }
1215 
1216 /*!
1217     Removes this element from the document and makes it a null element.
1218 
1219     \sa removeAllChildren(), takeFromDocument()
1220 */
removeFromDocument()1221 void QWebElement::removeFromDocument()
1222 {
1223     if (!m_element)
1224         return;
1225 
1226     ExceptionCode exception = 0;
1227     m_element->remove(exception);
1228     m_element->deref();
1229     m_element = 0;
1230 }
1231 
1232 /*!
1233     Removes all children from this element.
1234 
1235     \sa removeFromDocument(), takeFromDocument()
1236 */
removeAllChildren()1237 void QWebElement::removeAllChildren()
1238 {
1239     if (!m_element)
1240         return;
1241 
1242     m_element->removeAllChildren();
1243 }
1244 
1245 // FIXME: This code, and all callers are wrong, and have no place in a
1246 // WebKit implementation.  These should be replaced with WebCore implementations.
findInsertionPoint(PassRefPtr<Node> root)1247 static RefPtr<Node> findInsertionPoint(PassRefPtr<Node> root)
1248 {
1249     RefPtr<Node> node = root;
1250 
1251     // Go as far down the tree as possible.
1252     while (node->hasChildNodes() && node->firstChild()->isElementNode())
1253         node = node->firstChild();
1254 
1255     // TODO: Implement SVG support
1256     if (node->isHTMLElement()) {
1257         HTMLElement* element = static_cast<HTMLElement*>(node.get());
1258 
1259         // The insert point could be a non-enclosable tag and it can thus
1260         // never have children, so go one up. Get the parent element, and not
1261         // note as a root note will always exist.
1262         if (element->ieForbidsInsertHTML())
1263             node = node->parentElement();
1264     }
1265 
1266     return node;
1267 }
1268 
1269 /*!
1270     Encloses the contents of this element with \a element. This element becomes
1271     the child of the deepest descendant within \a element.
1272 
1273     ### illustration
1274 
1275     \sa encloseWith()
1276 */
encloseContentsWith(const QWebElement & element)1277 void QWebElement::encloseContentsWith(const QWebElement &element)
1278 {
1279     if (!m_element || element.isNull())
1280         return;
1281 
1282     RefPtr<Node> insertionPoint = findInsertionPoint(element.m_element);
1283 
1284     if (!insertionPoint)
1285         return;
1286 
1287     ExceptionCode exception = 0;
1288 
1289     // reparent children
1290     for (RefPtr<Node> child = m_element->firstChild(); child;) {
1291         RefPtr<Node> next = child->nextSibling();
1292         insertionPoint->appendChild(child, exception);
1293         child = next;
1294     }
1295 
1296     if (m_element->hasChildNodes())
1297         m_element->insertBefore(element.m_element, m_element->firstChild(), exception);
1298     else
1299         m_element->appendChild(element.m_element, exception);
1300 }
1301 
1302 /*!
1303     Encloses the contents of this element with the result of parsing \a markup.
1304     This element becomes the child of the deepest descendant within \a markup.
1305 
1306     \sa encloseWith()
1307 */
encloseContentsWith(const QString & markup)1308 void QWebElement::encloseContentsWith(const QString &markup)
1309 {
1310     if (!m_element)
1311         return;
1312 
1313     if (!m_element->parentNode())
1314         return;
1315 
1316     if (!m_element->isHTMLElement())
1317         return;
1318 
1319     HTMLElement* htmlElement = static_cast<HTMLElement*>(m_element);
1320     RefPtr<DocumentFragment> fragment = htmlElement->deprecatedCreateContextualFragment(markup);
1321 
1322     if (!fragment || !fragment->firstChild())
1323         return;
1324 
1325     RefPtr<Node> insertionPoint = findInsertionPoint(fragment->firstChild());
1326 
1327     if (!insertionPoint)
1328         return;
1329 
1330     ExceptionCode exception = 0;
1331 
1332     // reparent children
1333     for (RefPtr<Node> child = m_element->firstChild(); child;) {
1334         RefPtr<Node> next = child->nextSibling();
1335         insertionPoint->appendChild(child, exception);
1336         child = next;
1337     }
1338 
1339     if (m_element->hasChildNodes())
1340         m_element->insertBefore(fragment, m_element->firstChild(), exception);
1341     else
1342         m_element->appendChild(fragment, exception);
1343 }
1344 
1345 /*!
1346     Encloses this element with \a element. This element becomes the child of
1347     the deepest descendant within \a element.
1348 
1349     \sa replace()
1350 */
encloseWith(const QWebElement & element)1351 void QWebElement::encloseWith(const QWebElement &element)
1352 {
1353     if (!m_element || element.isNull())
1354         return;
1355 
1356     RefPtr<Node> insertionPoint = findInsertionPoint(element.m_element);
1357 
1358     if (!insertionPoint)
1359         return;
1360 
1361     // Keep reference to these two nodes before pulling out this element and
1362     // wrapping it in the fragment. The reason for doing it in this order is
1363     // that once the fragment has been added to the document it is empty, so
1364     // we no longer have access to the nodes it contained.
1365     Node* parent = m_element->parentNode();
1366     Node* siblingNode = m_element->nextSibling();
1367 
1368     ExceptionCode exception = 0;
1369     insertionPoint->appendChild(m_element, exception);
1370 
1371     if (!siblingNode)
1372         parent->appendChild(element.m_element, exception);
1373     else
1374         parent->insertBefore(element.m_element, siblingNode, exception);
1375 }
1376 
1377 /*!
1378     Encloses this element with the result of parsing \a markup. This element
1379     becomes the child of the deepest descendant within \a markup.
1380 
1381     \sa replace()
1382 */
encloseWith(const QString & markup)1383 void QWebElement::encloseWith(const QString &markup)
1384 {
1385     if (!m_element)
1386         return;
1387 
1388     if (!m_element->parentNode())
1389         return;
1390 
1391     if (!m_element->isHTMLElement())
1392         return;
1393 
1394     HTMLElement* htmlElement = static_cast<HTMLElement*>(m_element);
1395     RefPtr<DocumentFragment> fragment = htmlElement->deprecatedCreateContextualFragment(markup);
1396 
1397     if (!fragment || !fragment->firstChild())
1398         return;
1399 
1400     RefPtr<Node> insertionPoint = findInsertionPoint(fragment->firstChild());
1401 
1402     if (!insertionPoint)
1403         return;
1404 
1405     // Keep reference to these two nodes before pulling out this element and
1406     // wrapping it in the fragment. The reason for doing it in this order is
1407     // that once the fragment has been added to the document it is empty, so
1408     // we no longer have access to the nodes it contained.
1409     Node* parent = m_element->parentNode();
1410     Node* siblingNode = m_element->nextSibling();
1411 
1412     ExceptionCode exception = 0;
1413     insertionPoint->appendChild(m_element, exception);
1414 
1415     if (!siblingNode)
1416         parent->appendChild(fragment, exception);
1417     else
1418         parent->insertBefore(fragment, siblingNode, exception);
1419 }
1420 
1421 /*!
1422     Replaces this element with \a element.
1423 
1424     This method will not replace the <html>, <head> or <body> elements.
1425 
1426     \sa encloseWith()
1427 */
replace(const QWebElement & element)1428 void QWebElement::replace(const QWebElement &element)
1429 {
1430     if (!m_element || element.isNull())
1431         return;
1432 
1433     appendOutside(element);
1434     takeFromDocument();
1435 }
1436 
1437 /*!
1438     Replaces this element with the result of parsing \a markup.
1439 
1440     This method will not replace the <html>, <head> or <body> elements.
1441 
1442     \sa encloseWith()
1443 */
replace(const QString & markup)1444 void QWebElement::replace(const QString &markup)
1445 {
1446     if (!m_element)
1447         return;
1448 
1449     appendOutside(markup);
1450     takeFromDocument();
1451 }
1452 
1453 /*!
1454     \internal
1455     Walk \a node's parents until a valid QWebElement is found.
1456     For example, a WebCore::Text node is not a valid Html QWebElement, but its
1457     enclosing p tag is.
1458 */
enclosingElement(WebCore::Node * node)1459 QWebElement QWebElement::enclosingElement(WebCore::Node* node)
1460 {
1461     QWebElement element(node);
1462 
1463     while (element.isNull() && node) {
1464         node = node->parentNode();
1465         element = QWebElement(node);
1466     }
1467     return element;
1468 }
1469 
1470 /*!
1471     \fn inline bool QWebElement::operator==(const QWebElement& o) const;
1472 
1473     Returns true if this element points to the same underlying DOM object as
1474     \a o; otherwise returns false.
1475 */
1476 
1477 /*!
1478     \fn inline bool QWebElement::operator!=(const QWebElement& o) const;
1479 
1480     Returns true if this element points to a different underlying DOM object
1481     than \a o; otherwise returns false.
1482 */
1483 
1484 
1485 /*!
1486   Render the element into \a painter .
1487 */
render(QPainter * painter)1488 void QWebElement::render(QPainter* painter)
1489 {
1490     render(painter, QRect());
1491 }
1492 
1493 /*!
1494   Render the element into \a painter clipping to \a clip.
1495 */
render(QPainter * painter,const QRect & clip)1496 void QWebElement::render(QPainter* painter, const QRect& clip)
1497 {
1498     WebCore::Element* e = m_element;
1499     Document* doc = e ? e->document() : 0;
1500     if (!doc)
1501         return;
1502 
1503     Frame* frame = doc->frame();
1504     if (!frame || !frame->view() || !frame->contentRenderer())
1505         return;
1506 
1507     FrameView* view = frame->view();
1508 
1509     view->updateLayoutAndStyleIfNeededRecursive();
1510 
1511     IntRect rect = e->getRect();
1512 
1513     if (rect.size().isEmpty())
1514         return;
1515 
1516     QRect finalClipRect = rect;
1517     if (!clip.isEmpty())
1518         rect.intersect(clip.translated(rect.location()));
1519 
1520     GraphicsContext context(painter);
1521 
1522     context.save();
1523     context.translate(-rect.x(), -rect.y());
1524     painter->setClipRect(finalClipRect, Qt::IntersectClip);
1525     view->setNodeToDraw(e);
1526     view->paintContents(&context, finalClipRect);
1527     view->setNodeToDraw(0);
1528     context.restore();
1529 }
1530 
1531 class QWebElementCollectionPrivate : public QSharedData
1532 {
1533 public:
1534     static QWebElementCollectionPrivate* create(const PassRefPtr<Node> &context, const QString &query);
1535 
1536     RefPtr<NodeList> m_result;
1537 
1538 private:
QWebElementCollectionPrivate()1539     inline QWebElementCollectionPrivate() {}
1540 };
1541 
create(const PassRefPtr<Node> & context,const QString & query)1542 QWebElementCollectionPrivate* QWebElementCollectionPrivate::create(const PassRefPtr<Node> &context, const QString &query)
1543 {
1544     if (!context)
1545         return 0;
1546 
1547     // Let WebKit do the hard work hehehe
1548     ExceptionCode exception = 0; // ###
1549     RefPtr<NodeList> nodes = context->querySelectorAll(query, exception);
1550     if (!nodes)
1551         return 0;
1552 
1553     QWebElementCollectionPrivate* priv = new QWebElementCollectionPrivate;
1554     priv->m_result = nodes;
1555     return priv;
1556 }
1557 
1558 /*!
1559     \class QWebElementCollection
1560     \since 4.6
1561     \brief The QWebElementCollection class represents a collection of web elements.
1562     \preliminary
1563 
1564     Elements in a document can be selected using QWebElement::findAll() or using the
1565     QWebElement constructor. The collection is composed by choosing all elements in the
1566     document that match a specified CSS selector expression.
1567 
1568     The number of selected elements is provided through the count() property. Individual
1569     elements can be retrieved by index using at().
1570 
1571     It is also possible to iterate through all elements in the collection using Qt's foreach
1572     macro:
1573 
1574     \code
1575         QWebElementCollection collection = document.findAll("p");
1576         foreach (QWebElement paraElement, collection) {
1577             ...
1578         }
1579     \endcode
1580 */
1581 
1582 /*!
1583     Constructs an empty collection.
1584 */
QWebElementCollection()1585 QWebElementCollection::QWebElementCollection()
1586 {
1587 }
1588 
1589 /*!
1590     Constructs a copy of \a other.
1591 */
QWebElementCollection(const QWebElementCollection & other)1592 QWebElementCollection::QWebElementCollection(const QWebElementCollection &other)
1593     : d(other.d)
1594 {
1595 }
1596 
1597 /*!
1598     Constructs a collection of elements from the list of child elements of \a contextElement that
1599     match the specified CSS selector \a query.
1600 */
QWebElementCollection(const QWebElement & contextElement,const QString & query)1601 QWebElementCollection::QWebElementCollection(const QWebElement &contextElement, const QString &query)
1602 {
1603     d = QExplicitlySharedDataPointer<QWebElementCollectionPrivate>(QWebElementCollectionPrivate::create(contextElement.m_element, query));
1604 }
1605 
1606 /*!
1607     Assigns \a other to this collection and returns a reference to this collection.
1608 */
operator =(const QWebElementCollection & other)1609 QWebElementCollection &QWebElementCollection::operator=(const QWebElementCollection &other)
1610 {
1611     d = other.d;
1612     return *this;
1613 }
1614 
1615 /*!
1616     Destroys the collection.
1617 */
~QWebElementCollection()1618 QWebElementCollection::~QWebElementCollection()
1619 {
1620 }
1621 
1622 /*! \fn QWebElementCollection &QWebElementCollection::operator+=(const QWebElementCollection &other)
1623 
1624     Appends the items of the \a other list to this list and returns a
1625     reference to this list.
1626 
1627     \sa operator+(), append()
1628 */
1629 
1630 /*!
1631     Returns a collection that contains all the elements of this collection followed
1632     by all the elements in the \a other collection. Duplicates may occur in the result.
1633 
1634     \sa operator+=()
1635 */
operator +(const QWebElementCollection & other) const1636 QWebElementCollection QWebElementCollection::operator+(const QWebElementCollection &other) const
1637 {
1638     QWebElementCollection n = *this; n.d.detach(); n += other; return n;
1639 }
1640 
1641 /*!
1642     Extends the collection by appending all items of \a other.
1643 
1644     The resulting collection may include duplicate elements.
1645 
1646     \sa operator+=()
1647 */
append(const QWebElementCollection & other)1648 void QWebElementCollection::append(const QWebElementCollection &other)
1649 {
1650     if (!d) {
1651         *this = other;
1652         return;
1653     }
1654     if (!other.d)
1655         return;
1656     Vector<RefPtr<Node> > nodes;
1657     RefPtr<NodeList> results[] = { d->m_result, other.d->m_result };
1658     nodes.reserveInitialCapacity(results[0]->length() + results[1]->length());
1659 
1660     for (int i = 0; i < 2; ++i) {
1661         int j = 0;
1662         Node* n = results[i]->item(j);
1663         while (n) {
1664             nodes.append(n);
1665             n = results[i]->item(++j);
1666         }
1667     }
1668 
1669     d->m_result = StaticNodeList::adopt(nodes);
1670 }
1671 
1672 /*!
1673     Returns the number of elements in the collection.
1674 */
count() const1675 int QWebElementCollection::count() const
1676 {
1677     if (!d)
1678         return 0;
1679     return d->m_result->length();
1680 }
1681 
1682 /*!
1683     Returns the element at index position \a i in the collection.
1684 */
at(int i) const1685 QWebElement QWebElementCollection::at(int i) const
1686 {
1687     if (!d)
1688         return QWebElement();
1689     Node* n = d->m_result->item(i);
1690     return QWebElement(static_cast<Element*>(n));
1691 }
1692 
1693 /*!
1694     \fn const QWebElement QWebElementCollection::operator[](int position) const
1695 
1696     Returns the element at the specified \a position in the collection.
1697 */
1698 
1699 /*! \fn QWebElement QWebElementCollection::first() const
1700 
1701     Returns the first element in the collection.
1702 
1703     \sa last(), operator[](), at(), count()
1704 */
1705 
1706 /*! \fn QWebElement QWebElementCollection::last() const
1707 
1708     Returns the last element in the collection.
1709 
1710     \sa first(), operator[](), at(), count()
1711 */
1712 
1713 /*!
1714     Returns a QList object with the elements contained in this collection.
1715 */
toList() const1716 QList<QWebElement> QWebElementCollection::toList() const
1717 {
1718     if (!d)
1719         return QList<QWebElement>();
1720     QList<QWebElement> elements;
1721     int i = 0;
1722     Node* n = d->m_result->item(i);
1723     while (n) {
1724         if (n->isElementNode())
1725             elements.append(QWebElement(static_cast<Element*>(n)));
1726         n = d->m_result->item(++i);
1727     }
1728     return elements;
1729 }
1730 
1731 /*!
1732     \fn QWebElementCollection::const_iterator QWebElementCollection::begin() const
1733 
1734     Returns an STL-style iterator pointing to the first element in the collection.
1735 
1736     \sa end()
1737 */
1738 
1739 /*!
1740     \fn QWebElementCollection::const_iterator QWebElementCollection::end() const
1741 
1742     Returns an STL-style iterator pointing to the imaginary element after the
1743     last element in the list.
1744 
1745     \sa begin()
1746 */
1747 
1748 /*!
1749     \class QWebElementCollection::const_iterator
1750     \since 4.6
1751     \brief The QWebElementCollection::const_iterator class provides an STL-style const iterator for QWebElementCollection.
1752 
1753     QWebElementCollection provides STL style const iterators for fast low-level access to the elements.
1754 
1755     QWebElementCollection::const_iterator allows you to iterate over a QWebElementCollection.
1756 */
1757 
1758 /*!
1759     \fn QWebElementCollection::const_iterator::const_iterator(const const_iterator &other)
1760 
1761     Constructs a copy of \a other.
1762 */
1763 
1764 /*!
1765     \fn QWebElementCollection::const_iterator::const_iterator(const QWebElementCollection *collection, int index)
1766     \internal
1767 */
1768 
1769 /*!
1770     \fn const QWebElement QWebElementCollection::const_iterator::operator*() const
1771 
1772     Returns the current element.
1773 */
1774 
1775 /*!
1776     \fn bool QWebElementCollection::const_iterator::operator==(const const_iterator &other) const
1777 
1778     Returns true if \a other points to the same item as this iterator;
1779     otherwise returns false.
1780 
1781     \sa operator!=()
1782 */
1783 
1784 /*!
1785     \fn bool QWebElementCollection::const_iterator::operator!=(const const_iterator &other) const
1786 
1787     Returns true if \a other points to a different element than this;
1788     iterator; otherwise returns false.
1789 
1790     \sa operator==()
1791 */
1792 
1793 /*!
1794     \fn QWebElementCollection::const_iterator &QWebElementCollection::const_iterator::operator++()
1795 
1796     The prefix ++ operator (\c{++it}) advances the iterator to the next element in the collection
1797     and returns an iterator to the new current element.
1798 
1799     Calling this function on QWebElementCollection::end() leads to undefined results.
1800 
1801     \sa operator--()
1802 */
1803 
1804 /*!
1805     \fn QWebElementCollection::const_iterator QWebElementCollection::const_iterator::operator++(int)
1806 
1807     \overload
1808 
1809     The postfix ++ operator (\c{it++}) advances the iterator to the next element in the collection
1810     and returns an iterator to the previously current element.
1811 
1812     Calling this function on QWebElementCollection::end() leads to undefined results.
1813 */
1814 
1815 /*!
1816     \fn QWebElementCollection::const_iterator &QWebElementCollection::const_iterator::operator--()
1817 
1818     The prefix -- operator (\c{--it}) makes the preceding element current and returns an
1819     iterator to the new current element.
1820 
1821     Calling this function on QWebElementCollection::begin() leads to undefined results.
1822 
1823     \sa operator++()
1824 */
1825 
1826 /*!
1827     \fn QWebElementCollection::const_iterator QWebElementCollection::const_iterator::operator--(int)
1828 
1829     \overload
1830 
1831     The postfix -- operator (\c{it--}) makes the preceding element current and returns
1832     an iterator to the previously current element.
1833 */
1834 
1835 /*!
1836     \fn QWebElementCollection::const_iterator &QWebElementCollection::const_iterator::operator+=(int j)
1837 
1838     Advances the iterator by \a j elements. If \a j is negative, the iterator goes backward.
1839 
1840     \sa operator-=(), operator+()
1841 */
1842 
1843 /*!
1844     \fn QWebElementCollection::const_iterator &QWebElementCollection::const_iterator::operator-=(int j)
1845 
1846     Makes the iterator go back by \a j elements. If \a j is negative, the iterator goes forward.
1847 
1848     \sa operator+=(), operator-()
1849 */
1850 
1851 /*!
1852     \fn QWebElementCollection::const_iterator QWebElementCollection::const_iterator::operator+(int j) const
1853 
1854     Returns an iterator to the element at \a j positions forward from this iterator. If \a j
1855     is negative, the iterator goes backward.
1856 
1857     \sa operator-(), operator+=()
1858 */
1859 
1860 /*!
1861     \fn QWebElementCollection::const_iterator QWebElementCollection::const_iterator::operator-(int j) const
1862 
1863     Returns an iterator to the element at \a j positiosn backward from this iterator.
1864     If \a j is negative, the iterator goes forward.
1865 
1866     \sa operator+(), operator-=()
1867 */
1868 
1869 /*!
1870     \fn int QWebElementCollection::const_iterator::operator-(const_iterator other) const
1871 
1872     Returns the number of elements between the item point to by \a other
1873     and the element pointed to by this iterator.
1874 */
1875 
1876 /*!
1877     \fn bool QWebElementCollection::const_iterator::operator<(const const_iterator &other) const
1878 
1879     Returns true if the element pointed to by this iterator is less than the element pointed to
1880     by the \a other iterator.
1881 */
1882 
1883 /*!
1884     \fn bool QWebElementCollection::const_iterator::operator<=(const const_iterator &other) const
1885 
1886     Returns true if the element pointed to by this iterator is less than or equal to the
1887     element pointed to by the \a other iterator.
1888 */
1889 
1890 /*!
1891     \fn bool QWebElementCollection::const_iterator::operator>(const const_iterator &other) const
1892 
1893     Returns true if the element pointed to by this iterator is greater than the element pointed to
1894     by the \a other iterator.
1895 */
1896 
1897 /*!
1898     \fn bool QWebElementCollection::const_iterator::operator>=(const const_iterator &other) const
1899 
1900     Returns true if the element pointed to by this iterator is greater than or equal to the
1901     element pointed to by the \a other iterator.
1902 */
1903 
1904 /*!
1905     \fn QWebElementCollection::iterator QWebElementCollection::begin()
1906 
1907     Returns an STL-style iterator pointing to the first element in the collection.
1908 
1909     \sa end()
1910 */
1911 
1912 /*!
1913     \fn QWebElementCollection::iterator QWebElementCollection::end()
1914 
1915     Returns an STL-style iterator pointing to the imaginary element after the
1916     last element in the list.
1917 
1918     \sa begin()
1919 */
1920 
1921 /*!
1922     \fn QWebElementCollection::const_iterator QWebElementCollection::constBegin() const
1923 
1924     Returns an STL-style iterator pointing to the first element in the collection.
1925 
1926     \sa end()
1927 */
1928 
1929 /*!
1930     \fn QWebElementCollection::const_iterator QWebElementCollection::constEnd() const
1931 
1932     Returns an STL-style iterator pointing to the imaginary element after the
1933     last element in the list.
1934 
1935     \sa begin()
1936 */
1937 
1938 /*!
1939     \class QWebElementCollection::iterator
1940     \since 4.6
1941     \brief The QWebElementCollection::iterator class provides an STL-style iterator for QWebElementCollection.
1942 
1943     QWebElementCollection provides STL style iterators for fast low-level access to the elements.
1944 
1945     QWebElementCollection::iterator allows you to iterate over a QWebElementCollection.
1946 */
1947 
1948 /*!
1949     \fn QWebElementCollection::iterator::iterator(const iterator &other)
1950 
1951     Constructs a copy of \a other.
1952 */
1953 
1954 /*!
1955     \fn QWebElementCollection::iterator::iterator(const QWebElementCollection *collection, int index)
1956     \internal
1957 */
1958 
1959 /*!
1960     \fn const QWebElement QWebElementCollection::iterator::operator*() const
1961 
1962     Returns the current element.
1963 */
1964 
1965 /*!
1966     \fn bool QWebElementCollection::iterator::operator==(const iterator &other) const
1967 
1968     Returns true if \a other points to the same item as this iterator;
1969     otherwise returns false.
1970 
1971     \sa operator!=()
1972 */
1973 
1974 /*!
1975     \fn bool QWebElementCollection::iterator::operator!=(const iterator &other) const
1976 
1977     Returns true if \a other points to a different element than this;
1978     iterator; otherwise returns false.
1979 
1980     \sa operator==()
1981 */
1982 
1983 /*!
1984     \fn QWebElementCollection::iterator &QWebElementCollection::iterator::operator++()
1985 
1986     The prefix ++ operator (\c{++it}) advances the iterator to the next element in the collection
1987     and returns an iterator to the new current element.
1988 
1989     Calling this function on QWebElementCollection::end() leads to undefined results.
1990 
1991     \sa operator--()
1992 */
1993 
1994 /*!
1995     \fn QWebElementCollection::iterator QWebElementCollection::iterator::operator++(int)
1996 
1997     \overload
1998 
1999     The postfix ++ operator (\c{it++}) advances the iterator to the next element in the collection
2000     and returns an iterator to the previously current element.
2001 
2002     Calling this function on QWebElementCollection::end() leads to undefined results.
2003 */
2004 
2005 /*!
2006     \fn QWebElementCollection::iterator &QWebElementCollection::iterator::operator--()
2007 
2008     The prefix -- operator (\c{--it}) makes the preceding element current and returns an
2009     iterator to the new current element.
2010 
2011     Calling this function on QWebElementCollection::begin() leads to undefined results.
2012 
2013     \sa operator++()
2014 */
2015 
2016 /*!
2017     \fn QWebElementCollection::iterator QWebElementCollection::iterator::operator--(int)
2018 
2019     \overload
2020 
2021     The postfix -- operator (\c{it--}) makes the preceding element current and returns
2022     an iterator to the previously current element.
2023 */
2024 
2025 /*!
2026     \fn QWebElementCollection::iterator &QWebElementCollection::iterator::operator+=(int j)
2027 
2028     Advances the iterator by \a j elements. If \a j is negative, the iterator goes backward.
2029 
2030     \sa operator-=(), operator+()
2031 */
2032 
2033 /*!
2034     \fn QWebElementCollection::iterator &QWebElementCollection::iterator::operator-=(int j)
2035 
2036     Makes the iterator go back by \a j elements. If \a j is negative, the iterator goes forward.
2037 
2038     \sa operator+=(), operator-()
2039 */
2040 
2041 /*!
2042     \fn QWebElementCollection::iterator QWebElementCollection::iterator::operator+(int j) const
2043 
2044     Returns an iterator to the element at \a j positions forward from this iterator. If \a j
2045     is negative, the iterator goes backward.
2046 
2047     \sa operator-(), operator+=()
2048 */
2049 
2050 /*!
2051     \fn QWebElementCollection::iterator QWebElementCollection::iterator::operator-(int j) const
2052 
2053     Returns an iterator to the element at \a j positiosn backward from this iterator.
2054     If \a j is negative, the iterator goes forward.
2055 
2056     \sa operator+(), operator-=()
2057 */
2058 
2059 /*!
2060     \fn int QWebElementCollection::iterator::operator-(iterator other) const
2061 
2062     Returns the number of elements between the item point to by \a other
2063     and the element pointed to by this iterator.
2064 */
2065 
2066 /*!
2067     \fn bool QWebElementCollection::iterator::operator<(const iterator &other) const
2068 
2069     Returns true if the element pointed to by this iterator is less than the element pointed to
2070     by the \a other iterator.
2071 */
2072 
2073 /*!
2074     \fn bool QWebElementCollection::iterator::operator<=(const iterator &other) const
2075 
2076     Returns true if the element pointed to by this iterator is less than or equal to the
2077     element pointed to by the \a other iterator.
2078 */
2079 
2080 /*!
2081     \fn bool QWebElementCollection::iterator::operator>(const iterator &other) const
2082 
2083     Returns true if the element pointed to by this iterator is greater than the element pointed to
2084     by the \a other iterator.
2085 */
2086 
2087 /*!
2088     \fn bool QWebElementCollection::iterator::operator>=(const iterator &other) const
2089 
2090     Returns true if the element pointed to by this iterator is greater than or equal to the
2091     element pointed to by the \a other iterator.
2092 */
2093