~ubuntu-branches/ubuntu/maverick/webkit/maverick

« back to all changes in this revision

Viewing changes to WebCore/bindings/js/kjs_binding.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Mike Hommey
  • Date: 2007-08-19 15:54:12 UTC
  • Revision ID: james.westby@ubuntu.com-20070819155412-uxxg1h9plpghmtbi
Tags: upstream-0~svn25144
ImportĀ upstreamĀ versionĀ 0~svn25144

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  This file is part of the KDE libraries
 
3
 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
 
4
 *  Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
 
5
 *  Copyright (C) 2007 Samuel Weinig <sam@webkit.org>
 
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
// gcc 3.x can't handle including the HashMap pointer specialization in this file
 
23
#if defined __GNUC__ && !defined __GLIBCXX__ // less than gcc 3.4
 
24
#define HASH_MAP_PTR_SPEC_WORKAROUND 1
 
25
#endif
 
26
 
 
27
#include "config.h"
 
28
#include "kjs_binding.h"
 
29
 
 
30
#include "Chrome.h"
 
31
#include "Event.h"
 
32
#include "EventNames.h"
 
33
#include "Frame.h"
 
34
#include "JSNode.h"
 
35
#include "Page.h"
 
36
#include "PlatformString.h"
 
37
#include "Range.h"
 
38
#include "RangeException.h"
 
39
#include "XMLHttpRequest.h"
 
40
#include "kjs_dom.h"
 
41
#include "kjs_window.h"
 
42
#include <kjs/collector.h>
 
43
#include <wtf/HashMap.h>
 
44
 
 
45
#if ENABLE(SVG)
 
46
#include "SVGException.h"
 
47
#endif
 
48
 
 
49
#if ENABLE(XPATH)
 
50
#include "XPathEvaluator.h"
 
51
#endif
 
52
 
 
53
using namespace WebCore;
 
54
using namespace EventNames;
 
55
 
 
56
namespace KJS {
 
57
 
 
58
typedef HashMap<void*, DOMObject*> DOMObjectMap;
 
59
typedef HashMap<Node*, JSNode*> NodeMap;
 
60
typedef HashMap<Document*, NodeMap*> NodePerDocMap;
 
61
 
 
62
// For debugging, keep a set of wrappers currently registered, and check that
 
63
// all are unregistered before they are destroyed. This has helped us fix at
 
64
// least one bug.
 
65
 
 
66
#ifdef NDEBUG
 
67
 
 
68
#define ADD_WRAPPER(wrapper)
 
69
#define REMOVE_WRAPPER(wrapper)
 
70
#define REMOVE_WRAPPERS(wrappers)
 
71
 
 
72
#else
 
73
 
 
74
#define ADD_WRAPPER(wrapper) addWrapper(wrapper)
 
75
#define REMOVE_WRAPPER(wrapper) removeWrapper(wrapper)
 
76
#define REMOVE_WRAPPERS(wrappers) removeWrappers(wrappers)
 
77
 
 
78
static HashSet<DOMObject*>& wrapperSet()
 
79
{
 
80
    static HashSet<DOMObject*> staticWrapperSet;
 
81
    return staticWrapperSet;
 
82
}
 
83
 
 
84
static void addWrapper(DOMObject* wrapper)
 
85
{
 
86
    ASSERT(!wrapperSet().contains(wrapper));
 
87
    wrapperSet().add(wrapper);
 
88
}
 
89
 
 
90
static void removeWrapper(DOMObject* wrapper)
 
91
{
 
92
    if (!wrapper)
 
93
        return;
 
94
    ASSERT(wrapperSet().contains(wrapper));
 
95
    wrapperSet().remove(wrapper);
 
96
}
 
97
 
 
98
static void removeWrappers(const NodeMap& wrappers)
 
99
{
 
100
    for (NodeMap::const_iterator it = wrappers.begin(); it != wrappers.end(); ++it)
 
101
        removeWrapper(it->second);
 
102
}
 
103
 
 
104
DOMObject::~DOMObject()
 
105
{
 
106
    ASSERT(!wrapperSet().contains(this));
 
107
}
 
108
 
 
109
#endif
 
110
 
 
111
static DOMObjectMap& domObjects()
 
112
 
113
    // Don't use malloc here. Calling malloc from a mark function can deadlock.
 
114
    static DOMObjectMap staticDOMObjects;
 
115
    return staticDOMObjects;
 
116
}
 
