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>
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.
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.
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
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
28
#include "kjs_binding.h"
32
#include "EventNames.h"
36
#include "PlatformString.h"
38
#include "RangeException.h"
39
#include "XMLHttpRequest.h"
41
#include "kjs_window.h"
42
#include <kjs/collector.h>
43
#include <wtf/HashMap.h>
46
#include "SVGException.h"
50
#include "XPathEvaluator.h"
53
using namespace WebCore;
54
using namespace EventNames;
58
typedef HashMap<void*, DOMObject*> DOMObjectMap;
59
typedef HashMap<Node*, JSNode*> NodeMap;
60
typedef HashMap<Document*, NodeMap*> NodePerDocMap;
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
68
#define ADD_WRAPPER(wrapper)
69
#define REMOVE_WRAPPER(wrapper)
70
#define REMOVE_WRAPPERS(wrappers)
74
#define ADD_WRAPPER(wrapper) addWrapper(wrapper)
75
#define REMOVE_WRAPPER(wrapper) removeWrapper(wrapper)
76
#define REMOVE_WRAPPERS(wrappers) removeWrappers(wrappers)
78
static HashSet<DOMObject*>& wrapperSet()
80
static HashSet<DOMObject*> staticWrapperSet;
81
return staticWrapperSet;
84
static void addWrapper(DOMObject* wrapper)
86
ASSERT(!wrapperSet().contains(wrapper));
87
wrapperSet().add(wrapper);
90
static void removeWrapper(DOMObject* wrapper)
94
ASSERT(wrapperSet().contains(wrapper));
95
wrapperSet().remove(wrapper);
98
static void removeWrappers(const NodeMap& wrappers)
100
for (NodeMap::const_iterator it = wrappers.begin(); it != wrappers.end(); ++it)
101
removeWrapper(it->second);
104
DOMObject::~DOMObject()
106
ASSERT(!wrapperSet().contains(this));
111
static DOMObjectMap& domObjects()
113
// Don't use malloc here. Calling malloc from a mark function can deadlock.
114
static DOMObjectMap staticDOMObjects;
115
return staticDOMObjects;
118
static NodePerDocMap& domNodesPerDocument()
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());
125
// Don't use malloc here. Calling malloc from a mark function can deadlock.
126
static NodePerDocMap staticDOMNodesPerDocument;
127
return staticDOMNodesPerDocument;
130
ScriptInterpreter::ScriptInterpreter(JSObject* global, Frame* frame)
131
: Interpreter(global)
134
, m_inlineCode(false)
135
, m_timerCallback(false)
137
// Time in milliseconds before the script timeout handler kicks in.
138
setTimeoutTime(10000);
141
DOMObject* ScriptInterpreter::getDOMObject(void* objectHandle)
143
return domObjects().get(objectHandle);
146
void ScriptInterpreter::putDOMObject(void* objectHandle, DOMObject* wrapper)
148
ADD_WRAPPER(wrapper);
149
domObjects().set(objectHandle, wrapper);
152
void ScriptInterpreter::forgetDOMObject(void* objectHandle)
154
REMOVE_WRAPPER(domObjects().get(objectHandle));
155
domObjects().remove(objectHandle);
158
JSNode* ScriptInterpreter::getDOMNodeForDocument(Document* document, Node* node)
161
return static_cast<JSNode*>(domObjects().get(node));
162
NodeMap* documentDict = domNodesPerDocument().get(document);
164
return documentDict->get(node);
168
void ScriptInterpreter::forgetDOMNodeForDocument(Document* document, Node* node)
170
REMOVE_WRAPPER(getDOMNodeForDocument(document, node));
172
domObjects().remove(node);
175
NodeMap* documentDict = domNodesPerDocument().get(document);
177
documentDict->remove(node);
180
void ScriptInterpreter::putDOMNodeForDocument(Document* document, Node* node, JSNode* wrapper)
182
ADD_WRAPPER(wrapper);
184
domObjects().set(node, wrapper);
187
NodeMap* documentDict = domNodesPerDocument().get(document);
189
documentDict = new NodeMap;
190
domNodesPerDocument().set(document, documentDict);
192
documentDict->set(node, wrapper);
195
void ScriptInterpreter::forgetAllDOMNodesForDocument(Document* document)
198
NodePerDocMap::iterator it = domNodesPerDocument().find(document);
199
if (it != domNodesPerDocument().end()) {
200
REMOVE_WRAPPERS(*it->second);
202
domNodesPerDocument().remove(it);
206
void ScriptInterpreter::markDOMNodesForDocument(Document* doc)
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())
223
ExecState* ScriptInterpreter::globalExec()
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();
231
void ScriptInterpreter::updateDOMNodeDocument(Node* node, Document* oldDoc, Document* newDoc)
233
ASSERT(oldDoc != newDoc);
234
JSNode* wrapper = getDOMNodeForDocument(oldDoc, node);
236
REMOVE_WRAPPER(wrapper);
237
putDOMNodeForDocument(newDoc, node, wrapper);
238
forgetDOMNodeForDocument(oldDoc, node);
239
ADD_WRAPPER(wrapper);
243
bool ScriptInterpreter::wasRunByUserGesture() const
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 ||
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);
260
if (m_inlineCode && !m_timerCallback)
261
// This is the <a href="javascript:window.open('...')> case -> we let it through
263
// This is the <script>window.open(...)</script> case or a timer callback -> block it
268
bool ScriptInterpreter::isGlobalObject(JSValue* v)
270
return v->isObject(&Window::info);
273
bool ScriptInterpreter::isSafeScript(const Interpreter* target)
275
return Window::isSafeScript(this, static_cast<const ScriptInterpreter*>(target));
278
Interpreter* ScriptInterpreter::interpreterForGlobalObject(const JSValue* imp)
280
const Window* win = static_cast<const Window*>(imp);
281
return win->interpreter();
284
bool ScriptInterpreter::shouldInterruptScript() const
286
if (Page *page = m_frame->page())
287
return page->chrome()->shouldInterruptJavaScript();
294
JSValue* jsStringOrNull(const String& s)
301
JSValue* jsOwnedStringOrNull(const KJS::UString& s)
305
return jsOwnedString(s);
308
JSValue* jsStringOrUndefined(const String& s)
311
return jsUndefined();
315
JSValue* jsStringOrFalse(const String& s)
318
return jsBoolean(false);
322
String valueToStringWithNullCheck(ExecState* exec, JSValue* val)
326
return val->toString(exec);
329
String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue* val)
331
if (val->isUndefinedOrNull())
333
return val->toString(exec);
336
static const char* const exceptionNames[] = {
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",
347
"INUSE_ATTRIBUTE_ERR",
350
"INVALID_MODIFICATION_ERR",
352
"INVALID_ACCESS_ERR",
357
static const char* const rangeExceptionNames[] = {
358
0, "BAD_BOUNDARYPOINTS_ERR", "INVALID_NODE_TYPE_ERR"
361
static const char* const eventExceptionNames[] = {
362
"UNSPECIFIED_EVENT_TYPE_ERR"
365
static const char* const xmlHttpRequestExceptionNames[] = {
370
static const char* const xpathExceptionNames[] = {
371
"INVALID_EXPRESSION_ERR",
377
static const char* const svgExceptionNames[] = {
378
"SVG_WRONG_TYPE_ERR",
379
"SVG_INVALID_VALUE_ERR",
380
"SVG_MATRIX_NOT_INVERTABLE"
384
void setDOMException(ExecState* exec, ExceptionCode ec)
386
if (ec == 0 || exec->hadException())
389
const char* type = "DOM";
392
const char* const* nameTable;
396
if (code >= RangeExceptionOffset && code <= RangeExceptionMax) {
398
code -= RangeExceptionOffset;
400
nameTable = rangeExceptionNames;
401
nameTableSize = sizeof(rangeExceptionNames) / sizeof(rangeExceptionNames[0]);
402
} else if (code >= EventExceptionOffset && code <= EventExceptionMax) {
404
code -= EventExceptionOffset;
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");
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]);
420
} else if (code >= XPathExceptionOffset && code <= XPathExceptionMax) {
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]);
429
} else if (code >= SVGExceptionOffset && code <= SVGExceptionMax) {
431
code -= SVGExceptionOffset;
433
nameTable = svgExceptionNames;
434
nameTableSize = sizeof(svgExceptionNames) / sizeof(svgExceptionNames[0]);
438
nameTable = exceptionNames;
439
nameTableSize = sizeof(exceptionNames) / sizeof(exceptionNames[0]);
442
const char* name = (nameIndex < nameTableSize && nameIndex >= 0) ? nameTable[nameIndex] : 0;
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.
454
sprintf(buffer, "%s: %s Exception %d", name, type, code);
456
sprintf(buffer, "%s Exception %d", type, code);
458
JSObject* errorObject = throwError(exec, GeneralError, buffer);
459
errorObject->put(exec, "code", jsNumber(code));