Reviewed by Darin.
[WebKit-https.git] / WebCore / khtml / ecma / kjs_binding.cpp
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) 2004 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 #include "kjs_binding.h"
23 #include "kjs_dom.h"
24 #include <kjs/internal.h> // for InterpreterImp
25
26 #include "dom/dom_exception.h"
27 #include "dom/dom2_range.h"
28 #include "xml/dom2_eventsimpl.h"
29
30 #include <kdebug.h>
31
32 using DOM::DOMString;
33
34 using namespace KJS;
35
36 /* TODO:
37  * The catch all (...) clauses below shouldn't be necessary.
38  * But they helped to view for example www.faz.net in an stable manner.
39  * Those unknown exceptions should be treated as severe bugs and be fixed.
40  *
41  * these may be CSS exceptions - need to check - pmk
42  */
43
44 Value DOMObject::get(ExecState *exec, const Identifier &p) const
45 {
46   Value result;
47   try {
48     result = tryGet(exec,p);
49   }
50   catch (DOM::DOMException e) {
51     // ### translate code into readable string ?
52     // ### oh, and s/QString/i18n or I18N_NOOP (the code in kjs uses I18N_NOOP... but where is it translated ?)
53     //     and where does it appear to the user ?
54     Object err = Error::create(exec, GeneralError, QString("DOM exception %1").arg(e.code).local8Bit());
55     err.put(exec, "code", Number(e.code));
56     exec->setException( err );
57     result = Undefined();
58   }
59   catch (...) {
60     kdError(6070) << "Unknown exception in DOMObject::get()" << endl;
61     result = String("Unknown exception");
62   }
63
64   return result;
65 }
66
67 void DOMObject::put(ExecState *exec, const Identifier &propertyName,
68                     const Value &value, int attr)
69 {
70   try {
71     tryPut(exec, propertyName, value, attr);
72   }
73   catch (DOM::DOMException e) {
74     Object err = Error::create(exec, GeneralError, QString("DOM exception %1").arg(e.code).local8Bit());
75     err.put(exec, "code", Number(e.code));
76     exec->setException(err);
77   }
78   catch (...) {
79     kdError(6070) << "Unknown exception in DOMObject::put()" << endl;
80   }
81 }
82
83 UString DOMObject::toString(ExecState *) const
84 {
85   return "[object " + className() + "]";
86 }
87
88 Value DOMFunction::get(ExecState *exec, const Identifier &propertyName) const
89 {
90   Value result;
91   try {
92     result = tryGet(exec, propertyName);
93   }
94   catch (DOM::DOMException e) {
95     result = Undefined();
96     Object err = Error::create(exec, GeneralError, QString("DOM exception %1").arg(e.code).local8Bit());
97     err.put(exec, "code", Number(e.code));
98     exec->setException(err);
99   }
100   catch (...) {
101     kdError(6070) << "Unknown exception in DOMFunction::get()" << endl;
102     result = String("Unknown exception");
103   }
104
105   return result;
106 }
107
108 Value DOMFunction::call(ExecState *exec, Object &thisObj, const List &args)
109 {
110   Value val;
111   try {
112     val = tryCall(exec, thisObj, args);
113   }
114   // pity there's no way to distinguish between these in JS code
115   catch (DOM::DOMException e) {
116     Object err = Error::create(exec, GeneralError, QString("DOM Exception %1").arg(e.code).local8Bit());
117     err.put(exec, "code", Number(e.code));
118     exec->setException(err);
119   }
120   catch (DOM::RangeException e) {
121     Object err = Error::create(exec, GeneralError, QString("DOM Range Exception %1").arg(e.code).local8Bit());
122     err.put(exec, "code", Number(e.code));
123     exec->setException(err);
124   }
125   catch (DOM::CSSException e) {
126     Object err = Error::create(exec, GeneralError, QString("CSS Exception %1").arg(e.code).local8Bit());
127     err.put(exec, "code", Number(e.code));
128     exec->setException(err);
129   }
130   catch (DOM::EventException e) {
131     Object err = Error::create(exec, GeneralError, QString("DOM Event Exception %1").arg(e.code).local8Bit());
132     err.put(exec, "code", Number(e.code));
133     exec->setException(err);
134   }
135   catch (...) {
136     kdError(6070) << "Unknown exception in DOMFunction::call()" << endl;
137     Object err = Error::create(exec, GeneralError, "Unknown exception");
138     exec->setException(err);
139   }
140   return val;
141 }
142
143 static QPtrDict<DOMObject> * staticDomObjects = 0;
144 QPtrDict< QPtrDict<DOMObject> > * staticDomObjectsPerDocument = 0;
145
146 QPtrDict<DOMObject> & ScriptInterpreter::domObjects()
147 {
148   if (!staticDomObjects) {
149     staticDomObjects = new QPtrDict<DOMObject>(1021);
150   }
151   return *staticDomObjects;
152 }
153
154 QPtrDict< QPtrDict<DOMObject> > & ScriptInterpreter::domObjectsPerDocument()
155 {
156   if (!staticDomObjectsPerDocument) {
157     staticDomObjectsPerDocument = new QPtrDict<QPtrDict<DOMObject> >();
158     staticDomObjectsPerDocument->setAutoDelete(true);
159   }
160   return *staticDomObjectsPerDocument;
161 }
162
163
164 ScriptInterpreter::ScriptInterpreter( const Object &global, KHTMLPart* part )
165   : Interpreter( global ), m_part( part ),
166     m_evt( 0L ), m_inlineCode(false), m_timerCallback(false)
167 {
168 #ifdef KJS_VERBOSE
169   kdDebug(6070) << "ScriptInterpreter::ScriptInterpreter " << this << " for part=" << m_part << endl;
170 #endif
171 }
172
173 ScriptInterpreter::~ScriptInterpreter()
174 {
175 #ifdef KJS_VERBOSE
176   kdDebug(6070) << "ScriptInterpreter::~ScriptInterpreter " << this << " for part=" << m_part << endl;
177 #endif
178 }
179
180 void ScriptInterpreter::forgetDOMObject( void* objectHandle )
181 {
182   deleteDOMObject( objectHandle );
183 }
184
185 DOMObject* ScriptInterpreter::getDOMObjectForDocument( DOM::DocumentImpl* documentHandle, void *objectHandle )
186 {
187   QPtrDict<DOMObject> *documentDict = (QPtrDict<DOMObject> *)domObjectsPerDocument()[documentHandle];
188   if (documentDict) {
189     return (*documentDict)[objectHandle];
190   }
191
192   return NULL;
193 }
194
195 void ScriptInterpreter::putDOMObjectForDocument( DOM::DocumentImpl* documentHandle, void *objectHandle, DOMObject *obj )
196 {
197   QPtrDict<DOMObject> *documentDict = (QPtrDict<DOMObject> *)domObjectsPerDocument()[documentHandle];
198   if (!documentDict) {
199     documentDict = new QPtrDict<DOMObject>();
200     domObjectsPerDocument().insert(documentHandle, documentDict);
201   }
202
203   documentDict->insert( objectHandle, obj );
204 }
205
206 bool ScriptInterpreter::deleteDOMObjectsForDocument( DOM::DocumentImpl* documentHandle )
207 {
208   return domObjectsPerDocument().remove( documentHandle );
209 }
210
211 void ScriptInterpreter::mark()
212 {
213   QPtrDictIterator<QPtrDict<DOMObject> > dictIterator(domObjectsPerDocument());
214
215   QPtrDict<DOMObject> *objectDict;
216   while ((objectDict = dictIterator.current())) {
217     QPtrDictIterator<DOMObject> objectIterator(*objectDict);
218
219     DOMObject *obj;
220     while ((obj = objectIterator.current())) {
221       if (!obj->marked()) {
222         obj->mark();
223       }
224       ++objectIterator;
225     }
226     ++dictIterator;
227   }
228 }
229
230 void ScriptInterpreter::forgetDOMObjectsForDocument( DOM::DocumentImpl* documentHandle )
231 {
232   deleteDOMObjectsForDocument( documentHandle );
233 }
234
235 void ScriptInterpreter::updateDOMObjectDocument(void *objectHandle, DOM::DocumentImpl *oldDoc, DOM::DocumentImpl *newDoc)
236 {
237   DOMObject* cachedObject = getDOMObjectForDocument(oldDoc, objectHandle);
238   if (cachedObject) {
239     putDOMObjectForDocument(newDoc, objectHandle, cachedObject);
240   }
241 }
242
243 bool ScriptInterpreter::wasRunByUserGesture() const
244 {
245   if ( m_evt )
246   {
247     int id = m_evt->handle()->id();
248     bool eventOk = ( // mouse events
249       id == DOM::EventImpl::CLICK_EVENT || id == DOM::EventImpl::MOUSEDOWN_EVENT ||
250       id == DOM::EventImpl::MOUSEUP_EVENT || id == DOM::EventImpl::KHTML_DBLCLICK_EVENT ||
251       id == DOM::EventImpl::KHTML_CLICK_EVENT ||
252       // keyboard events
253       id == DOM::EventImpl::KEYDOWN_EVENT || id == DOM::EventImpl::KEYPRESS_EVENT ||
254       id == DOM::EventImpl::KEYUP_EVENT ||
255       // other accepted events
256       id == DOM::EventImpl::SELECT_EVENT || id == DOM::EventImpl::CHANGE_EVENT ||
257       id == DOM::EventImpl::FOCUS_EVENT || id == DOM::EventImpl::BLUR_EVENT ||
258       id == DOM::EventImpl::SUBMIT_EVENT );
259     kdDebug(6070) << "Window.open, smart policy: id=" << id << " eventOk=" << eventOk << endl;
260     if (eventOk)
261       return true;
262   } else // no event
263   {
264     if ( m_inlineCode  && !m_timerCallback )
265     {
266       // This is the <a href="javascript:window.open('...')> case -> we let it through
267       return true;
268       kdDebug(6070) << "Window.open, smart policy, no event, inline code -> ok" << endl;
269     }
270     else // This is the <script>window.open(...)</script> case or a timer callback -> block it
271       kdDebug(6070) << "Window.open, smart policy, no event, <script> tag -> refused" << endl;
272   }
273   return false;
274 }
275
276 //////
277
278 UString::UString(const QString &d)
279 {
280   unsigned int len = d.length();
281   UChar *dat = new UChar[len];
282   memcpy(dat, d.unicode(), len * sizeof(UChar));
283   rep = UString::Rep::create(dat, len);
284 }
285
286 UString::UString(const DOMString &d)
287 {
288   if (d.isNull()) {
289     attach(&Rep::null);
290     return;
291   }
292
293   unsigned int len = d.length();
294   UChar *dat = new UChar[len];
295   memcpy(dat, d.unicode(), len * sizeof(UChar));
296   rep = UString::Rep::create(dat, len);
297 }
298
299 DOMString UString::string() const
300 {
301   if (isNull())
302     return DOMString();
303   if (isEmpty())
304     return DOMString("");
305   return DOMString((QChar*) data(), size());
306 }
307
308 QString UString::qstring() const
309 {
310   if (isNull())
311     return QString();
312   if (isEmpty())
313     return QString("");
314   return QString((QChar*) data(), size());
315 }
316
317 QConstString UString::qconststring() const
318 {
319   return QConstString((QChar*) data(), size());
320 }
321
322 DOMString Identifier::string() const
323 {
324   if (isNull())
325     return DOMString();
326   if (isEmpty())
327     return DOMString("");
328   return DOMString((QChar*) data(), size());
329 }
330
331 QString Identifier::qstring() const
332 {
333   if (isNull())
334     return QString();
335   if (isEmpty())
336     return QString("");
337   return QString((QChar*) data(), size());
338 }
339
340 DOM::Node KJS::toNode(const Value& val)
341 {
342   Object obj = Object::dynamicCast(val);
343   if (obj.isNull() || !obj.inherits(&DOMNode::info))
344     return DOM::Node();
345
346   const DOMNode *dobj = static_cast<const DOMNode*>(obj.imp());
347   return dobj->toNode();
348 }
349
350 Value KJS::getStringOrNull(DOMString s)
351 {
352   if (s.isNull())
353     return Null();
354   else
355     return String(s);
356 }
357
358 QVariant KJS::ValueToVariant(ExecState* exec, const Value &val) {
359   QVariant res;
360   switch (val.type()) {
361   case BooleanType:
362     res = QVariant(val.toBoolean(exec), 0);
363     break;
364   case NumberType:
365     res = QVariant(val.toNumber(exec));
366     break;
367   case StringType:
368     res = QVariant(val.toString(exec).qstring());
369     break;
370   default:
371     // everything else will be 'invalid'
372     break;
373   }
374   return res;
375 }