File indexing completed on 2024-04-28 15:23:17

0001 /*
0002  *  This file is part of the KDE libraries
0003  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
0004  *
0005  *  This library is free software; you can redistribute it and/or
0006  *  modify it under the terms of the GNU Library General Public
0007  *  License as published by the Free Software Foundation; either
0008  *  version 2 of the License, or (at your option) any later version.
0009  *
0010  *  This library is distributed in the hope that it will be useful,
0011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  *  Library General Public License for more details.
0014  *
0015  *  You should have received a copy of the GNU Library General Public
0016  *  License along with this library; if not, write to the Free Software
0017  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018  */
0019 
0020 #include "kjs_traversal.h"
0021 #include "kjs_traversal.lut.h"
0022 #include "kjs_proxy.h"
0023 #include <dom/dom_node.h>
0024 #include <xml/dom_nodeimpl.h>
0025 #include <xml/dom_docimpl.h>
0026 #include <khtmlview.h>
0027 #include <khtml_part.h>
0028 #include "khtml_debug.h"
0029 
0030 using namespace KJS;
0031 
0032 namespace KJS
0033 {
0034 
0035 class TraversalExceptionForwarder
0036 {
0037 public:
0038     explicit TraversalExceptionForwarder(ExecState *exec) : m_exec(exec), m_code(nullptr) { }
0039     ~TraversalExceptionForwarder()
0040     {
0041         if (m_code) {
0042             m_exec->setException(static_cast<JSValue *>(m_code));
0043         }
0044     }
0045     operator void *&()
0046     {
0047         return m_code;
0048     }
0049 private:
0050     ExecState *m_exec;
0051     void *m_code;
0052 };
0053 
0054 }
0055 // -------------------------------------------------------------------------
0056 
0057 const ClassInfo DOMNodeIterator::info = { "NodeIterator", nullptr, &DOMNodeIteratorTable, nullptr };
0058 /*
0059 @begin DOMNodeIteratorTable 5
0060   root              DOMNodeIterator::Root           DontDelete|ReadOnly
0061   whatToShow            DOMNodeIterator::WhatToShow     DontDelete|ReadOnly
0062   filter            DOMNodeIterator::Filter         DontDelete|ReadOnly
0063   expandEntityReferences    DOMNodeIterator::ExpandEntityReferences DontDelete|ReadOnly
0064 @end
0065 @begin DOMNodeIteratorProtoTable 3
0066   nextNode  DOMNodeIterator::NextNode   DontDelete|Function 0
0067   previousNode  DOMNodeIterator::PreviousNode   DontDelete|Function 0
0068   detach    DOMNodeIterator::Detach     DontDelete|Function 0
0069 @end
0070 */
0071 KJS_DEFINE_PROTOTYPE(DOMNodeIteratorProto)
0072 KJS_IMPLEMENT_PROTOFUNC(DOMNodeIteratorProtoFunc)
0073 KJS_IMPLEMENT_PROTOTYPE("DOMNodeIterator", DOMNodeIteratorProto, DOMNodeIteratorProtoFunc, ObjectPrototype)
0074 
0075 DOMNodeIterator::DOMNodeIterator(ExecState *exec, DOM::NodeIteratorImpl *ni)
0076     : DOMObject(DOMNodeIteratorProto::self(exec)), m_impl(ni) {}
0077 
0078 DOMNodeIterator::~DOMNodeIterator()
0079 {
0080     ScriptInterpreter::forgetDOMObject(m_impl.get());
0081 }
0082 
0083 bool DOMNodeIterator::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
0084 {
0085     return getStaticValueSlot<DOMNodeIterator, DOMObject>(exec, &DOMNodeIteratorTable, this, propertyName, slot);
0086 }
0087 
0088 JSValue *DOMNodeIterator::getValueProperty(ExecState *exec, int token) const
0089 {
0090     DOM::NodeIteratorImpl &ni = *impl();
0091     switch (token) {
0092     case Root:
0093         return getDOMNode(exec, ni.root());
0094     case WhatToShow:
0095         return jsNumber(ni.whatToShow());
0096     case Filter:
0097         return getDOMNodeFilter(exec, ni.filter());
0098     case ExpandEntityReferences:
0099         return jsBoolean(ni.expandEntityReferences());
0100     default:
0101         // qCDebug(KHTML_LOG) << "WARNING: Unhandled token in DOMNodeIterator::getValueProperty : " << token;
0102         return nullptr;
0103     }
0104 }
0105 
0106 JSValue *DOMNodeIteratorProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &)
0107 {
0108     KJS_CHECK_THIS(KJS::DOMNodeIterator, thisObj);
0109     DOMExceptionTranslator      exception(exec);
0110     TraversalExceptionForwarder filterException(exec);
0111     DOM::NodeIteratorImpl &nodeIterator = *static_cast<DOMNodeIterator *>(thisObj)->impl();
0112     switch (id) {
0113     case DOMNodeIterator::PreviousNode: {
0114         SharedPtr<DOM::NodeImpl> node = nodeIterator.previousNode(exception, filterException);
0115         if (!filterException) {
0116             return getDOMNode(exec, node.get());
0117         }
0118         break;
0119     }
0120     case DOMNodeIterator::NextNode: {
0121         SharedPtr<DOM::NodeImpl> node = nodeIterator.nextNode(exception, filterException);
0122         if (!filterException) {
0123             return getDOMNode(exec, node.get());
0124         }
0125         break;
0126     }
0127     case DOMNodeIterator::Detach:
0128         nodeIterator.detach(exception);
0129         return jsUndefined();
0130     }
0131     return jsUndefined();
0132 }
0133 
0134 JSValue *KJS::getDOMNodeIterator(ExecState *exec, DOM::NodeIteratorImpl *ni)
0135 {
0136     return cacheDOMObject<DOM::NodeIteratorImpl, DOMNodeIterator>(exec, ni);
0137 }
0138 
0139 // -------------------------------------------------------------------------
0140 
0141 const ClassInfo NodeFilterConstructor::info = { "NodeFilterConstructor", nullptr, &NodeFilterConstructorTable, nullptr };
0142 /*
0143 @begin NodeFilterConstructorTable 17
0144   FILTER_ACCEPT     DOM::NodeFilter::FILTER_ACCEPT  DontDelete|ReadOnly
0145   FILTER_REJECT     DOM::NodeFilter::FILTER_REJECT  DontDelete|ReadOnly
0146   FILTER_SKIP       DOM::NodeFilter::FILTER_SKIP    DontDelete|ReadOnly
0147   SHOW_ALL      DOM::NodeFilter::SHOW_ALL   DontDelete|ReadOnly
0148   SHOW_ELEMENT      DOM::NodeFilter::SHOW_ELEMENT   DontDelete|ReadOnly
0149   SHOW_ATTRIBUTE    DOM::NodeFilter::SHOW_ATTRIBUTE DontDelete|ReadOnly
0150   SHOW_TEXT     DOM::NodeFilter::SHOW_TEXT  DontDelete|ReadOnly
0151   SHOW_CDATA_SECTION    DOM::NodeFilter::SHOW_CDATA_SECTION DontDelete|ReadOnly
0152   SHOW_ENTITY_REFERENCE DOM::NodeFilter::SHOW_ENTITY_REFERENCE  DontDelete|ReadOnly
0153   SHOW_ENTITY       DOM::NodeFilter::SHOW_ENTITY    DontDelete|ReadOnly
0154   SHOW_PROCESSING_INSTRUCTION   DOM::NodeFilter::SHOW_PROCESSING_INSTRUCTION    DontDelete|ReadOnly
0155   SHOW_COMMENT      DOM::NodeFilter::SHOW_COMMENT   DontDelete|ReadOnly
0156   SHOW_DOCUMENT     DOM::NodeFilter::SHOW_DOCUMENT  DontDelete|ReadOnly
0157   SHOW_DOCUMENT_TYPE    DOM::NodeFilter::SHOW_DOCUMENT_TYPE DontDelete|ReadOnly
0158   SHOW_DOCUMENT_FRAGMENT    DOM::NodeFilter::SHOW_DOCUMENT_FRAGMENT DontDelete|ReadOnly
0159   SHOW_NOTATION     DOM::NodeFilter::SHOW_NOTATION  DontDelete|ReadOnly
0160 @end
0161 */
0162 
0163 NodeFilterConstructor::NodeFilterConstructor(ExecState *exec)
0164     : DOMObject(exec->lexicalInterpreter()->builtinObjectPrototype())
0165 {
0166 }
0167 
0168 bool NodeFilterConstructor::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
0169 {
0170     return getStaticValueSlot<NodeFilterConstructor, DOMObject>(exec, &NodeFilterConstructorTable, this, propertyName, slot);
0171 }
0172 
0173 JSValue *NodeFilterConstructor::getValueProperty(ExecState *, int token) const
0174 {
0175     // We use the token as the value to return directly
0176     return jsNumber(token);
0177 }
0178 
0179 JSValue *KJS::getNodeFilterConstructor(ExecState *exec)
0180 {
0181     return cacheGlobalObject<NodeFilterConstructor>(exec, "[[nodeFilter.constructor]]");
0182 }
0183 
0184 // -------------------------------------------------------------------------
0185 
0186 const ClassInfo DOMTreeWalker::info = { "TreeWalker", nullptr, &DOMTreeWalkerTable, nullptr };
0187 /*
0188 @begin DOMTreeWalkerTable 5
0189   root          DOMTreeWalker::Root     DontDelete|ReadOnly
0190   whatToShow        DOMTreeWalker::WhatToShow   DontDelete|ReadOnly
0191   filter        DOMTreeWalker::Filter       DontDelete|ReadOnly
0192   expandEntityReferences DOMTreeWalker::ExpandEntityReferences  DontDelete|ReadOnly
0193   currentNode       DOMTreeWalker::CurrentNode  DontDelete
0194 @end
0195 @begin DOMTreeWalkerProtoTable 7
0196   parentNode    DOMTreeWalker::ParentNode   DontDelete|Function 0
0197   firstChild    DOMTreeWalker::FirstChild   DontDelete|Function 0
0198   lastChild DOMTreeWalker::LastChild    DontDelete|Function 0
0199   previousSibling DOMTreeWalker::PreviousSibling    DontDelete|Function 0
0200   nextSibling   DOMTreeWalker::NextSibling  DontDelete|Function 0
0201   previousNode  DOMTreeWalker::PreviousNode DontDelete|Function 0
0202   nextNode  DOMTreeWalker::NextNode     DontDelete|Function 0
0203 @end
0204 */
0205 KJS_DEFINE_PROTOTYPE(DOMTreeWalkerProto)
0206 KJS_IMPLEMENT_PROTOFUNC(DOMTreeWalkerProtoFunc)
0207 KJS_IMPLEMENT_PROTOTYPE("DOMTreeWalker", DOMTreeWalkerProto, DOMTreeWalkerProtoFunc, ObjectPrototype)
0208 
0209 DOMTreeWalker::DOMTreeWalker(ExecState *exec, DOM::TreeWalkerImpl *tw)
0210     : m_impl(tw)
0211 {
0212     setPrototype(DOMTreeWalkerProto::self(exec));
0213 }
0214 
0215 DOMTreeWalker::~DOMTreeWalker()
0216 {
0217     ScriptInterpreter::forgetDOMObject(m_impl.get());
0218 }
0219 
0220 void DOMTreeWalker::mark()
0221 {
0222     JSObject::mark();
0223     JSNodeFilter *filt = JSNodeFilter::fromDOMFilter(impl()->getFilter());
0224     if (filt) {
0225         filt->mark();
0226     }
0227 }
0228 
0229 bool DOMTreeWalker::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
0230 {
0231     return getStaticValueSlot<DOMTreeWalker, DOMObject>(exec, &DOMTreeWalkerTable, this, propertyName, slot);
0232 }
0233 
0234 JSValue *DOMTreeWalker::getValueProperty(ExecState *exec, int token) const
0235 {
0236     DOM::TreeWalkerImpl &tw = *impl();
0237     switch (token) {
0238     case Root:
0239         return getDOMNode(exec, tw.getRoot());
0240     case WhatToShow:
0241         return jsNumber(tw.getWhatToShow());
0242     case Filter:
0243         return getDOMNodeFilter(exec, tw.getFilter());
0244     case ExpandEntityReferences:
0245         return jsBoolean(tw.getExpandEntityReferences());
0246     case CurrentNode:
0247         return getDOMNode(exec, tw.getCurrentNode());
0248     default:
0249         // qCDebug(KHTML_LOG) << "WARNING: Unhandled token in DOMTreeWalker::getValueProperty : " << token;
0250         return nullptr;
0251     }
0252 }
0253 
0254 void DOMTreeWalker::put(ExecState *exec, const Identifier &propertyName,
0255                         JSValue *value, int attr)
0256 {
0257     DOMExceptionTranslator exception(exec);
0258     if (propertyName == "currentNode") {
0259         m_impl->setCurrentNode(toNode(value), exception);
0260     } else {
0261         JSObject::put(exec, propertyName, value, attr);
0262     }
0263 }
0264 
0265 JSValue *DOMTreeWalkerProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &)
0266 {
0267     KJS_CHECK_THIS(KJS::DOMTreeWalker, thisObj);
0268     DOM::TreeWalkerImpl &treeWalker = *static_cast<DOMTreeWalker *>(thisObj)->impl();
0269     TraversalExceptionForwarder   filterException(exec);
0270     switch (id) {
0271     case DOMTreeWalker::ParentNode:
0272         return getDOMNode(exec, treeWalker.parentNode(filterException));
0273     case DOMTreeWalker::FirstChild:
0274         return getDOMNode(exec, treeWalker.firstChild(filterException));
0275     case DOMTreeWalker::LastChild:
0276         return getDOMNode(exec, treeWalker.lastChild(filterException));
0277     case DOMTreeWalker::PreviousSibling:
0278         return getDOMNode(exec, treeWalker.previousSibling(filterException));
0279     case DOMTreeWalker::NextSibling:
0280         return getDOMNode(exec, treeWalker.nextSibling(filterException));
0281     case DOMTreeWalker::PreviousNode:
0282         return getDOMNode(exec, treeWalker.previousNode(filterException));
0283     case DOMTreeWalker::NextNode:
0284         return getDOMNode(exec, treeWalker.nextNode(filterException));
0285     }
0286     return jsUndefined();
0287 }
0288 
0289 JSValue *KJS::getDOMTreeWalker(ExecState *exec, DOM::TreeWalkerImpl *tw)
0290 {
0291     return cacheDOMObject<DOM::TreeWalkerImpl, DOMTreeWalker>(exec, tw);
0292 }
0293 
0294 // -------------------------------------------------------------------------
0295 
0296 DOM::NodeFilterImpl *KJS::toNodeFilter(JSValue *val)
0297 {
0298     JSObject *obj = val->getObject();
0299     if (!obj) {
0300         return nullptr;
0301     }
0302 
0303     return new JSNodeFilter(obj);
0304 }
0305 
0306 JSValue *KJS::getDOMNodeFilter(ExecState *exec, DOM::NodeFilterImpl *nf)
0307 {
0308     Q_UNUSED(exec);
0309     if (nf && nf->isJSFilter()) {
0310         return static_cast<JSNodeFilter *>(nf)->filter();
0311     }
0312 
0313     return jsNull();
0314 }
0315 
0316 JSNodeFilter::JSNodeFilter(JSObject *_filter) : m_filter(_filter)
0317 {}
0318 
0319 JSNodeFilter::~JSNodeFilter()
0320 {}
0321 
0322 void JSNodeFilter::mark()
0323 {
0324     if (!m_filter->marked()) {
0325         m_filter->mark();
0326     }
0327 }
0328 
0329 bool JSNodeFilter::isJSFilter() const
0330 {
0331     return true;
0332 }
0333 
0334 JSNodeFilter *JSNodeFilter::fromDOMFilter(DOM::NodeFilterImpl *nf)
0335 {
0336     if (!nf || !nf->isJSFilter()) {
0337         return nullptr;
0338     }
0339 
0340     return static_cast<JSNodeFilter *>(nf);
0341 }
0342 
0343 short JSNodeFilter::acceptNode(const DOM::Node &n, void *&bindingsException)
0344 {
0345     KHTMLPart *part = n.handle()->document()->part();
0346     if (!part) {
0347         return DOM::NodeFilter::FILTER_REJECT;
0348     }
0349 
0350     KJSProxy *proxy = part->jScript();
0351     if (proxy) {
0352         ExecState *exec = proxy->interpreter()->globalExec();
0353         JSObject *fn = nullptr;
0354 
0355         // Use a function given directly, or extract it from the acceptNode field
0356         if (m_filter->implementsCall()) {
0357             fn = m_filter;
0358         } else {
0359             JSObject *acceptNodeFunc = m_filter->get(exec, "acceptNode")->getObject();
0360             if (acceptNodeFunc && acceptNodeFunc->implementsCall()) {
0361                 fn = acceptNodeFunc;
0362             }
0363         }
0364 
0365         if (fn) {
0366             List args;
0367             args.append(getDOMNode(exec, n.handle()));
0368             JSValue *result = fn->call(exec, m_filter, args);
0369             if (exec->hadException()) {
0370                 bindingsException = exec->exception();
0371                 exec->clearException();
0372                 return DOM::NodeFilter::FILTER_REJECT; // throw '1' isn't accept :-)
0373             }
0374             return result->toInteger(exec);
0375         }
0376     }
0377 
0378     return DOM::NodeFilter::FILTER_REJECT;
0379 }