117
 
 
118
static NodePerDocMap& domNodesPerDocument()
 
119
{
 
120
    // domNodesPerDocument() callers must synchronize using the JSLock because 
 
121
    // domNodesPerDocument() is called from a mark function, which can run
 
122
    // on a secondary thread.
 
123
    ASSERT(JSLock::lockCount());
 
124
 
 
125
    // Don't use malloc here. Calling malloc from a mark function can deadlock.
 
126
    static NodePerDocMap staticDOMNodesPerDocument;
 
127
    return staticDOMNodesPerDocument;
 
128
}
 
129
 
 
130
ScriptInterpreter::ScriptInterpreter(JSObject* global, Frame* frame)
 
131
    : Interpreter(global)
 
132
    , m_frame(frame)
 
133
    , m_currentEvent(0)
 
134
    , m_inlineCode(false)
 
135
    , m_timerCallback(false)
 
136
{
 
137
    // Time in milliseconds before the script timeout handler kicks in.
 
138
    setTimeoutTime(10000);
 
139
}
 
140
 
 
141
DOMObject* ScriptInterpreter::getDOMObject(void* objectHandle) 
 
142
{
 
143
    return domObjects().get(objectHandle);
 
144
}
 
145
 
 
146
void ScriptInterpreter::putDOMObject(void* objectHandle, DOMObject* wrapper) 
 
147
{
 
148
    ADD_WRAPPER(wrapper);
 
149
    domObjects().set(objectHandle, wrapper);
 
150
}
 
151
 
 
152
void ScriptInterpreter::forgetDOMObject(void* objectHandle)
 
153
{
 
154
    REMOVE_WRAPPER(domObjects().get(objectHandle));
 
155
    domObjects().remove(objectHandle);
 
156
}
 
157
 
 
158
JSNode* ScriptInterpreter::getDOMNodeForDocument(Document* document, Node* node)
 
159
{
 
160
    if (!document)
 
161
        return static_cast<JSNode*>(domObjects().get(node));
 
162
    NodeMap* documentDict = domNodesPerDocument().get(document);
 
163
    if (documentDict)
 
164
        return documentDict->get(node);
 
165
    return NULL;
 
166
}
 
167
 
 
168
void ScriptInterpreter::forgetDOMNodeForDocument(Document* document, Node* node)
 
169
{
 
170
    REMOVE_WRAPPER(getDOMNodeForDocument(document, node));
 
171
    if (!document) {
 
172
        domObjects().remove(node);
 
173
        return;
 
174
    }
 
175
    NodeMap* documentDict = domNodesPerDocument().get(document);
 
176
    if (documentDict)
 
177
        documentDict->remove(node);
 
178
}
 
179
 
 
180
void ScriptInterpreter::putDOMNodeForDocument(Document* document, Node* node, JSNode* wrapper)
 
181
{
 
182
    ADD_WRAPPER(wrapper);
 
183
    if (!document) {
 
184
        domObjects().set(node, wrapper);
 
185
        return;
 
186
    }
 
187
    NodeMap* documentDict = domNodesPerDocument().get(document);
 
188
    if (!documentDict) {
 
189
        documentDict = new NodeMap;
 
190
        domNodesPerDocument().set(document, documentDict);
 
191
    }
 
192
    documentDict->set(node, wrapper);
 
193
}
 
194
 
 
195
void ScriptInterpreter::forgetAllDOMNodesForDocument(Document* document)
 
