KHtml

kjs_scriptable.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2010 Maksim Orlovich <[email protected]>
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 "kjs_scriptable.h"
21 #include "kjs_binding.h"
22 #include "kjs_proxy.h"
23 #include "kjs_dom.h"
24 #include "khtmlpart_p.h"
25 
26 namespace KJS
27 {
28 
29 static QVariant scriptableNull()
30 {
31  return QVariant::fromValue(ScriptableExtension::Null());
32 }
33 
34 static bool isException(const QVariant &v)
35 {
36  return v.canConvert<ScriptableExtension::Exception>();
37 }
38 
39 static bool isFuncRef(const QVariant &v)
40 {
41  return v.canConvert<ScriptableExtension::FunctionRef>();
42 }
43 
44 static bool isForeignObject(const QVariant &v)
45 {
46  return v.canConvert<ScriptableExtension::Object>();
47 }
48 
49 QVariant exception(const char *msg)
50 {
51  qCWarning(KHTML_LOG) << msg;
52  return QVariant::fromValue(ScriptableExtension::Exception(QString::fromLatin1(msg)));
53 }
54 
55 //------------------------------------------------------------------------------
56 
57 // will return with owner = 0 on failure.
58 static ScriptableExtension::Object grabRoot(ScriptableExtension *ext)
59 {
60  ScriptableExtension::Object o;
61 
62  if (!ext) {
63  return o;
64  }
65 
66  // Grab root, make sure it's an actual object.
67  QVariant root = ext->rootObject();
68  if (!isForeignObject(root)) {
69  // Might still need to release it if it's a function
70  ScriptableExtension::releaseValue(root);
71 
72  return o;
73  }
74 
75  o = root.value<ScriptableExtension::Object>();
76  return o;
77 }
78 
79 // a couple helpers for client code.
80 bool pluginRootGet(ExecState *exec, ScriptableExtension *ext, const KJS::Identifier &i, PropertySlot &slot)
81 {
82  ScriptableExtension::Object rootObj = grabRoot(ext);
83  if (!rootObj.owner) {
84  return false;
85  }
86 
87  QVariant v = rootObj.owner->get(nullptr /* ### we don't expect leaves to check credentials*/,
88  rootObj.objId, i.qstring());
89 
90  bool ok = false;
91  if (!isException(v)) {
92  getImmediateValueSlot(nullptr, ScriptableOperations::importValue(exec, v, true), slot);
93  ok = true;
94  }
95 
96  rootObj.owner->release(rootObj.objId);
97  return ok;
98 }
99 
100 bool pluginRootPut(ExecState * /*exec*/, ScriptableExtension *ext, const KJS::Identifier &i, JSValue *v)
101 {
102  ScriptableExtension::Object rootObj = grabRoot(ext);
103  if (!rootObj.owner) {
104  return false;
105  }
106 
107  QVariant qv = ScriptableOperations::exportValue(v, true);
108  bool ok = rootObj.owner->put(nullptr, rootObj.objId, i.qstring(), qv);
109  ScriptableExtension::releaseValue(qv);
110 
111  rootObj.owner->release(rootObj.objId);
112  return ok;
113 }
114 
115 //------------------------------------------------------------------------------
116 // KJS peer wrapping external objects
117 
118 const ClassInfo WrapScriptableObject::info = { " WrapScriptableObject", nullptr, nullptr, nullptr };
119 
120 WrapScriptableObject::WrapScriptableObject(ExecState * /*exec*/, Type t,
121  ScriptableExtension *owner, quint64 objId,
122  const QString &field):
123  objExtension(owner), objId(objId), field(field), type(t), refsByUs(1), tableKey(owner)
124 {
125  owner->acquire(objId);
126 }
127 
128 WrapScriptableObject::~WrapScriptableObject()
129 {
130  if (ScriptableExtension *o = objExtension.data()) {
131  for (int r = 0; r < refsByUs; ++r) {
132  o->release(objId);
133  }
134  }
135 
136  ScriptableExtension::Object obj(tableKey, objId);
137  if (type == Object) {
138  ScriptableOperations::importedObjects()->remove(obj);
139  } else {
140  ScriptableOperations::importedFunctions()->remove(ScriptableExtension::FunctionRef(obj, field));
141  }
142 }
143 
144 void WrapScriptableObject::reportRef()
145 {
146  ++refsByUs;
147 }
148 
149 QVariant WrapScriptableObject::doGet(ExecState *exec, const ScriptableExtension::Object &o,
150  const QString &field, bool *ok)
151 {
152  *ok = false;
153 
154  if (!o.owner) { // such as when was constructed from null .data();
155  return QVariant();
156  }
157 
158  QVariant v = o.owner->get(principal(exec), o.objId, field);
159 
160  if (!isException(v)) {
161  *ok = true;
162  }
163  return v;
164 }
165 
166 ScriptableExtension::Object WrapScriptableObject::resolveReferences(
167  ExecState *exec,
168  const ScriptableExtension::FunctionRef &f,
169  bool *ok)
170 {
171  QVariant v = doGet(exec, f.base, f.field, ok);
172  if (*ok) {
173  // See what sort of type we got.
174  if (isForeignObject(v)) {
175  return v.value<ScriptableExtension::Object>();
176  } else if (isFuncRef(v)) {
177  return resolveReferences(exec, v.value<ScriptableExtension::FunctionRef>(), ok);
178  } else {
179  // We got a primitive. We don't care for those.
180  *ok = false;
181  }
182  }
183  return ScriptableExtension::Object();
184 }
185 
186 ScriptableExtension::Object WrapScriptableObject::resolveAnyReferences(ExecState *exec, bool *ok)
187 {
188  ScriptableExtension::Object obj(objExtension.data(), objId);
189 
190  if (type == FunctionRef) {
191  obj = resolveReferences(exec, ScriptableExtension::FunctionRef(obj, field), ok);
192  }
193 
194  if (!obj.owner) {
195  *ok = false;
196  }
197 
198  return obj;
199 }
200 
201 bool WrapScriptableObject::getOwnPropertySlot(ExecState *exec, const Identifier &i,
202  PropertySlot &slot)
203 {
204  bool ok;
205  ScriptableExtension::Object actualObj = resolveAnyReferences(exec, &ok);
206 
207  if (!ok) {
208  return false;
209  }
210 
211  QVariant v = doGet(exec, actualObj, i.qstring(), &ok);
212 
213  if (!ok) {
214  return false;
215  }
216 
217  return getImmediateValueSlot(this, ScriptableOperations::importValue(exec, v, true), slot);
218 }
219 
220 void WrapScriptableObject::put(ExecState *exec, const Identifier &i, JSValue *value, int)
221 {
222  // ### Do we swallow failure, or what?
223  bool ok;
224  ScriptableExtension::Object actualObj = resolveAnyReferences(exec, &ok);
225 
226  if (!ok) {
227  return;
228  }
229 
230  QVariant sv = ScriptableOperations::exportValue(value, true);
231  actualObj.owner->put(principal(exec), actualObj.objId, i.qstring(), sv);
232  ScriptableExtension::releaseValue(sv);
233 }
234 
235 bool WrapScriptableObject::deleteProperty(ExecState *exec, const Identifier &i)
236 {
237  bool ok;
238  ScriptableExtension::Object actualObj = resolveAnyReferences(exec, &ok);
239 
240  if (!ok) {
241  return false;
242  }
243 
244  return actualObj.owner->removeProperty(principal(exec),
245  actualObj.objId, i.qstring());
246 }
247 
248 ScriptableExtension::ArgList WrapScriptableObject::exportArgs(const List &l)
249 {
250  ScriptableExtension::ArgList ol;
251  for (int p = 0; p < l.size(); ++p) {
252  ol.append(ScriptableOperations::exportValue(l.at(p), true));
253  }
254  return ol;
255 }
256 
257 void WrapScriptableObject::releaseArgs(ScriptableExtension::ArgList &a)
258 {
259  for (int p = 0; p < a.size(); ++p) {
260  ScriptableOperations::releaseValue(a[p]);
261  }
262 }
263 
264 JSValue *WrapScriptableObject::callAsFunction(ExecState *exec, JSObject */*thisObj*/, const List &args)
265 {
266  QVariant res;
267 
268  if (ScriptableExtension *base = objExtension.data()) {
269  ScriptableExtension::ArgList sargs = exportArgs(args);
270  if (type == Object) {
271  res = base->callAsFunction(principal(exec), objId, sargs);
272  } else {
273  res = base->callFunctionReference(principal(exec), objId, field, sargs);
274  }
275  releaseArgs(sargs);
276  }
277 
278  // Problem. Throw an exception.
279  if (!res.isValid() || isException(res)) {
280  return throwError(exec, GeneralError, "Call to plugin function failed");
281  } else {
282  return ScriptableOperations::importValue(exec, res, true);
283  }
284 }
285 
286 JSObject *WrapScriptableObject::construct(ExecState *exec, const List &args)
287 {
288  QVariant res;
289 
290  bool ok;
291  ScriptableExtension::Object actualObj = resolveAnyReferences(exec, &ok);
292  if (ok) {
293  ScriptableExtension::ArgList sargs = exportArgs(args);
294  res = actualObj.owner->callAsConstructor(principal(exec), actualObj.objId,
295  sargs);
296  releaseArgs(sargs);
297  }
298 
299  if (!res.isValid() || isException(res)) {
300  return throwError(exec, GeneralError, "Call to plugin ctor failed");
301  } else {
302  JSValue *v = ScriptableOperations::importValue(exec, res, true);
303  return v->toObject(exec);
304  }
305 }
306 
307 void WrapScriptableObject::getOwnPropertyNames(ExecState *exec, PropertyNameArray &a, PropertyMap::PropertyMode mode)
308 {
309  JSObject::getOwnPropertyNames(exec, a, mode);
310 
311  bool ok;
312  ScriptableExtension::Object actualObj = resolveAnyReferences(exec, &ok);
313  if (ok) {
314  QStringList out;
315  if (actualObj.owner->enumerateProperties(principal(exec), actualObj.objId, &out)) {
316  foreach (const QString &s, out) {
317  a.add(Identifier(s));
318  }
319  }
320  }
321 }
322 
323 UString WrapScriptableObject::toString(ExecState *) const
324 {
325  QString iface;
326  if (ScriptableExtension *se = objExtension.data()) {
327  iface = QString::fromLatin1(se->metaObject()->className());
328  } else {
329  iface = QString::fromLatin1("detached");
330  }
331 
332  if (type == FunctionRef) {
333  return QString(QLatin1String("[function ImportedScriptable:") + iface + QLatin1Char('/') + field + QLatin1Char(']'));
334  } else {
335  return QString(QLatin1String("[object ImportedScriptable:") + iface + QLatin1Char(']'));
336  }
337 }
338 
339 ScriptableExtension *WrapScriptableObject::principal(ExecState *exec)
340 {
341  KJS::ScriptInterpreter *si = static_cast<KJS::ScriptInterpreter *>(exec->dynamicInterpreter());
342  KParts::ReadOnlyPart *part = si->part();
343  if (!part) {
344  return nullptr;
345  }
346 
347  return ScriptableExtension::childObject(part);
348 }
349 
350 //-----------------------------------------------------------------------------
351 // conversion stuff
352 
353 /**
354  SECURITY: For the conversion helpers, it is assumed that 'exec' corresponds
355  to the appropriate principal.
356 */
357 
358 JSObject *ScriptableOperations::importObject(ExecState *exec, const QVariant &v, bool alreadyRefd)
359 {
360  ScriptableExtension::Object obj = v.value<ScriptableExtension::Object>();
361  if (JSObject *our = tryGetNativeObject(obj)) {
362  return our;
363  } else {
364  // Create a wrapper, and register the import, this is just for
365  // hashconsing.
366  if (WrapScriptableObject *old = importedObjects()->value(obj)) {
367  if (alreadyRefd) {
368  old->reportRef();
369  }
370  return old;
371  } else {
372  WrapScriptableObject *wrap =
373  new WrapScriptableObject(exec, WrapScriptableObject::Object,
374  obj.owner, obj.objId);
375  importedObjects()->insert(obj, wrap);
376  if (alreadyRefd) {
377  wrap->reportRef();
378  }
379  return wrap;
380  }
381  }
382 }
383 
384 // Note: this is used to convert a full function ref to a value,
385 // which loses the this-binding in the native case. We do keep the pair in the
386 // external case, inside the wrapper, since the method might not have an own
387 // name, and we'd like to be able to refer to it.
388 JSValue *ScriptableOperations::importFunctionRef(ExecState *exec, const QVariant &v, bool /*alreadyRefd*/)
389 {
390  ScriptableExtension::FunctionRef fr = v.value<ScriptableExtension::FunctionRef>();
391 
392  if (JSObject *base = tryGetNativeObject(fr.base)) {
393  return base->get(exec, Identifier(fr.field));
394  } else {
395  if (WrapScriptableObject *old = importedFunctions()->value(fr)) {
396  return old;
397  } else {
398  WrapScriptableObject *wrap =
399  new WrapScriptableObject(exec, WrapScriptableObject::FunctionRef,
400  fr.base.owner, fr.base.objId, fr.field);
401  importedFunctions()->insert(fr, wrap);
402  return wrap;
403  }
404  }
405 }
406 
407 JSValue *ScriptableOperations::importValue(ExecState *exec, const QVariant &v, bool alreadyRefd)
408 {
409  if (v.canConvert<ScriptableExtension::FunctionRef>()) {
410  return importFunctionRef(exec, v, alreadyRefd);
411  }
412  if (v.canConvert<ScriptableExtension::Object>()) {
413  return importObject(exec, v, alreadyRefd);
414  }
415  if (v.canConvert<ScriptableExtension::Null>()) {
416  return jsNull();
417  }
419  return jsUndefined();
420  }
421  if (v.type() == QVariant::Bool) {
422  return jsBoolean(v.toBool());
423  }
424  if (v.type() == QVariant::String) {
425  return jsString(v.toString());
426  }
427  if (v.canConvert<double>()) {
428  return jsNumber(v.toDouble());
429  }
430  qCWarning(KHTML_LOG) << "conversion from " << v << "failed";
431  return jsNull();
432 }
433 
434 List ScriptableOperations::importArgs(ExecState *exec, const ArgList &args)
435 {
436  // Args are not pre-ref'd for us..
437  List out;
438  for (int i = 0; i < args.size(); ++i) {
439  out.append(importValue(exec, args[i], false));
440  }
441  return out;
442 }
443 
444 ScriptableExtension::Object ScriptableOperations::exportNativeObject(JSObject *o, bool preRef)
445 {
446  assert(!o->inherits(&WrapScriptableObject::info));
447  // We're exporting our own. Add to export table if needed.
448  // Use the pointer as an ID.
449  if (!exportedObjects()->contains(o)) {
450  exportedObjects()->insert(o, 0);
451  }
452  if (preRef) {
453  ++(*exportedObjects())[o];
454  }
455 
456  return ScriptableExtension::Object(ScriptableOperations::self(),
457  reinterpret_cast<quint64>(o));
458 }
459 
460 QVariant ScriptableOperations::exportObject(JSObject *o, bool preRef)
461 {
462  // XSS checks are done at get time, so if we have a value here, we can
463  // export it.
464  if (o->inherits(&WrapScriptableObject::info)) {
465  // Re-exporting external one. That's easy.
466  WrapScriptableObject *wo = static_cast<WrapScriptableObject *>(o);
467  if (ScriptableExtension *owner = wo->objExtension.data()) {
468  QVariant v = QVariant::fromValue(ScriptableExtension::Object(owner, wo->objId));
469  if (preRef) {
470  acquireValue(v);
471  }
472  return v;
473  } else {
474  qCWarning(KHTML_LOG) << "export of an object of a destroyed extension. Returning null";
475  return scriptableNull();
476  }
477  } else {
478  return QVariant::fromValue(exportNativeObject(o, preRef));
479  }
480 }
481 
482 QVariant ScriptableOperations::exportFuncRef(JSObject *base, const QString &field, bool preRef)
483 {
484  ScriptableExtension::Object exportBase = exportNativeObject(base, preRef);
485  return QVariant::fromValue(ScriptableExtension::FunctionRef(exportBase, field));
486 }
487 
488 QVariant ScriptableOperations::exportValue(JSValue *v, bool preRef)
489 {
490  switch (v->type()) {
491  case NumberType:
492  return QVariant::fromValue(v->getNumber());
493  case BooleanType:
494  return QVariant::fromValue(v->getBoolean());
495  case NullType:
496  return QVariant::fromValue(ScriptableExtension::Null());
497  case StringType:
498  return QVariant::fromValue(v->getString().qstring());
499  case ObjectType:
500  return exportObject(v->getObject(), preRef);
501  case UndefinedType:
502  default:
504  }
505 }
506 
507 //-----------------------------------------------------------------------------
508 // operations
509 QHash<JSObject *, int> *ScriptableOperations::s_exportedObjects = nullptr;
510 QHash<ScriptableExtension::Object, WrapScriptableObject *> *ScriptableOperations::s_importedObjects = nullptr;
511 QHash<ScriptableExtension::FunctionRef, WrapScriptableObject *> *ScriptableOperations::s_importedFunctions = nullptr;
512 ScriptableOperations *ScriptableOperations::s_instance = nullptr;
513 
514 QHash<ScriptableExtension::Object, WrapScriptableObject *> *ScriptableOperations::importedObjects()
515 {
516  if (!s_importedObjects) {
517  s_importedObjects = new QHash<Object, WrapScriptableObject *>;
518  }
519  return s_importedObjects;
520 }
521 
522 QHash<ScriptableExtension::FunctionRef, WrapScriptableObject *> *ScriptableOperations::importedFunctions()
523 {
524  if (!s_importedFunctions) {
525  s_importedFunctions = new QHash<FunctionRef, WrapScriptableObject *>();
526  }
527  return s_importedFunctions;
528 }
529 
530 // A little helper for marking exportedObjects. We need this since we have
531 // an unclear runtime with respect to interpreter.
532 class ScriptableOperationsMarker: public JSObject
533 {
534 public:
535  void mark() override
536  {
537  JSObject::mark();
538  ScriptableOperations::self()->mark();
539  }
540 };
541 
542 QHash<JSObject *, int> *ScriptableOperations::exportedObjects()
543 {
544  if (!s_exportedObjects) {
545  s_exportedObjects = new QHash<JSObject *, int>;
546 
547  gcProtect(new ScriptableOperationsMarker());
548  }
549  return s_exportedObjects;
550 }
551 
552 ScriptableOperations *ScriptableOperations::self()
553 {
554  if (!s_instance) {
555  s_instance = new ScriptableOperations;
556  }
557  return s_instance;
558 }
559 
560 ScriptableOperations::ScriptableOperations(): ScriptableExtension(nullptr)
561 {}
562 
563 ScriptableOperations::~ScriptableOperations()
564 {
565  assert(false);
566 }
567 
568 JSObject *ScriptableOperations::tryGetNativeObject(const Object &sObj)
569 {
570  if (ScriptableOperations *o = qobject_cast<ScriptableOperations *>(sObj.owner)) {
571  return o->objectForId(sObj.objId);
572  } else {
573  return nullptr;
574  }
575 }
576 
577 QVariant ScriptableOperations::handleReturn(ExecState *exec, JSValue *v)
578 {
579  if (exec->hadException()) {
580  JSValue *e = exec->exception();
581  exec->clearException();
582 
583  QString msg = QLatin1String("KJS exception");
584 
585  if (JSObject *eo = e->getObject()) {
586  JSValue *msgVal = eo->get(exec, exec->propertyNames().message);
587  if (!msgVal->isUndefined()) {
588  msg = msgVal->toString(exec).qstring();
589  }
590 
591  // in case the get failed too.
592  exec->clearException();
593  }
594 
595  return QVariant::fromValue(ScriptableExtension::Exception(msg));
596  }
597 
598  return exportValue(v, true);
599 }
600 
601 QVariant ScriptableOperations::callAsFunction(ScriptableExtension *caller,
602  quint64 objId, const ArgList &args)
603 {
604  ExecState *exec = execStateForPrincipal(caller);
605  if (!exec) {
606  return exception("No scripting context or frame");
607  }
608 
609  JSObject *fn = objectForId(objId);
610  if (!fn || !fn->implementsCall()) {
611  return exception("Call on a non-object or non-calleable");
612  }
613 
614  JSValue *res = fn->callAsFunction(exec, exec->dynamicInterpreter()->globalObject(),
615  importArgs(exec, args));
616  return handleReturn(exec, res);
617 }
618 
619 QVariant ScriptableOperations::callAsConstructor(ScriptableExtension *caller, quint64 objId, const ArgList &args)
620 {
621  ExecState *exec = execStateForPrincipal(caller);
622  if (!exec) {
623  return exception("No scripting context or frame");
624  }
625 
626  JSObject *fn = objectForId(objId);
627  if (!fn || !fn->implementsConstruct()) {
628  return exception("new on a non-constructor");
629  }
630 
631  JSValue *res = fn->construct(exec, importArgs(exec, args));
632  return handleReturn(exec, res);
633 }
634 
635 QVariant ScriptableOperations::callFunctionReference(ScriptableExtension *caller,
636  quint64 objId, const QString &f, const ArgList &args)
637 {
638  ExecState *exec = execStateForPrincipal(caller);
639 
640  if (!exec) {
641  return exception("No scripting context or frame");
642  }
643 
644  JSObject *base = objectForId(objId);
645  if (!base) {
646  return exception("Call with an invalid base");
647  }
648 
649  JSValue *kid = base->get(exec, Identifier(f));
650  if (!kid->isObject() || exec->hadException() || !kid->getObject()->implementsCall()) {
651  exec->clearException();
652  return exception("Reference did not resolve to a function");
653  }
654 
655  // Whee..
656  JSValue *res = kid->getObject()->callAsFunction(exec, base, importArgs(exec, args));
657  return handleReturn(exec, res);
658 }
659 
660 bool ScriptableOperations::hasProperty(ScriptableExtension *caller, quint64 objId, const QString &propName)
661 {
662  ExecState *exec = execStateForPrincipal(caller);
663  if (!exec) {
664  exception("No scripting context or frame");
665  return false;
666  }
667 
668  JSObject *o = objectForId(objId);
669  if (!o) {
670  exception("hasProperty on a non-object");
671  return false;
672  }
673 
674  return o->hasProperty(exec, Identifier(propName));
675 }
676 
677 QVariant ScriptableOperations::get(ScriptableExtension *caller, quint64 objId, const QString &propName)
678 {
679  ExecState *exec = execStateForPrincipal(caller);
680  if (!exec) {
681  return exception("No scripting context or frame");
682  }
683 
684  JSObject *o = objectForId(objId);
685  if (!o) {
686  return exception("get on a non-object");
687  }
688 
689  JSValue *v = o->get(exec, Identifier(propName));
690  if (!exec->hadException() && v->isObject() && v->getObject()->implementsCall()) {
691  // For a function we got OK, return a reference.
692  return exportFuncRef(o, propName, true);
693  } else {
694  // straight return for other stuff or failure
695  return handleReturn(exec, v);
696  }
697 }
698 
699 bool ScriptableOperations::put(ScriptableExtension *caller, quint64 objId,
700  const QString &propName, const QVariant &value)
701 {
702  ExecState *exec = execStateForPrincipal(caller);
703  if (!exec) {
704  return false; // ### debug warn?
705  }
706 
707  JSObject *o = objectForId(objId);
708  if (!o) {
709  return false;
710  }
711 
712  o->put(exec, Identifier(propName), importValue(exec, value, false));
713  if (!exec->hadException()) {
714  return true;
715  } else {
716  exec->clearException();
717  return false;
718  }
719 }
720 
721 bool ScriptableOperations::removeProperty(ScriptableExtension *caller,
722  quint64 objId, const QString &propName)
723 {
724  ExecState *exec = execStateForPrincipal(caller);
725  if (!exec) {
726  return false; // ### debug warn?
727  }
728 
729  JSObject *o = objectForId(objId);
730  if (!o) {
731  return false;
732  }
733 
734  bool ok = o->deleteProperty(exec, Identifier(propName));
735  if (exec->hadException()) {
736  exec->clearException();
737  ok = false;
738  }
739  return ok;
740 }
741 
742 bool ScriptableOperations::enumerateProperties(ScriptableExtension *caller,
743  quint64 objId, QStringList *result)
744 {
745  ExecState *exec = execStateForPrincipal(caller);
746  if (!exec) {
747  return false; // ### debug warn?
748  }
749 
750  JSObject *o = objectForId(objId);
751  if (!o) {
752  return false;
753  }
754 
755  PropertyNameArray pa;
756  o->getPropertyNames(exec, pa);
757 
758  for (int i = 0; i < pa.size(); ++i) {
759  result->append(pa[i].qstring());
760  }
761  return true;
762 }
763 
764 KHTMLPart *ScriptableOperations::partForPrincipal(ScriptableExtension *caller)
765 {
766  // We implement our security checks by delegating to the KHTMLPart corresponding
767  // to the given plugin's principal (which is the KHTMLPart owning it), and letting
768  // the underlying implementation perform them (which it has to anyway)
769 
770  if (KHTMLPartScriptable *o = qobject_cast<KHTMLPartScriptable *>(caller)) {
771  return o->m_part;
772  } else {
773  // We always set the host on child extensions.
774  return partForPrincipal(caller->host());
775  }
776 }
777 
778 ExecState *ScriptableOperations::execStateForPrincipal(ScriptableExtension *caller)
779 {
780  KHTMLPart *part = partForPrincipal(caller);
781 
782  if (!part) {
783  return nullptr;
784  }
785 
786  KJSProxy *proxy = KJSProxy::proxy(part);
787  if (!proxy) {
788  return nullptr;
789  }
790 
791  KJS::Interpreter *i = proxy->interpreter();
792  if (!i) {
793  return nullptr;
794  }
795 
796  return i->globalExec();
797 }
798 
799 void ScriptableOperations::acquire(quint64 objId)
800 {
801  JSObject *ptr = objectForId(objId);
802 
803  if (ptr) {
804  ++(*exportedObjects())[ptr];
805  } else {
806  assert(false);
807  }
808 }
809 
810 void ScriptableOperations::release(quint64 objId)
811 {
812  JSObject *ptr = objectForId(objId);
813 
814  if (ptr) {
815  int newRC = --(*exportedObjects())[ptr];
816  if (newRC == 0) {
817  exportedObjects()->remove(ptr);
818  }
819  } else {
820  assert(false);
821  }
822 }
823 
824 JSObject *ScriptableOperations::objectForId(quint64 objId)
825 {
826  JSObject *ptr = reinterpret_cast<JSObject *>(objId);
827 
828  // Verify the pointer against the exports table for paranoia.
829  if (exportedObjects()->contains(ptr)) {
830  return ptr;
831  } else {
832  assert(false);
833  return nullptr;
834  }
835 }
836 
837 void ScriptableOperations::mark()
838 {
839  QHash<JSObject *, int> *exp = exportedObjects();
840 
841  for (QHash<JSObject *, int>::iterator i = exp->begin(); i != exp->end(); ++i) {
842  JSObject *o = i.key();
843  if (i.value() && !o->marked()) {
844  o->mark();
845  }
846  }
847 }
848 
849 //-----------------------------------------------------------------------------
850 // per-part stuff.
851 
852 KHTMLPartScriptable::KHTMLPartScriptable(KHTMLPart *part):
853  ScriptableExtension(part), m_part(part)
854 {
855 }
856 
857 KJS::Interpreter *KHTMLPartScriptable::interpreter()
858 {
859  KJSProxy *proxy = KJSProxy::proxy(m_part);
860  if (!proxy) {
861  return nullptr;
862  }
863 
864  return proxy->interpreter();
865 }
866 
867 QVariant KHTMLPartScriptable::rootObject()
868 {
869  if (KJS::Interpreter *i = interpreter()) {
870  return ScriptableOperations::exportObject(i->globalObject(), true);
871  }
872 
873  return scriptableNull();
874 }
875 
876 QVariant KHTMLPartScriptable::encloserForKid(KParts::ScriptableExtension *kid)
877 {
878  ReadOnlyPart *childPart = qobject_cast<ReadOnlyPart *>(kid->parent());
879 
880  KJS::Interpreter *i = interpreter();
881  if (!childPart || !i) {
882  return scriptableNull();
883  }
884 
885  khtml::ChildFrame *f = m_part->frame(childPart);
886 
887  if (!f) {
888  qCWarning(KHTML_LOG) << "unable to find frame. Huh?";
889  return scriptableNull();
890  }
891 
892  // ### should this deal with fake window objects for iframes?
893  // ### this should never actually get an iframe once iframes are fixed
894  if (!f->m_partContainerElement.isNull()) {
895  return ScriptableOperations::exportValue(
896  getDOMNode(i->globalExec(), f->m_partContainerElement.data()), true);
897  }
898 
899  qCWarning(KHTML_LOG) << "could not find the part container";
900  return scriptableNull();
901 }
902 
903 // For paranoia: forward to ScriptOperations
904 void KHTMLPartScriptable::acquire(quint64 objid)
905 {
906  ScriptableOperations::self()->acquire(objid);
907 }
908 
909 void KHTMLPartScriptable::release(quint64 objid)
910 {
911  ScriptableOperations::self()->release(objid);
912 }
913 
914 QVariant KHTMLPartScriptable::evaluateScript(ScriptableExtension *caller,
915  quint64 contextObjectId,
916  const QString &code,
917  ScriptLanguage lang)
918 {
919  // qCDebug(KHTML_LOG) << code;
920 
921  if (lang != ECMAScript) {
922  return exception("unsupported language");
923  }
924 
925  KHTMLPart *callingHtmlPart = ScriptableOperations::partForPrincipal(caller);
926  if (!callingHtmlPart) {
927  return exception("failed to resolve principal");
928  }
929 
930  // Figure out the object we want to access, and its corresponding part.
931  JSObject *o = ScriptableOperations::objectForId(contextObjectId);
932  if (!o) {
933  return exception("invalid object");
934  }
935 
936  DOM::NodeImpl *node = toNode(o);
937 
938  // Presently, we only permit node contexts here.
939  // ### TODO: window contexts?
940  if (!node) {
941  return exception("non-Node context");
942  }
943 
944  KHTMLPart *targetPart = node->document()->part();
945 
946  if (!targetPart) {
947  return exception("failed to resolve destination principal");
948  }
949 
950  if (!targetPart->checkFrameAccess(callingHtmlPart)) {
951  return exception("XSS check failed");
952  }
953 
954  // wheee..
955  targetPart->executeScript(DOM::Node(node), code);
956 
957  // ### TODO: Return value. Which is a completely different kind of QVariant.
958  // might just want to go to kJSProxy directly
959 
960  return scriptableNull();
961 }
962 
963 bool KHTMLPartScriptable::isScriptLanguageSupported(ScriptLanguage lang) const
964 {
965  return lang == ECMAScript;
966 }
967 
968 bool KHTMLPartScriptable::setException(ScriptableExtension * /*caller*/,
969  const QString &message)
970 {
971  qCWarning(KHTML_LOG) << "ignoring:" << message;
972  return false;
973 }
974 
975 } // namespace KJS
976 
bool canConvert(int targetTypeId) const const
KJS_EXTERNAL_EXPORT QString qstring() const
The Node interface is the primary datatype for the entire Document Object Model.
Definition: dom_node.h:278
virtual ExecState * globalExec()
virtual QVariant rootObject()
virtual void mark(bool currentThreadIsMainThread)
ScriptableExtension * host() const
Proxy class serving as interface when being dlopen&#39;ed.
Definition: kjs_proxy.h:61
This class is khtml&#39;s main class.
Definition: khtml_part.h:208
T value() const const
virtual void acquire(quint64 objid)
We inherit from Interpreter, to save a pointer to the HTML part that the interpreter runs for...
Definition: kjs_binding.h:96
void append(const T &value)
Type type(const QSqlDatabase &db)
QHash::iterator begin()
QVariant fromValue(const T &value)
QVariant executeScript(const DOM::Node &n, const QString &script)
Same as executeScript( const QString & ) except with the Node parameter specifying the &#39;this&#39; value...
bool toBool() const const
ObjectType
QString fromLatin1(const char *str, int size)
bool isValid() const const
double toDouble(bool *ok) const const
DOM::Document document() const
Returns a reference to the DOM document.
QHash::iterator end()
QVariant::Type type() const const
QObject * parent() const const
QString toString() const const
JSGlobalObject * globalObject() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Oct 25 2021 22:48:18 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.