summaryrefslogtreecommitdiffstats
path: root/tdehtml/ecma/kjs_binding.h
blob: b6d60348efda6936819b4ce088dcfdfc5698c482 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
/*
 *  This file is part of the KDE libraries
 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
 *  Copyright (C) 2003 Apple Computer, Inc.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifndef _KJS_BINDING_H_
#define _KJS_BINDING_H_

#include <kjs/interpreter.h>
#include <kjs/function_object.h> /// for FunctionPrototypeImp

#include <dom/dom_node.h>
#include <tqvariant.h>
#include <tqptrdict.h>
#include <kurl.h>
#include <kjs/lookup.h>

namespace KParts {
  class ReadOnlyPart;
  class LiveConnectExtension;
}

namespace tdehtml {
  class ChildFrame;
}

namespace KJS {

  /**
   * Base class for all objects in this binding - get() and put() run
   * tryGet() and tryPut() respectively, and catch exceptions if they
   * occur.
   */
  class DOMObject : public ObjectImp {
  public:
    DOMObject(const Object &proto) : ObjectImp(proto) {}
    virtual Value get(ExecState *exec, const Identifier &propertyName) const;
    virtual Value tryGet(ExecState *exec, const Identifier &propertyName) const
      { return ObjectImp::get(exec, propertyName); }
    virtual bool implementsHasInstance() const { return true; }
    virtual Boolean hasInstance(ExecState *exec, const Value &value);
    virtual void put(ExecState *exec, const Identifier &propertyName,
                     const Value &value, int attr = None);
    virtual void tryPut(ExecState *exec, const Identifier &propertyName,
                        const Value& value, int attr = None);

    virtual UString toString(ExecState *exec) const;
  };

  /**
   * Base class for all functions in this binding - get() and call() run
   * tryGet() and tryCall() respectively, and catch exceptions if they
   * occur.
   */
  class DOMFunction : public InternalFunctionImp {
  public:
    DOMFunction(ExecState* exec) : InternalFunctionImp(
      static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
      ) {}
    virtual Value get(ExecState *exec, const Identifier &propertyName) const;
    virtual Value tryGet(ExecState *exec, const Identifier &propertyName) const
      { return ObjectImp::get(exec, propertyName); }

    virtual bool implementsCall() const { return true; }
    virtual Value call(ExecState *exec, Object &thisObj, const List &args);

    virtual Value tryCall(ExecState *exec, Object &thisObj, const List&args)
      { return ObjectImp::call(exec, thisObj, args); }
    virtual bool toBoolean(ExecState *) const { return true; }
  };

  /**
   * We inherit from Interpreter, to save a pointer to the HTML part
   * that the interpreter runs for.
   * The interpreter also stores the DOM object - >KJS::DOMObject cache.
   */
  class KDE_EXPORT ScriptInterpreter : public Interpreter
  {
  public:
    ScriptInterpreter( const Object &global, tdehtml::ChildFrame* frame );
    virtual ~ScriptInterpreter();

    DOMObject* getDOMObject( void* objectHandle ) const {
      return m_domObjects[objectHandle];
    }
    void putDOMObject( void* objectHandle, DOMObject* obj ) {
      m_domObjects.insert( objectHandle, obj );
    }
    void customizedDOMObject( DOMObject* obj ) {
      m_customizedDomObjects.replace( obj, this );
    }
    bool deleteDOMObject( void* objectHandle ) {
      DOMObject* obj = m_domObjects.take( objectHandle );
      if (obj) {
        m_customizedDomObjects.remove( obj );
        return true;
      }
      else
        return false;
    }
    void clear() {
      m_customizedDomObjects.clear();
      m_domObjects.clear();
    }
    /**
     * Static method. Makes all interpreters forget about the object
     */
    static void forgetDOMObject( void* objectHandle );

    /**
     * Mark objects in the DOMObject cache.
     */
    virtual void mark();
    KParts::ReadOnlyPart* part() const;

    virtual int rtti() { return 1; }

    /**
     * Set the event that is triggering the execution of a script, if any
     */
    void setCurrentEvent( DOM::Event *evt ) { m_evt = evt; }
    void setInlineCode( bool inlineCode ) { m_inlineCode = inlineCode; }
    void setProcessingTimerCallback( bool timerCallback ) { m_timerCallback = timerCallback; }
    /**
     * "Smart" window.open policy
     */
    bool isWindowOpenAllowed() const;

  private:
    tdehtml::ChildFrame* m_frame;
    TQPtrDict<DOMObject> m_domObjects;
    TQPtrDict<void> m_customizedDomObjects; //Objects which had custom properties set,
                                           //and should not be GC'd. key is DOMObject*
    DOM::Event *m_evt;
    bool m_inlineCode;
    bool m_timerCallback;
  };
  /**
   * Retrieve from cache, or create, a KJS object around a DOM object
   */
  template<class DOMObj, class KJSDOMObj>
  inline Value cacheDOMObject(ExecState *exec, DOMObj domObj)
  {
    DOMObject *ret;
    if (domObj.isNull())
      return Null();
    ScriptInterpreter* interp = static_cast<ScriptInterpreter *>(exec->interpreter());
    if ((ret = interp->getDOMObject(domObj.handle())))
      return Value(ret);
    else {
      ret = new KJSDOMObj(exec, domObj);
      interp->putDOMObject(domObj.handle(),ret);
      return Value(ret);
    }
  }

  /**
   * Convert an object to a Node. Returns a null Node if not possible.
   */
  DOM::Node toNode(const Value&);
  /**
   *  Get a String object, or Null() if s is null
   */
  Value getString(DOM::DOMString s);

  /**
   * Convery a KJS value into a QVariant
   */
  TQVariant ValueToVariant(ExecState* exec, const Value& val);

  /**
   * We need a modified version of lookupGet because
   * we call tryGet instead of get, in DOMObjects.
   */
  template <class FuncImp, class ThisImp, class ParentImp>
  inline Value DOMObjectLookupGet(ExecState *exec, const Identifier &propertyName,
                                  const HashTable* table, const ThisImp* thisObj)
  {
    const HashEntry* entry = Lookup::findEntry(table, propertyName);

    if (!entry) // not found, forward to parent
      return thisObj->ParentImp::tryGet(exec, propertyName);

    if (entry->attr & Function) {
      return lookupOrCreateFunction<FuncImp>(exec, propertyName, thisObj, entry->value, entry->params, entry->attr);
    }
    return thisObj->getValueProperty(exec, entry->value);
  }

  /**
   * Simplified version of DOMObjectLookupGet in case there are no
   * functions, only "values".
   */
  template <class ThisImp, class ParentImp>
  inline Value DOMObjectLookupGetValue(ExecState *exec, const Identifier &propertyName,
                                       const HashTable* table, const ThisImp* thisObj)
  {
    const HashEntry* entry = Lookup::findEntry(table, propertyName);

    if (!entry) // not found, forward to parent
      return thisObj->ParentImp::tryGet(exec, propertyName);

    if (entry->attr & Function)
      fprintf(stderr, "Function bit set! Shouldn't happen in lookupValue!\n" );
    return thisObj->getValueProperty(exec, entry->value);
  }

  /**
   * We need a modified version of lookupPut because
   * we call tryPut instead of put, in DOMObjects.
   */
  template <class ThisImp, class ParentImp>
  inline void DOMObjectLookupPut(ExecState *exec, const Identifier &propertyName,
                                 const Value& value, int attr,
                                 const HashTable* table, ThisImp* thisObj)
  {
    const HashEntry* entry = Lookup::findEntry(table, propertyName);

    if (!entry) // not found: forward to parent
      thisObj->ParentImp::tryPut(exec, propertyName, value, attr);
    else if (entry->attr & Function) // function: put as override property
      thisObj->ObjectImp::put(exec, propertyName, value, attr);
    else if (entry->attr & ReadOnly) // readonly! Can't put!
#ifdef KJS_VERBOSE
      fprintf(stderr,"WARNING: Attempt to change value of readonly property '%s'\n",propertyName.ascii());
#else
    ; // do nothing
#endif
    else
      thisObj->putValueProperty(exec, entry->value, value, attr);
  }
  
