File indexing completed on 2023-09-24 04:06:14
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 }