File indexing completed on 2024-12-01 09:50:26

0001 /*
0002  *  This file is part of the KDE libraries
0003  *  Copyright (C) 2006 Matt Broadstone (mbroadst@gmail.com)
0004  *            (C) 2007 Maks Orlovich <maksim@kde.org>
0005  *
0006  *  This library is free software; you can redistribute it and/or
0007  *  modify it under the terms of the GNU Library General Public
0008  *  License as published by the Free Software Foundation; either
0009  *  version 2 of the License, or (at your option) any later version.
0010  *
0011  *  This library is distributed in the hope that it will be useful,
0012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014  *  Library General Public License for more details.
0015  *
0016  *  You should have received a copy of the GNU Library General Public
0017  *  License along with this library; if not, write to the Free Software
0018  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0019  */
0020 
0021 #include "localvariabledock.h"
0022 
0023 #include <QVBoxLayout>
0024 #include <QTreeWidget>
0025 #include <QEventLoop>
0026 #include <QStringList>
0027 
0028 #include <kjs/interpreter.h>
0029 #include <kjs/PropertyNameArray.h>
0030 #include <kjs/context.h>
0031 #include <kjs/scope_chain.h>
0032 #include <kjs/JSVariableObject.h>
0033 #include <kjs/object.h>
0034 #include "khtml_debug.h"
0035 #include <klocalizedstring.h>
0036 
0037 #include "value2string.h"
0038 
0039 namespace KJSDebugger
0040 {
0041 
0042 LocalVariablesDock::LocalVariablesDock(QWidget *parent)
0043     : QDockWidget(i18n("Local Variables"), parent), m_execState(0)
0044 {
0045     setFeatures(DockWidgetMovable | DockWidgetFloatable);
0046     m_view = new QTreeWidget(this);
0047 
0048     m_view->setColumnCount(2);
0049 
0050     QStringList headers;
0051     headers << i18n("Reference");
0052     headers << i18n("Value");
0053     m_view->setHeaderLabels(headers);
0054 
0055     connect(m_view, SIGNAL(itemExpanded(QTreeWidgetItem*)),
0056             this,   SLOT(slotItemExpanded(QTreeWidgetItem*)));
0057 
0058     setWidget(m_view);
0059 }
0060 
0061 LocalVariablesDock::~LocalVariablesDock()
0062 {
0063 }
0064 
0065 void LocalVariablesDock::updateValue(KJS::ExecState *exec, KJS::JSValue *val,
0066                                      QTreeWidgetItem *item, bool recurse)
0067 {
0068     // Note: parent is responsible for setting our name..
0069     item->setText(1, valueToString(val));
0070     if (recurse) {
0071         if (val->isObject()) {
0072             updateObjectProperties(exec, val, item);
0073         } else {
0074             // It has no kids, so treeview item better not, either
0075             QList<QTreeWidgetItem *> kids = item->takeChildren();
0076             qDeleteAll(kids);
0077         }
0078     }
0079 }
0080 
0081 void LocalVariablesDock::updateObjectProperties(KJS::ExecState *exec, KJS::JSValue *val,
0082         QTreeWidgetItem *item, bool globalObject)
0083 {
0084     bool root = (item == m_view->invisibleRootItem());
0085 
0086     // We have to be careful here -- we don't want to recurse if we're not open;
0087     // except for root since we may want the + there
0088     bool recurse = item->isExpanded() || root;
0089 
0090     QStringList props;
0091     KJS::JSObject *obj = 0;
0092 
0093     // Get the list of all relevant properties..
0094     // Note: val may be null for root case..
0095     if (val) {
0096         assert(val->isObject());
0097 
0098         obj = val->getObject();
0099         KJS::PropertyNameArray jsProps;
0100 
0101         // We use getOwnPropertyNames and not getPropertyNames here
0102         // to not include things from the prototype
0103         obj->getOwnPropertyNames(exec, jsProps, KJS::PropertyMap::ExcludeDontEnumProperties);
0104 
0105         for (int pos = 0; pos < jsProps.size(); ++pos) {
0106             // For global (window) objects, we only show hash table properties,
0107             // for less cluttered display.
0108             if (globalObject && !obj->getDirect(jsProps[pos])) {
0109                 continue;
0110             }
0111 
0112             props.append(jsProps[pos].ustring().qstring());
0113         }
0114     }
0115 
0116     // If we're not an activation, also list the prototype, as __proto__
0117     if (val && !val->getObject()->isActivation()) {
0118         props << QLatin1String("__proto__");
0119     }
0120 
0121     // If we're the root, also pretend 'this' is there.
0122     if (root && exec) {
0123         props << QLatin1String("this");
0124     }
0125 
0126     // Sort them, to make updates easier.
0127     props.sort();
0128 
0129     // Do we need more or less nodes?
0130     while (props.size() < item->childCount()) {
0131         delete item->takeChild(item->childCount() - 1);
0132     }
0133 
0134     while (props.size() > item->childCount()) {
0135         item->addChild(new QTreeWidgetItem);
0136     }
0137 
0138     // Update names and values.
0139     for (int pos = 0; pos < props.size(); ++pos) {
0140         QString propName = props[pos];
0141         QTreeWidgetItem *kid = item->child(pos);
0142         kid->setText(0, propName);
0143 
0144         if (root && propName == "this") {
0145             updateValue(exec, exec->thisValue(), kid, true);
0146         } else {
0147             updateValue(exec, obj->get(exec, KJS::Identifier(KJS::UString(props[pos]))), kid, recurse);
0148         }
0149     }
0150 }
0151 
0152 void LocalVariablesDock::slotItemExpanded(QTreeWidgetItem *item)
0153 {
0154     Q_UNUSED(item);
0155     updateDisplay(m_execState);
0156 }
0157 
0158 void LocalVariablesDock::updateDisplay(KJS::ExecState *exec)
0159 {
0160     m_execState = exec;
0161 
0162     // Find out out scope object...
0163     KJS::JSObject *scopeObject = 0;
0164     KJS::Context  *context = 0;
0165 
0166     if (exec) {
0167         context = exec->context();
0168     }
0169 
0170     // Find the nearest local scope, or
0171     // failing that the topmost scope
0172     if (context) {
0173         KJS::ScopeChain chain = context->scopeChain();
0174         for (KJS::ScopeChainIterator iter = chain.begin();
0175                 iter != chain.end(); ++iter) {
0176             scopeObject = *iter;
0177             if (scopeObject->isActivation()) {
0178                 break;
0179             }
0180         }
0181     }
0182 
0183     updateObjectProperties(exec, scopeObject, m_view->invisibleRootItem(),
0184                            scopeObject && !scopeObject->isActivation());
0185 }
0186 
0187 }
0188 
0189 #include "moc_localvariabledock.cpp"