1 /* 2 * Copyright (C) 1999-2001 Harri Porten (porten (at) kde.org) 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. 4 * Copyright (C) 2007 Samuel Weinig <sam (at) 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 90 using namespace JSC; 91 92 namespace WebCore { 93 94 using namespace HTMLNames; 95 96 class JSGlobalDataWorldIterator { 97 public: 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 127 private: 128 HashSet<DOMWrapperWorld*>::iterator m_pos; 129 HashSet<DOMWrapperWorld*>::iterator m_end; 130 }; 131 132 const JSC::HashTable* getHashTableForGlobalData(JSGlobalData& globalData, const JSC::HashTable* staticTable) 133 { 134 return DOMObjectHashTableMap::mapFor(globalData).get(staticTable); 135 } 136 137 void 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 161 void 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 175 static void stringWrapperDestroyed(JSString*, void* context) 176 { 177 StringImpl* cacheKey = static_cast<StringImpl*>(context); 178 cacheKey->deref(); 179 } 180 181 JSValue 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 191 JSValue jsStringOrNull(ExecState* exec, const String& s) 192 { 193 if (s.isNull()) 194 return jsNull(); 195 return jsString(exec, s); 196 } 197 198 JSValue jsOwnedStringOrNull(ExecState* exec, const String& s) 199 { 200 if (s.isNull()) 201 return jsNull(); 202 return jsOwnedString(exec, stringToUString(s)); 203 } 204 205 JSValue jsStringOrUndefined(ExecState* exec, const String& s) 206 { 207 if (s.isNull()) 208 return jsUndefined(); 209 return jsString(exec, s); 210 } 211 212 JSValue jsStringOrFalse(ExecState* exec, const String& s) 213 { 214 if (s.isNull()) 215 return jsBoolean(false); 216 return jsString(exec, s); 217 } 218 219 JSValue jsString(ExecState* exec, const KURL& url) 220 { 221 return jsString(exec, url.string()); 222 } 223 224 JSValue jsStringOrNull(ExecState* exec, const KURL& url) 225 { 226 if (url.isNull()) 227 return jsNull(); 228 return jsString(exec, url.string()); 229 } 230 231 JSValue jsStringOrUndefined(ExecState* exec, const KURL& url) 232 { 233 if (url.isNull()) 234 return jsUndefined(); 235 return jsString(exec, url.string()); 236 } 237 238 JSValue jsStringOrFalse(ExecState* exec, const KURL& url) 239 { 240 if (url.isNull()) 241 return jsBoolean(false); 242 return jsString(exec, url.string()); 243 } 244 245 AtomicStringImpl* 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 254 String valueToStringWithNullCheck(ExecState* exec, JSValue value) 255 { 256 if (value.isNull()) 257 return String(); 258 return ustringToString(value.toString(exec)); 259 } 260 261 String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value) 262 { 263 if (value.isUndefinedOrNull()) 264 return String(); 265 return ustringToString(value.toString(exec)); 266 } 267 268 JSValue 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 275 double 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 284 void 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 309 void reportCurrentException(ExecState* exec) 310 { 311 JSValue exception = exec->exception(); 312 exec->clearException(); 313 reportException(exec, exception); 314 } 315 316 void 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 374 DOMWindow* activeDOMWindow(ExecState* exec) 375 { 376 return asJSDOMWindow(exec->lexicalGlobalObject())->impl(); 377 } 378 379 DOMWindow* firstDOMWindow(ExecState* exec) 380 { 381 return asJSDOMWindow(exec->dynamicGlobalObject())->impl(); 382 } 383 384 bool checkNodeSecurity(ExecState* exec, Node* node) 385 { 386 return node && allowsAccessFromFrame(exec, node->document()->frame()); 387 } 388 389 bool 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 397 bool 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 405 void 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. 413 Frame* toDynamicFrame(ExecState* exec) 414 { 415 return firstDOMWindow(exec)->frame(); 416 } 417 418 // FIXME: We should remove this function. Callers can use ScriptController directly. 419 bool processingUserGesture() 420 { 421 return ScriptController::processingUserGesture(); 422 } 423 424 JSValue objectToStringFunctionGetter(ExecState* exec, JSValue, const Identifier& propertyName) 425 { 426 return new (exec) JSFunction(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->functionStructure(), 0, propertyName, objectProtoFuncToString); 427 } 428 429 Structure* getCachedDOMStructure(JSDOMGlobalObject* globalObject, const ClassInfo* classInfo) 430 { 431 JSDOMStructureMap& structures = globalObject->structures(); 432 return structures.get(classInfo).get(); 433 } 434 435 Structure* 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 442 JSC::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