Home | History | Annotate | Download | only in js
      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