196
{
 
197
    ASSERT(document);
 
198
    NodePerDocMap::iterator it = domNodesPerDocument().find(document);
 
199
    if (it != domNodesPerDocument().end()) {
 
200
        REMOVE_WRAPPERS(*it->second);
 
201
        delete it->second;
 
202
        domNodesPerDocument().remove(it);
 
203
    }
 
204
}
 
205
 
 
206
void ScriptInterpreter::markDOMNodesForDocument(Document* doc)
 
207
{
 
208
    NodePerDocMap::iterator dictIt = domNodesPerDocument().find(doc);
 
209
    if (dictIt != domNodesPerDocument().end()) {
 
210
        NodeMap* nodeDict = dictIt->second;
 
211
        NodeMap::iterator nodeEnd = nodeDict->end();
 
212
        for (NodeMap::iterator nodeIt = nodeDict->begin(); nodeIt != nodeEnd; ++nodeIt) {
 
213
            JSNode* node = nodeIt->second;
 
214
            // don't mark wrappers for nodes that are no longer in the
 
215
            // document - they should not be saved if the node is not
 
216
            // otherwise reachable from JS.
 
217
            if (node->impl()->inDocument() && !node->marked())
 
218
                node->mark();
 
219
        }
 
220
    }
 
221
}
 
222
 
 
223
ExecState* ScriptInterpreter::globalExec()
 
224
{
 
225
    // we need to make sure that any script execution happening in this
 
226
    // frame does not destroy it
 
227
    m_frame->keepAlive();
 
228
    return Interpreter::globalExec();
 
229
}
 
230
 
 
231
void ScriptInterpreter::updateDOMNodeDocument(Node* node, Document* oldDoc, Document* newDoc)
 
232
{
 
233
    ASSERT(oldDoc != newDoc);
 
234
    JSNode* wrapper = getDOMNodeForDocument(oldDoc, node);
 
235
    if (wrapper) {
 
236
        REMOVE_WRAPPER(wrapper);
 
237
        putDOMNodeForDocument(newDoc, node, wrapper);
 
238
        forgetDOMNodeForDocument(oldDoc, node);
 
239
        ADD_WRAPPER(wrapper);
 
240
    }
 
241
}
 
242
 
 
243
bool ScriptInterpreter::wasRunByUserGesture() const
 
244
{
 
245
    if (m_currentEvent) {
 
246
        const AtomicString& type = m_currentEvent->type();
 
247
        bool eventOk = ( // mouse events
 
248
            type == clickEvent || type == mousedownEvent ||
 
249
            type == mouseupEvent || type == dblclickEvent ||
 
250
            // keyboard events
 
251
            type == keydownEvent || type == keypressEvent ||
 
252
            type == keyupEvent ||
 
253
            // other accepted events
 
254
            type == selectEvent || type == changeEvent ||
 
255
            type == focusEvent || type == blurEvent ||
 
256
            type == submitEvent);
 
257
        if (eventOk)
 
258
            return true;
 
259
    } else { // no event
 
260
        if (m_inlineCode && !m_timerCallback)
 
261
            // This is the <a href="javascript:window.open('...')> case -> we let it through
 
262
            return true;
 
263
        // This is the <script>window.open(...)</script> case or a timer callback -> block it
 
264
    }
 
265
    return false;
 
266
}
 
267
 
 
268
bool ScriptInterpreter::isGlobalObject(JSValue* v)
 
269
{
 
270
    return v->isObject(&Window::info);
 
271
}
 
272
 
 
273
bool ScriptInterpreter::isSafeScript(const Interpreter* target)
 
274
{
 
275
    return Window::isSafeScript(this, static_cast<const ScriptInterpreter*>(target));
 
276
}
 
277
 
 
278
Interpreter* ScriptInterpreter::interpreterForGlobalObject(const JSValue* imp)
 
279
{
 
280
    const Window* win = static_cast<const Window*>(imp);
 
281
    return win->interpreter();
 
282
}
 
283
 
 
284
bool ScriptInterpreter::shouldInterruptScript() const
 