// Versions of prototype functions that properly support instanceof,
// and are compatible with trunk.
#define KJS_DEFINE_PROTOTYPE_IMP(ClassProto,ProtoCode) \
  class ClassProto : public ObjectImp { \
  friend Object cacheGlobalObject<ClassProto>(ExecState *exec, const Identifier &propertyName); \
  public: \
    static Object self(ExecState *exec); \
    virtual const ClassInfo *classInfo() const { return &info; } \
    static const ClassInfo info; \
    Value get(ExecState *exec, const Identifier &propertyName) const; \
  protected: \
    ClassProto( ExecState *exec ) \
      : ObjectImp( ProtoCode ) {} \
    \
    static Identifier* s_name; \
    static Identifier* name(); \
  };

#define KJS_DEFINE_PROTOTYPE(ClassProto) \
        KJS_DEFINE_PROTOTYPE_IMP(ClassProto, exec->interpreter()->builtinObjectPrototype())

#define KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE(ClassProto, ClassProtoProto) \
            KJS_DEFINE_PROTOTYPE_IMP(ClassProto, ClassProtoProto::self(exec))

#define KJS_EMPTY_PROTOTYPE_IMP(ClassName, ClassProto, ProtoCode) \
      class ClassProto : public ObjectImp { \
      friend Object cacheGlobalObject<ClassProto>(ExecState *exec, const Identifier &propertyName); \
      public: \
        static Object self(ExecState *exec) { \
          return cacheGlobalObject<ClassProto>(exec, *name()); \
        } \
        virtual const ClassInfo *classInfo() const { return &info; } \
        static const ClassInfo info; \
      protected: \
        ClassProto( ExecState *exec ) \
          : ObjectImp( ProtoCode ) {} \
        \
        static Identifier* s_name; \
        static Identifier* name() { \
            if (!s_name) s_name = new Identifier("[[" ClassName ".prototype]]"); \
            return s_name; \
        }\
      }; \
      Identifier* ClassProto::s_name = 0; \
      const ClassInfo ClassProto::info = { ClassName, 0, 0, 0 };
      
