60157365774b1f23ecc8b8148bbc52b3cc879238
[WebKit-https.git] / WebCore / khtml / ecma / kjs_binding.h
1 // -*- c-basic-offset: 2 -*-
2 /*
3  *  This file is part of the KDE libraries
4  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
5  *  Copyright (C) 2003 Apple Computer, Inc.
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #ifndef _KJS_BINDING_H_
23 #define _KJS_BINDING_H_
24
25 #include <kjs/interpreter.h>
26 #include <qvariant.h>
27 #include <qptrdict.h>
28 #include <kjs/lookup.h>
29 #include <kjs/protect.h>
30
31 #if APPLE_CHANGES
32 #include <JavaScriptCore/runtime.h>
33 #endif
34
35 class KHTMLPart;
36
37 namespace DOM {
38     class DocumentImpl;
39     class EventImpl;
40     class NodeImpl;
41 }
42
43 namespace KJS {
44
45   /**
46    * Base class for all objects in this binding - get() and put() run
47    * tryGet() and tryPut() respectively, and catch exceptions if they
48    * occur.
49    */
50   class DOMObject : public ObjectImp {
51   public:
52     DOMObject() : ObjectImp() {}
53     virtual Value get(ExecState *exec, const Identifier &propertyName) const;
54     virtual Value tryGet(ExecState *exec, const Identifier &propertyName) const
55       { return ObjectImp::get(exec, propertyName); }
56
57     virtual void put(ExecState *exec, const Identifier &propertyName,
58                      const Value &value, int attr = None);
59     virtual void tryPut(ExecState *exec, const Identifier &propertyName,
60                         const Value& value, int attr = None)
61       { ObjectImp::put(exec,propertyName,value,attr); }
62
63     virtual UString toString(ExecState *exec) const;
64   };
65
66   /**
67    * Base class for all functions in this binding - get() and call() run
68    * tryGet() and tryCall() respectively, and catch exceptions if they
69    * occur.
70    */
71   class DOMFunction : public ObjectImp {
72   public:
73     DOMFunction() : ObjectImp( /* proto? */ ) {}
74     virtual Value get(ExecState *exec, const Identifier &propertyName) const;
75     virtual Value tryGet(ExecState *exec, const Identifier &propertyName) const
76       { return ObjectImp::get(exec, propertyName); }
77
78     virtual bool implementsCall() const { return true; }
79     virtual Value call(ExecState *exec, Object &thisObj, const List &args);
80
81     virtual Value tryCall(ExecState *exec, Object &thisObj, const List&args)
82       { return ObjectImp::call(exec, thisObj, args); }
83     virtual bool toBoolean(ExecState *) const { return true; }
84     virtual Value toPrimitive(ExecState *exec, Type) const { return String(toString(exec)); }
85     virtual UString toString(ExecState *) const { return UString("[function]"); }
86   };
87
88   class DOMNode;
89
90   /**
91    * We inherit from Interpreter, to save a pointer to the HTML part
92    * that the interpreter runs for.
93    * The interpreter also stores the DOM object - >KJS::DOMObject cache.
94    */
95   class ScriptInterpreter : public Interpreter
96   {
97   public:
98     ScriptInterpreter( const Object &global, KHTMLPart* part );
99     virtual ~ScriptInterpreter();
100
101     static DOMObject* getDOMObject( void* objectHandle ) {
102       return domObjects()[objectHandle];
103     }
104     static void putDOMObject( void* objectHandle, DOMObject* obj ) {
105       domObjects().insert( objectHandle, obj );
106     }
107     static bool deleteDOMObject( void* objectHandle ) {
108       return domObjects().remove( objectHandle );
109     }
110
111     static void forgetDOMObject( void* objectHandle );
112
113
114     static DOMNode *getDOMNodeForDocument(DOM::DocumentImpl *document, DOM::NodeImpl *node);
115     static void putDOMNodeForDocument(DOM::DocumentImpl *document, DOM::NodeImpl *nodeHandle, DOMNode *nodeWrapper);
116     static void forgetDOMNodeForDocument(DOM::DocumentImpl *document, DOM::NodeImpl *node);
117     static void forgetAllDOMNodesForDocument(DOM::DocumentImpl *document);
118     static void updateDOMNodeDocument(DOM::NodeImpl *nodeHandle, DOM::DocumentImpl *oldDoc, DOM::DocumentImpl *newDoc);
119
120
121
122     KHTMLPart* part() const { return m_part; }
123
124     virtual int rtti() { return 1; }
125
126     /**
127      * Set the event that is triggering the execution of a script, if any
128      */
129     void setCurrentEvent( DOM::EventImpl *evt ) { m_evt = evt; }
130     void setInlineCode( bool inlineCode ) { m_inlineCode = inlineCode; }
131     void setProcessingTimerCallback( bool timerCallback ) { m_timerCallback = timerCallback; }
132     /**
133      * "Smart" window.open policy
134      */
135     bool wasRunByUserGesture() const;
136
137     virtual void mark();
138     
139     DOM::EventImpl *getCurrentEvent() const { return m_evt; }
140
141 #if APPLE_CHANGES
142     virtual bool isGlobalObject(const Value &v);
143     virtual Interpreter *interpreterForGlobalObject (const ValueImp *imp);
144     virtual bool isSafeScript (const Interpreter *target);
145     virtual void *createLanguageInstanceForValue (ExecState *exec, Bindings::Instance::BindingLanguage language, const Object &value, const Bindings::RootObject *origin, const Bindings::RootObject *current);
146     void *createObjcInstanceForValue (ExecState *exec, const Object &value, const Bindings::RootObject *origin, const Bindings::RootObject *current);
147 #endif
148
149   private:
150     KHTMLPart* m_part;
151
152     static QPtrDict<DOMObject> &domObjects();
153     static QPtrDict<QPtrDict<DOMNode> > &domNodesPerDocument();
154
155     DOM::EventImpl *m_evt;
156     bool m_inlineCode;
157     bool m_timerCallback;
158   };
159
160   /**
161    * Retrieve from cache, or create, a KJS object around a DOM object
162    */
163   template<class DOMObj, class KJSDOMObj>
164   inline ValueImp *cacheDOMObject(ExecState *exec, DOMObj *domObj)
165   {
166     if (!domObj)
167       return null();
168     ScriptInterpreter *interp = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter());
169     if (DOMObject *ret = interp->getDOMObject(domObj))
170       return ret;
171     DOMObject *ret = new KJSDOMObj(exec, domObj);
172     interp->putDOMObject(domObj, ret);
173     return ret;
174   }
175
176   // Convert a DOM implementation exception code into a JavaScript exception in the execution state.
177   void setDOMException(ExecState *exec, int DOMExceptionCode);
178
179   // Helper class to call setDOMException on exit without adding lots of separate calls to that function.
180   class DOMExceptionTranslator {
181   public:
182     explicit DOMExceptionTranslator(ExecState *exec) : m_exec(exec), m_code(0) { }
183     ~DOMExceptionTranslator() { setDOMException(m_exec, m_code); }
184     operator int &() { return m_code; }
185   private:
186     ExecState *m_exec;
187     int m_code;
188   };
189
190   /**
191    *  Get a String object, or Null() if s is null
192    */
193   Value getStringOrNull(DOM::DOMString s);
194
195   /**
196    * Convert a KJS value into a QVariant
197    * Deprecated: Use variant instead.
198    */
199   QVariant ValueToVariant(ExecState* exec, const Value& val);
200
201   /**
202    * We need a modified version of lookupGet because
203    * we call tryGet instead of get, in DOMObjects.
204    */
205   template <class FuncImp, class ThisImp, class ParentImp>
206   inline Value DOMObjectLookupGet(ExecState *exec, const Identifier &propertyName,
207                                   const HashTable* table, const ThisImp* thisObj)
208   {
209     const HashEntry* entry = Lookup::findEntry(table, propertyName);
210
211     if (!entry) // not found, forward to parent
212       return thisObj->ParentImp::tryGet(exec, propertyName);
213
214     if (entry->attr & Function)
215       return lookupOrCreateFunction<FuncImp>(exec, propertyName, thisObj, entry->value, entry->params, entry->attr);
216     return thisObj->getValueProperty(exec, entry->value);
217   }
218
219   /**
220    * Simplified version of DOMObjectLookupGet in case there are no
221    * functions, only "values".
222    */
223   template <class ThisImp, class ParentImp>
224   inline Value DOMObjectLookupGetValue(ExecState *exec, const Identifier &propertyName,
225                                        const HashTable* table, const ThisImp* thisObj)
226   {
227     const HashEntry* entry = Lookup::findEntry(table, propertyName);
228
229     if (!entry) // not found, forward to parent
230       return thisObj->ParentImp::tryGet(exec, propertyName);
231
232     if (entry->attr & Function)
233       fprintf(stderr, "Function bit set! Shouldn't happen in lookupValue!\n" );
234     return thisObj->getValueProperty(exec, entry->value);
235   }
236
237   /**
238    * We need a modified version of lookupPut because
239    * we call tryPut instead of put, in DOMObjects.
240    */
241   template <class ThisImp, class ParentImp>
242   inline void DOMObjectLookupPut(ExecState *exec, const Identifier &propertyName,
243                                  const Value& value, int attr,
244                                  const HashTable* table, ThisImp* thisObj)
245   {
246     const HashEntry* entry = Lookup::findEntry(table, propertyName);
247
248     if (!entry) // not found: forward to parent
249       thisObj->ParentImp::tryPut(exec, propertyName, value, attr);
250     else if (entry->attr & Function) // function: put as override property
251       thisObj->ObjectImp::put(exec, propertyName, value, attr);
252     else if (entry->attr & ReadOnly) // readonly! Can't put!
253 #ifdef KJS_VERBOSE
254       fprintf(stderr,"Attempt to change value of readonly property '%s'\n",propertyName.ascii());
255 #else
256     ; // do nothing
257 #endif
258     else
259       thisObj->putValue(exec, entry->value, value, attr);
260   }
261
262   /**
263    * This template method retrieves or create an object that is unique
264    * (for a given interpreter) The first time this is called (for a given
265    * property name), the Object will be constructed, and set as a property
266    * of the interpreter's global object. Later calls will simply retrieve
267    * that cached object. Note that the object constructor must take 1 argument, exec.
268    */
269   template <class ClassCtor>
270   inline ObjectImp *cacheGlobalObject(ExecState *exec, const Identifier &propertyName)
271   {
272     ObjectImp *globalObject = static_cast<ObjectImp *>(exec->lexicalInterpreter()->globalObject().imp());
273     ValueImp *obj = globalObject->getDirect(propertyName);
274     if (obj) {
275       assert(obj->isObject());
276       return static_cast<ObjectImp *>(obj);
277     }
278     ObjectImp *newObject = new ClassCtor(exec);
279     globalObject->put(exec, propertyName, Value(newObject), Internal);
280     return newObject;
281   }
282
283   /**
284    * Helpers to define prototype objects (each of which simply implements
285    * the functions for a type of objects).
286    * Sorry for this not being very readable, but it actually saves much copy-n-paste.
287    * ParentProto is not our base class, it's the object we use as fallback.
288    * The reason for this is that there should only be ONE DOMNode.hasAttributes (e.g.),
289    * not one in each derived class. So we link the (unique) prototypes between them.
290    *
291    * Using those macros is very simple: define the hashtable (e.g. "DOMNodeProtoTable"), then
292    * DEFINE_PROTOTYPE("DOMNode",DOMNodeProto)
293    * IMPLEMENT_PROTOFUNC(DOMNodeProtoFunc)
294    * IMPLEMENT_PROTOTYPE(DOMNodeProto,DOMNodeProtoFunc)
295    * and use DOMNodeProto::self(exec) as prototype in the DOMNode constructor.
296    * If the prototype has a "parent prototype", e.g. DOMElementProto falls back on DOMNodeProto,
297    * then the last line will use IMPLEMENT_PROTOTYPE_WITH_PARENT, with DOMNodeProto as last argument.
298    */
299 #define DEFINE_PROTOTYPE(ClassName,ClassProto) \
300   class ClassProto : public ObjectImp { \
301     friend ObjectImp *cacheGlobalObject<ClassProto>(ExecState *exec, const Identifier &propertyName); \
302   public: \
303     static ObjectImp *self(ExecState *exec) \
304     { \
305       return cacheGlobalObject<ClassProto>( exec, "[[" ClassName ".prototype]]" ); \
306     } \
307   protected: \
308     ClassProto( ExecState *exec ) \
309       : ObjectImp( exec->lexicalInterpreter()->builtinObjectPrototype() ) {} \
310     \
311   public: \
312     virtual const ClassInfo *classInfo() const { return &info; } \
313     static const ClassInfo info; \
314     Value get(ExecState *exec, const Identifier &propertyName) const; \
315     bool hasProperty(ExecState *exec, const Identifier &propertyName) const; \
316   }; \
317   const ClassInfo ClassProto::info = { ClassName, 0, &ClassProto##Table, 0 };
318
319 #define IMPLEMENT_PROTOTYPE(ClassProto,ClassFunc) \
320     Value ClassProto::get(ExecState *exec, const Identifier &propertyName) const \
321     { \
322       /*fprintf( stderr, "%sProto::get(%s) [in macro, no parent]\n", info.className, propertyName.ascii());*/ \
323       return lookupGetFunction<ClassFunc,ObjectImp>(exec, propertyName, &ClassProto##Table, this ); \
324     } \
325     bool ClassProto::hasProperty(ExecState *exec, const Identifier &propertyName) const \
326     { /*stupid but we need this to have a common macro for the declaration*/ \
327       return ObjectImp::hasProperty(exec, propertyName); \
328     }
329
330 #define IMPLEMENT_PROTOTYPE_WITH_PARENT(ClassProto,ClassFunc,ParentProto)  \
331     Value ClassProto::get(ExecState *exec, const Identifier &propertyName) const \
332     { \
333       /*fprintf( stderr, "%sProto::get(%s) [in macro]\n", info.className, propertyName.ascii());*/ \
334       Value val = lookupGetFunction<ClassFunc,ObjectImp>(exec, propertyName, &ClassProto##Table, this ); \
335       if ( val.type() != UndefinedType ) return val; \
336       /* Not found -> forward request to "parent" prototype */ \
337       return ParentProto::self(exec)->get( exec, propertyName ); \
338     } \
339     bool ClassProto::hasProperty(ExecState *exec, const Identifier &propertyName) const \
340     { \
341       if (ObjectImp::hasProperty(exec, propertyName)) \
342         return true; \
343       return ParentProto::self(exec)->hasProperty(exec, propertyName); \
344     }
345
346 #define IMPLEMENT_PROTOFUNC(ClassFunc) \
347   class ClassFunc : public DOMFunction { \
348   public: \
349     ClassFunc(ExecState *exec, int i, int len) \
350        : DOMFunction( /*proto? */ ), id(i) { \
351        Value protect(this); \
352        put(exec,lengthPropertyName,Number(len),DontDelete|ReadOnly|DontEnum); \
353     } \
354     /** You need to implement that one */ \
355     virtual Value tryCall(ExecState *exec, Object &thisObj, const List &args); \
356   private: \
357     int id; \
358   };
359
360 } // namespace
361
362 #endif