1/* 2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. 4 * Copyright (C) 2007 Samuel Weinig <sam@webkit.org> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21#include "config.h" 22#include "JSDOMBinding.h" 23 24#include "debugger/DebuggerCallFrame.h" 25 26#include "ActiveDOMObject.h" 27#include "DOMCoreException.h" 28#include "DOMObjectHashTableMap.h" 29#include "Document.h" 30#include "EventException.h" 31#include "ExceptionBase.h" 32#include "ExceptionCode.h" 33#include "Frame.h" 34#include "HTMLAudioElement.h" 35#include "HTMLCanvasElement.h" 36#include "HTMLFrameElementBase.h" 37#include "HTMLImageElement.h" 38#include "HTMLLinkElement.h" 39#include "HTMLNames.h" 40#include "HTMLScriptElement.h" 41#include "HTMLStyleElement.h" 42#include "JSDOMCoreException.h" 43#include "JSDOMWindowCustom.h" 44#include "JSEventException.h" 45#include "JSExceptionBase.h" 46#include "JSMainThreadExecState.h" 47#include "JSRangeException.h" 48#include "JSXMLHttpRequestException.h" 49#include "KURL.h" 50#include "MessagePort.h" 51#include "ProcessingInstruction.h" 52#include "RangeException.h" 53#include "ScriptCachedFrameData.h" 54#include "ScriptCallStack.h" 55#include "ScriptController.h" 56#include "Settings.h" 57#include "WebCoreJSClientData.h" 58#include "XMLHttpRequestException.h" 59#include <runtime/DateInstance.h> 60#include <runtime/Error.h> 61#include <runtime/JSFunction.h> 62#include <wtf/MathExtras.h> 63#include <wtf/StdLibExtras.h> 64 65#if ENABLE(SVG) 66#include "JSSVGException.h" 67#include "SVGException.h" 68#endif 69 70#if ENABLE(XPATH) 71#include "JSXPathException.h" 72#include "XPathException.h" 73#endif 74 75#if ENABLE(DATABASE) 76#include "JSSQLException.h" 77#include "SQLException.h" 78#endif 79 80#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) 81#include "FileException.h" 82#include "JSFileException.h" 83#endif 84 85#if ENABLE(INDEXED_DATABASE) 86#include "IDBDatabaseException.h" 87#include "JSIDBDatabaseException.h" 88#endif 89 90using namespace JSC; 91 92namespace WebCore { 93 94using namespace HTMLNames; 95 96class JSGlobalDataWorldIterator { 97public: 98 JSGlobalDataWorldIterator(JSGlobalData* globalData) 99 : m_pos(static_cast<WebCoreJSClientData*>(globalData->clientData)->m_worldSet.begin()) 100 , m_end(static_cast<WebCoreJSClientData*>(globalData->clientData)->m_worldSet.end()) 101 { 102 } 103 104 operator bool() 105 { 106 return m_pos != m_end; 107 } 108 109 DOMWrapperWorld* operator*() 110 { 111 ASSERT(m_pos != m_end); 112 return *m_pos; 113 } 114 115 DOMWrapperWorld* operator->() 116 { 117 ASSERT(m_pos != m_end); 118 return *m_pos; 119 } 120 121 JSGlobalDataWorldIterator& operator++() 122 { 123 ++m_pos; 124 return *this; 125 } 126 127private: 128 HashSet<DOMWrapperWorld*>::iterator m_pos; 129 HashSet<DOMWrapperWorld*>::iterator m_end; 130}; 131 132const JSC::HashTable* getHashTableForGlobalData(JSGlobalData& globalData, const JSC::HashTable* staticTable) 133{ 134 return DOMObjectHashTableMap::mapFor(globalData).get(staticTable); 135} 136 137void markActiveObjectsForContext(MarkStack& markStack, JSGlobalData& globalData, ScriptExecutionContext* scriptExecutionContext) 138{ 139 // If an element has pending activity that may result in event listeners being called 140 // (e.g. an XMLHttpRequest), we need to keep JS wrappers alive. 141 142 const HashMap<ActiveDOMObject*, void*>& activeObjects = scriptExecutionContext->activeDOMObjects(); 143 HashMap<ActiveDOMObject*, void*>::const_iterator activeObjectsEnd = activeObjects.end(); 144 for (HashMap<ActiveDOMObject*, void*>::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) { 145 if (iter->first->hasPendingActivity()) { 146 // Generally, an active object with pending activity must have a wrapper to mark its listeners. 147 // However, some ActiveDOMObjects don't have JS wrappers. 148 markDOMObjectWrapper(markStack, globalData, iter->second); 149 } 150 } 151 152 const HashSet<MessagePort*>& messagePorts = scriptExecutionContext->messagePorts(); 153 HashSet<MessagePort*>::const_iterator portsEnd = messagePorts.end(); 154 for (HashSet<MessagePort*>::const_iterator iter = messagePorts.begin(); iter != portsEnd; ++iter) { 155 // If the message port is remotely entangled, then always mark it as in-use because we can't determine reachability across threads. 156 if (!(*iter)->locallyEntangledPort() || (*iter)->hasPendingActivity()) 157 markDOMObjectWrapper(markStack, globalData, *iter); 158 } 159} 160 161void markDOMObjectWrapper(MarkStack& markStack, JSGlobalData& globalData, void* object) 162{ 163 // FIXME: This could be changed to only mark wrappers that are "observable" 164 // as markDOMNodesForDocument does, allowing us to collect more wrappers, 165 // but doing this correctly would be challenging. 166 if (!object) 167 return; 168 169 for (JSGlobalDataWorldIterator worldIter(&globalData); worldIter; ++worldIter) { 170 if (JSDOMWrapper* wrapper = worldIter->m_wrappers.get(object).get()) 171 markStack.deprecatedAppend(reinterpret_cast<JSCell**>(&wrapper)); 172 } 173} 174 175static void stringWrapperDestroyed(JSString*, void* context) 176{ 177 StringImpl* cacheKey = static_cast<StringImpl*>(context); 178 cacheKey->deref(); 179} 180 181JSValue jsStringSlowCase(ExecState* exec, JSStringCache& stringCache, StringImpl* stringImpl) 182{ 183 JSString* wrapper = jsStringWithFinalizer(exec, UString(stringImpl), stringWrapperDestroyed, stringImpl); 184 stringCache.set(exec->globalData(), stringImpl, wrapper); 185 // ref explicitly instead of using a RefPtr-keyed hashtable because the wrapper can 186 // outlive the cache, so the stringImpl has to match the wrapper's lifetime. 187 stringImpl->ref(); 188 return wrapper; 189} 190 191JSValue jsStringOrNull(ExecState* exec, const String& s) 192{ 193 if (s.isNull()) 194 return jsNull(); 195 return jsString(exec, s); 196} 197 198JSValue jsOwnedStringOrNull(ExecState* exec, const String& s) 199{ 200 if (s.isNull()) 201 return jsNull(); 202 return jsOwnedString(exec, stringToUString(s)); 203} 204 205JSValue jsStringOrUndefined(ExecState* exec, const String& s) 206{ 207 if (s.isNull()) 208 return jsUndefined(); 209 return jsString(exec, s); 210} 211 212JSValue jsStringOrFalse(ExecState* exec, const String& s) 213{ 214 if (s.isNull()) 215 return jsBoolean(false); 216 return jsString(exec, s); 217} 218 219JSValue jsString(ExecState* exec, const KURL& url) 220{ 221 return jsString(exec, url.string()); 222} 223 224JSValue jsStringOrNull(ExecState* exec, const KURL& url) 225{ 226 if (url.isNull()) 227 return jsNull(); 228 return jsString(exec, url.string()); 229} 230 231JSValue jsStringOrUndefined(ExecState* exec, const KURL& url) 232{ 233 if (url.isNull()) 234 return jsUndefined(); 235 return jsString(exec, url.string()); 236} 237 238JSValue jsStringOrFalse(ExecState* exec, const KURL& url) 239{ 240 if (url.isNull()) 241 return jsBoolean(false); 242 return jsString(exec, url.string()); 243} 244 245AtomicStringImpl* findAtomicString(const Identifier& identifier) 246{ 247 if (identifier.isNull()) 248 return 0; 249 StringImpl* impl = identifier.impl(); 250 ASSERT(impl->existingHash()); 251 return AtomicString::find(impl->characters(), impl->length(), impl->existingHash()); 252} 253 254String valueToStringWithNullCheck(ExecState* exec, JSValue value) 255{ 256 if (value.isNull()) 257 return String(); 258 return ustringToString(value.toString(exec)); 259} 260 261String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value) 262{ 263 if (value.isUndefinedOrNull()) 264 return String(); 265 return ustringToString(value.toString(exec)); 266} 267 268JSValue jsDateOrNull(ExecState* exec, double value) 269{ 270 if (!isfinite(value)) 271 return jsNull(); 272 return new (exec) DateInstance(exec, exec->lexicalGlobalObject()->dateStructure(), value); 273} 274 275double valueToDate(ExecState* exec, JSValue value) 276{ 277 if (value.isNumber()) 278 return value.uncheckedGetNumber(); 279 if (!value.inherits(&DateInstance::s_info)) 280 return std::numeric_limits<double>::quiet_NaN(); 281 return static_cast<DateInstance*>(value.toObject(exec))->internalNumber(); 282} 283 284void reportException(ExecState* exec, JSValue exception) 285{ 286 if (exception.isObject() && asObject(exception)->exceptionType() == Terminated) 287 return; 288 289 UString errorMessage = exception.toString(exec); 290 JSObject* exceptionObject = exception.toObject(exec); 291 int lineNumber = exceptionObject->get(exec, Identifier(exec, "line")).toInt32(exec); 292 UString exceptionSourceURL = exceptionObject->get(exec, Identifier(exec, "sourceURL")).toString(exec); 293 exec->clearException(); 294 295 if (ExceptionBase* exceptionBase = toExceptionBase(exception)) 296 errorMessage = stringToUString(exceptionBase->message() + ": " + exceptionBase->description()); 297 298 ScriptExecutionContext* scriptExecutionContext = static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject())->scriptExecutionContext(); 299 ASSERT(scriptExecutionContext); 300 301 // Crash data indicates null-dereference crashes at this point in the Safari 4 Public Beta. 302 // It's harmless to return here without reporting the exception to the log and the debugger in this case. 303 if (!scriptExecutionContext) 304 return; 305 306 scriptExecutionContext->reportException(ustringToString(errorMessage), lineNumber, ustringToString(exceptionSourceURL), 0); 307} 308 309void reportCurrentException(ExecState* exec) 310{ 311 JSValue exception = exec->exception(); 312 exec->clearException(); 313 reportException(exec, exception); 314} 315 316void setDOMException(ExecState* exec, ExceptionCode ec) 317{ 318 if (!ec || exec->hadException()) 319 return; 320 321 // FIXME: All callers to setDOMException need to pass in the right global object 322 // for now, we're going to assume the lexicalGlobalObject. Which is wrong in cases like this: 323 // frames[0].document.createElement(null, null); // throws an exception which should have the subframes prototypes. 324 JSDOMGlobalObject* globalObject = deprecatedGlobalObjectForPrototype(exec); 325 326 ExceptionCodeDescription description; 327 getExceptionCodeDescription(ec, description); 328 329 JSValue errorObject; 330 switch (description.type) { 331 case DOMExceptionType: 332 errorObject = toJS(exec, globalObject, DOMCoreException::create(description)); 333 break; 334 case RangeExceptionType: 335 errorObject = toJS(exec, globalObject, RangeException::create(description)); 336 break; 337 case EventExceptionType: 338 errorObject = toJS(exec, globalObject, EventException::create(description)); 339 break; 340 case XMLHttpRequestExceptionType: 341 errorObject = toJS(exec, globalObject, XMLHttpRequestException::create(description)); 342 break; 343#if ENABLE(SVG) 344 case SVGExceptionType: 345 errorObject = toJS(exec, globalObject, SVGException::create(description).get()); 346 break; 347#endif 348#if ENABLE(XPATH) 349 case XPathExceptionType: 350 errorObject = toJS(exec, globalObject, XPathException::create(description)); 351 break; 352#endif 353#if ENABLE(DATABASE) 354 case SQLExceptionType: 355 errorObject = toJS(exec, globalObject, SQLException::create(description)); 356 break; 357#endif 358#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) 359 case FileExceptionType: 360 errorObject = toJS(exec, globalObject, FileException::create(description)); 361 break; 362#endif 363#if ENABLE(INDEXED_DATABASE) 364 case IDBDatabaseExceptionType: 365 errorObject = toJS(exec, globalObject, IDBDatabaseException::create(description)); 366 break; 367#endif 368 } 369 370 ASSERT(errorObject); 371 throwError(exec, errorObject); 372} 373 374DOMWindow* activeDOMWindow(ExecState* exec) 375{ 376 return asJSDOMWindow(exec->lexicalGlobalObject())->impl(); 377} 378 379DOMWindow* firstDOMWindow(ExecState* exec) 380{ 381 return asJSDOMWindow(exec->dynamicGlobalObject())->impl(); 382} 383 384bool checkNodeSecurity(ExecState* exec, Node* node) 385{ 386 return node && allowsAccessFromFrame(exec, node->document()->frame()); 387} 388 389bool allowsAccessFromFrame(ExecState* exec, Frame* frame) 390{ 391 if (!frame) 392 return false; 393 JSDOMWindow* window = toJSDOMWindow(frame, currentWorld(exec)); 394 return window && window->allowsAccessFrom(exec); 395} 396 397bool allowsAccessFromFrame(ExecState* exec, Frame* frame, String& message) 398{ 399 if (!frame) 400 return false; 401 JSDOMWindow* window = toJSDOMWindow(frame, currentWorld(exec)); 402 return window && window->allowsAccessFrom(exec, message); 403} 404 405void printErrorMessageForFrame(Frame* frame, const String& message) 406{ 407 if (!frame) 408 return; 409 frame->domWindow()->printErrorMessage(message); 410} 411 412// FIXME: We should remove or at least deprecate this function. Callers can use firstDOMWindow directly. 413Frame* toDynamicFrame(ExecState* exec) 414{ 415 return firstDOMWindow(exec)->frame(); 416} 417 418// FIXME: We should remove this function. Callers can use ScriptController directly. 419bool processingUserGesture() 420{ 421 return ScriptController::processingUserGesture(); 422} 423 424JSValue objectToStringFunctionGetter(ExecState* exec, JSValue, const Identifier& propertyName) 425{ 426 return new (exec) JSFunction(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->functionStructure(), 0, propertyName, objectProtoFuncToString); 427} 428 429Structure* getCachedDOMStructure(JSDOMGlobalObject* globalObject, const ClassInfo* classInfo) 430{ 431 JSDOMStructureMap& structures = globalObject->structures(); 432 return structures.get(classInfo).get(); 433} 434 435Structure* cacheDOMStructure(JSDOMGlobalObject* globalObject, Structure* structure, const ClassInfo* classInfo) 436{ 437 JSDOMStructureMap& structures = globalObject->structures(); 438 ASSERT(!structures.contains(classInfo)); 439 return structures.set(classInfo, WriteBarrier<Structure>(globalObject->globalData(), globalObject, structure)).first->second.get(); 440} 441 442JSC::JSObject* toJSSequence(ExecState* exec, JSValue value, unsigned& length) 443{ 444 JSObject* object = value.getObject(); 445 if (!object) { 446 throwTypeError(exec); 447 return 0; 448 } 449 JSValue lengthValue = object->get(exec, exec->propertyNames().length); 450 if (exec->hadException()) 451 return 0; 452 453 if (lengthValue.isUndefinedOrNull()) { 454 throwTypeError(exec); 455 return 0; 456 } 457 458 length = lengthValue.toUInt32(exec); 459 if (exec->hadException()) 460 return 0; 461 462 return object; 463} 464 465} // namespace WebCore 466