285
{
 
286
    if (Page *page = m_frame->page())
 
287
        return page->chrome()->shouldInterruptJavaScript();
 
288
    
 
289
    return false;
 
290
}
 
291
    
 
292
//////
 
293
 
 
294
JSValue* jsStringOrNull(const String& s)
 
295
{
 
296
    if (s.isNull())
 
297
        return jsNull();
 
298
    return jsString(s);
 
299
}
 
300
 
 
301
JSValue* jsOwnedStringOrNull(const KJS::UString& s)
 
302
{
 
303
    if (s.isNull())
 
304
        return jsNull();
 
305
    return jsOwnedString(s);
 
306
}
 
307
 
 
308
JSValue* jsStringOrUndefined(const String& s)
 
309
{
 
310
    if (s.isNull())
 
311
        return jsUndefined();
 
312
    return jsString(s);
 
313
}
 
314
 
 
315
JSValue* jsStringOrFalse(const String& s)
 
316
{
 
317
    if (s.isNull())
 
318
        return jsBoolean(false);
 
319
    return jsString(s);
 
320
}
 
321
 
 
322
String valueToStringWithNullCheck(ExecState* exec, JSValue* val)
 
323
{
 
324
    if (val->isNull())
 
325
        return String();
 
326
    return val->toString(exec);
 
327
}
 
328
 
 
329
String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue* val)
 
330
{
 
331
    if (val->isUndefinedOrNull())
 
332
        return String();
 
333
    return val->toString(exec);
 
334
}
 
335
 
 
336
static const char* const exceptionNames[] = {
 
337
    0,
 
338
    "INDEX_SIZE_ERR",
 
339
    "DOMSTRING_SIZE_ERR",
 
340
    "HIERARCHY_REQUEST_ERR",
 
341
    "WRONG_DOCUMENT_ERR",
 
342
    "INVALID_CHARACTER_ERR",
 
343
    "NO_DATA_ALLOWED_ERR",
 
344
    "NO_MODIFICATION_ALLOWED_ERR",
 
345
    "NOT_FOUND_ERR",
 
346
    "NOT_SUPPORTED_ERR",
 
347
    "INUSE_ATTRIBUTE_ERR",
 
348
    "INVALID_STATE_ERR",
 
349
    "SYNTAX_ERR",
 
350
    "INVALID_MODIFICATION_ERR",
 
351
    "NAMESPACE_ERR",
 
352
    "INVALID_ACCESS_ERR",
 
353
    "VALIDATION_ERR",
 
354
    "TYPE_MISMATCH_ERR",
 
355
};
 
356
 
 
357
static const char* const rangeExceptionNames[] = {
 
358
    0, "BAD_BOUNDARYPOINTS_ERR", "INVALID_NODE_TYPE_ERR"
 
359
};
 
360
 
 
361
static const char* const eventExceptionNames[] = {
 
362
    "UNSPECIFIED_EVENT_TYPE_ERR"
 
363
};
 
364
 
 
365
static const char* const xmlHttpRequestExceptionNames[] = {
 
366
    "NETWORK_ERR"
 
367
};
 
368
 
 
369
#if ENABLE(XPATH)
 
370
static const char* const xpathExceptionNames[] = {
 
371
    "INVALID_EXPRESSION_ERR",
 
372
    "TYPE_ERR"
 
373
};
 
374
#endif
 
375
 
 
376
#if ENABLE(SVG)
 
377
static const char* const svgExceptionNames[] = {
 
378
    "SVG_WRONG_TYPE_ERR",
 
379
    "SVG_INVALID_VALUE_ERR",
 
380
    "SVG_MATRIX_NOT_INVERTABLE"
 
381
};
 
382
#endif
 
383
 
 
384
void setDOMException(ExecState* exec, ExceptionCode ec)
 