#define KJS_EMPTY_PROTOTYPE_WITH_PROTOTYPE(ClassName, ClassProto, ClassProtoProto) \
  KJS_EMPTY_PROTOTYPE_IMP(ClassName, ClassProto, ClassProtoProto::self(exec))

//### this doesn't implement hasProperty, but stuff in lookup.h didn't 
//either (just did the forward)
#define KJS_IMPLEMENT_PROTOTYPE(ClassName, ClassProto, ClassFunc) \
    const ClassInfo ClassProto::info = { ClassName, 0, &ClassProto##Table, 0 }; \
    Identifier* ClassProto::s_name = 0; \
    Object ClassProto::self(ExecState *exec) \
    { \
      return cacheGlobalObject<ClassProto>(exec, *name()); \
    } \
    Value ClassProto::get(ExecState *exec, const Identifier &propertyName) const \
    { \
      /*fprintf( stderr, "%sProto::get(%s) [in macro, no parent]\n", info.className, propertyName.ascii());*/ \
      return lookupGetFunction<ClassFunc,ObjectImp>(exec, propertyName, &ClassProto##Table, this ); \
    } \
    Identifier* ClassProto::name() \
    { \
      if (!s_name) s_name = new Identifier("[[" ClassName ".prototype]]"); \
      return s_name; \
    } 
    
  // Modified version of IMPLEMENT_PROTOFUNC, to use DOMFunction and tryCall
#define IMPLEMENT_PROTOFUNC_DOM(ClassFunc) \
  class ClassFunc : public DOMFunction { \
  public: \
    ClassFunc(ExecState *exec, int i, int len) \
       : DOMFunction( exec ), id(i) { \
       Value protect(this); \
       put(exec,lengthPropertyName,Number(len),DontDelete|ReadOnly|DontEnum); \
    } \
    /** You need to implement that one */ \
    virtual Value tryCall(ExecState *exec, Object &thisObj, const List &args); \
  private: \
    int id; \
  }; 

  Value getLiveConnectValue(KParts::LiveConnectExtension *lc, const TQString & name, const int type, const TQString & value, int id);


// This is used to create pseudo-constructor objects, like Mozillaish
// Element, HTMLDocument, etc., which do not act like real constructors,
// but do have the prototype property pointing to prototype of "instances"
#define DEFINE_PSEUDO_CONSTRUCTOR(ClassName) \
  class ClassName : public DOMObject { \
      public: \
          ClassName(ExecState *); \
          virtual const ClassInfo* classInfo() const { return &info; } \
          static const ClassInfo info; \
          static Object self(ExecState *exec); \
  };

#define IMPLEMENT_PSEUDO_CONSTRUCTOR_IMP(Class,ClassName,ProtoClass,ParentProto) \
    const ClassInfo Class::info = { ClassName, 0, 0, 0 }; \
    Class::Class(ExecState* exec): DOMObject(ParentProto) {\
        Object proto = ProtoClass::self(exec); \
        putDirect(prototypePropertyName, proto.imp(), DontDelete|ReadOnly); \
    }\
    Object Class::self(ExecState *exec) { \
        return Object(cacheGlobalObject<Class>(exec, "[[" ClassName ".constructor]]")); \
    }

#define IMPLEMENT_PSEUDO_CONSTRUCTOR(Class,ClassName,ProtoClass) \
    IMPLEMENT_PSEUDO_CONSTRUCTOR_IMP(Class,ClassName,ProtoClass,exec->lexicalInterpreter()->builtinObjectPrototype())

#define IMPLEMENT_PSEUDO_CONSTRUCTOR_WITH_PARENT(Class,ClassName,ProtoClass,ParentProtoClass) \
    IMPLEMENT_PSEUDO_CONSTRUCTOR_IMP(Class,ClassName,ProtoClass,ParentProtoClass::self(exec))

// This declares a constant table, which merely maps everything in its 
// table to its token value. Can be used as a prototype
#define DEFINE_CONSTANT_TABLE(Class) \
   class Class : public DOMObject { \
   public: \
     Class(ExecState *exec): DOMObject(exec->lexicalInterpreter()->builtinObjectPrototype()) {} \
     \
     virtual Value tryGet(ExecState *exec, const Identifier &propertyName) const;\
     Value  getValueProperty(ExecState * /*exec*/, int token) const; \
     virtual const ClassInfo* classInfo() const { return &info; } \
     static const ClassInfo info; \
     static Object self(ExecState *exec);\
     static Identifier* s_name; \
     static Identifier* name(); \
   };

// Emits an implementation of a constant table 
#define IMPLEMENT_CONSTANT_TABLE(Class,ClassName) \
    Value Class::tryGet(ExecState *exec, const Identifier &propertyName) const { \
        return DOMObjectLookupGetValue<Class, DOMObject>(exec, propertyName, &Class##Table, this);\
    } \
    Value Class::getValueProperty(ExecState * /*exec*/, int token) const { \
        /* We use the token as the value to return directly*/ \
        return Number((unsigned int)token); \
    }  \
    Object Class::self(ExecState *exec) { \
        return cacheGlobalObject<Class>(exec, *name()); \
    } \
    Identifier* Class::s_name = 0; \
    Identifier* Class::name() { \
        if (!s_name) s_name = new Identifier("[[" ClassName ".constant_table]]"); \
        return s_name; \
    } \
    const ClassInfo Class::info = { ClassName, 0, &Class##Table, 0 };

   
// Hide some of the stuff in lookup.h..
#undef PUBLIC_DEFINE_PROTOTYPE
#undef DEFINE_PROTOTYPE
#undef IMPLEMENT_PROTOTYPE
#undef PUBLIC_IMPLEMENT_PROTOTYPE
#undef IMPLEMENT_PROTOTYPE_WITH_PARENT

} // namespace

#endif