385
{
 
386
    if (ec == 0 || exec->hadException())
 
387
        return;
 
388
 
 
389
    const char* type = "DOM";
 
390
    int code = ec;
 
391
 
 
392
    const char* const* nameTable;
 
393
  
 
394
    int nameTableSize;
 
395
    int nameIndex;
 
396
    if (code >= RangeExceptionOffset && code <= RangeExceptionMax) {
 
397
        type = "DOM Range";
 
398
        code -= RangeExceptionOffset;
 
399
        nameIndex = code;
 
400
        nameTable = rangeExceptionNames;
 
401
        nameTableSize = sizeof(rangeExceptionNames) / sizeof(rangeExceptionNames[0]);
 
402
    } else if (code >= EventExceptionOffset && code <= EventExceptionMax) {
 
403
        type = "DOM Events";
 
404
        code -= EventExceptionOffset;
 
405
        nameIndex = code;
 
406
        nameTable = eventExceptionNames;
 
407
        nameTableSize = sizeof(eventExceptionNames) / sizeof(eventExceptionNames[0]);
 
408
    } else if (code == XMLHttpRequestExceptionOffset) {
 
409
        // FIXME: this exception should be replaced with DOM SECURITY_ERR when it finds its way to the spec.
 
410
        throwError(exec, GeneralError, "Permission denied");
 
411
        return;
 
412
    } else if (code > XMLHttpRequestExceptionOffset && code <= XMLHttpRequestExceptionMax) {
 
413
        type = "XMLHttpRequest";
 
414
        // XMLHttpRequest exception codes start with 101 and we don't want 100 empty elements in the name array
 
415
        nameIndex = code - NETWORK_ERR;
 
416
        code -= XMLHttpRequestExceptionOffset;
 
417
        nameTable = xmlHttpRequestExceptionNames;
 
418
        nameTableSize = sizeof(xmlHttpRequestExceptionNames) / sizeof(xmlHttpRequestExceptionNames[0]);
 
419
#if ENABLE(XPATH)
 
420
    } else if (code >= XPathExceptionOffset && code <= XPathExceptionMax) {
 
421
        type = "DOM XPath";
 
422
        // XPath exception codes start with 51 and we don't want 51 empty elements in the name array
 
423
        nameIndex = code - INVALID_EXPRESSION_ERR;
 
424
        code -= XPathExceptionOffset;
 
425
        nameTable = xpathExceptionNames;
 
426
        nameTableSize = sizeof(xpathExceptionNames) / sizeof(xpathExceptionNames[0]);
 
427
#endif
 
428
#if ENABLE(SVG)
 
429
    } else if (code >= SVGExceptionOffset && code <= SVGExceptionMax) {
 
430
        type = "DOM SVG";
 
431
        code -= SVGExceptionOffset;
 
432
        nameIndex = code;
 
433
        nameTable = svgExceptionNames;
 
434
        nameTableSize = sizeof(svgExceptionNames) / sizeof(svgExceptionNames[0]);
 
435
#endif
 
436
    } else {
 
437
        nameIndex = code;
 
438
        nameTable = exceptionNames;
 
439
        nameTableSize = sizeof(exceptionNames) / sizeof(exceptionNames[0]);
 
440
    }
 
441
 
 
442
    const char* name = (nameIndex < nameTableSize && nameIndex >= 0) ? nameTable[nameIndex] : 0;
 
443
 
 
444
    // 100 characters is a big enough buffer, because there are:
 
445
    //   13 characters in the message
 
446
    //   10 characters in the longest type, "DOM Events"
 
447
    //   27 characters in the longest name, "NO_MODIFICATION_ALLOWED_ERR"
 
448
    //   20 or so digits in the longest integer's ASCII form (even if int is 64-bit)
 
449
    //   1 byte for a null character
 
450
    // That adds up to about 70 bytes.
 
451
    char buffer[100];
 
452
 
 
453
    if (name)
 
454
        sprintf(buffer, "%s: %s Exception %d", name, type, code);
 
455
    else
 
456
        sprintf(buffer, "%s Exception %d", type, code);
 
457
 
 
458
    JSObject* errorObject = throwError(exec, GeneralError, buffer);
 
459
    errorObject->put(exec, "code", jsNumber(code));
 
460
}
 
461
 
 
462
}