Warning, file /frameworks/khtml/src/ecma/kjs_data.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * This file is part of the KDE libraries 0003 * Copyright (C) 2010 Maksim Orlovich <maksim@kde.org> 0004 * 0005 * This library is free software; you can redistribute it and/or 0006 * modify it under the terms of the GNU Lesser General Public 0007 * License as published by the Free Software Foundation; either 0008 * version 2 of the License, or (at your option) any later version. 0009 * 0010 * This library is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 * Lesser General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU Lesser General Public 0016 * License along with this library; if not, write to the Free Software 0017 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0018 */ 0019 #include "khtml_part.h" 0020 #include "kjs_data.h" 0021 #include <dom/dom_exception.h> 0022 #include <kjs/array_instance.h> 0023 0024 #include <QSet> 0025 0026 using namespace DOM; 0027 using namespace khtml; 0028 0029 namespace KJS 0030 { 0031 0032 // HTML5 deep copy algorithm, as described in "2.7.5 Safe passing of structured data" 0033 static JSValue *cloneInternal(ExecState *exec, Interpreter *ctx, JSValue *in, QSet<JSObject *> &path) 0034 { 0035 if (exec->hadException()) { // e.g. OOM or DATA_CLONE_ERR 0036 return jsUndefined(); 0037 } 0038 0039 switch (in->type()) { 0040 case NumberType: 0041 case BooleanType: 0042 case UndefinedType: 0043 case NullType: 0044 case StringType: 0045 // Values -> can pass straight through. 0046 return in; 0047 0048 case ObjectType: { 0049 JSObject *obj = in->getObject(); 0050 0051 // Some things are handled by creating a new wrapper for their value; 0052 // this includes both JS-builtin types like autoboxing wrappers, and 0053 // those of HTML5 types we support that have deep cloning specified. 0054 // This goes through valueClone. 0055 if (JSObject *copy = obj->valueClone(ctx)) { 0056 return copy; 0057 } 0058 0059 // Otherwise, we can only clone if it it's an array or plain object 0060 // that isn't already on our path from the root 0061 if (path.contains(obj)) { 0062 setDOMException(exec, DOM::DOMException::DATA_CLONE_ERR); 0063 break; 0064 } 0065 0066 path.insert(obj); 0067 0068 JSObject *clone = nullptr; 0069 if (obj->inherits(&ArrayInstance::info)) { 0070 clone = new ArrayInstance(ctx->builtinArrayPrototype(), 0); 0071 } else if (!obj->classInfo()) { // plain object 0072 clone = new JSObject(ctx->builtinObjectPrototype()); 0073 } else { 0074 // Something complicated and native -> error out 0075 setDOMException(exec, DOM::DOMException::DATA_CLONE_ERR); 0076 break; 0077 } 0078 0079 // Copy over clones of properties 0080 PropertyNameArray props; 0081 obj->getOwnPropertyNames(exec, props, PropertyMap::ExcludeDontEnumProperties); 0082 for (PropertyNameArrayIterator i = props.begin(); i != props.end(); ++i) { 0083 JSValue *propVal = obj->get(exec, *i); 0084 clone->put(exec, *i, cloneInternal(exec, ctx, propVal, path)); // ### flags? 0085 } 0086 0087 path.remove(obj); 0088 0089 return clone; 0090 } 0091 0092 default: // shouldn't happen! 0093 setDOMException(exec, DOM::DOMException::DATA_CLONE_ERR); 0094 } 0095 0096 return jsUndefined(); 0097 } 0098 0099 JSValue *cloneData(ExecState *exec, JSValue *data) 0100 { 0101 QSet<JSObject *> visited; 0102 return cloneInternal(exec, exec->dynamicInterpreter(), data, visited); 0103 } 0104 0105 class JSMessageData : public DOM::MessageEventImpl::Data 0106 { 0107 public: 0108 DOM::MessageEventImpl::DataType messageDataType() const override 0109 { 0110 return DOM::MessageEventImpl::JS_VALUE; 0111 } 0112 0113 JSMessageData(JSValue *val): m_value(val) {} 0114 0115 ProtectedPtr<JSValue> m_value; 0116 }; 0117 0118 DOM::MessageEventImpl::Data *encapsulateMessageEventData(ExecState *exec, Interpreter *ctx, 0119 JSValue *data) 0120 { 0121 QSet<JSObject *> visited; 0122 JSValue *copy = cloneInternal(exec, ctx, data, visited); 0123 if (exec->hadException()) { 0124 return nullptr; 0125 } else { 0126 return new JSMessageData(copy); 0127 } 0128 } 0129 0130 JSValue *getMessageEventData(ExecState * /*exec*/, DOM::MessageEventImpl::Data *data) 0131 { 0132 if (data && data->messageDataType() == DOM::MessageEventImpl::JS_VALUE) { 0133 return static_cast<JSMessageData *>(data)->m_value.get(); 0134 } else { 0135 return jsUndefined(); 0136 } 0137 } 0138 0139 //------------------------------------------------------------------------------ 0140 DelayedPostMessage::DelayedPostMessage(KHTMLPart *_source, 0141 const QString &_sourceOrigin, 0142 const QString &_targetOrigin, 0143 JSValue *_payload): 0144 sourceOrigin(_sourceOrigin), targetOrigin(_targetOrigin), payload(_payload), source(_source) 0145 {} 0146 0147 void DelayedPostMessage::mark() 0148 { 0149 if (!payload->marked()) { 0150 payload->mark(); 0151 } 0152 } 0153 0154 bool DelayedPostMessage::execute(Window *w) 0155 { 0156 KHTMLPart *part = qobject_cast<KHTMLPart *>(w->part()); 0157 DOM::DocumentImpl *doc = part ? static_cast<DOM::DocumentImpl *>(part->document().handle()) : nullptr; 0158 KJSProxy *js = part ? KJSProxy::proxy(part) : nullptr; 0159 0160 // qCDebug(KHTML_LOG) << doc << js << sourceOrigin << targetOrigin; 0161 if (doc && js) { 0162 // Verify destination. 0163 bool safe = false; 0164 if (targetOrigin == QLatin1String("*")) { 0165 safe = true; 0166 } else { 0167 RefPtr<SecurityOrigin> targetCtx = 0168 SecurityOrigin::createFromString(targetOrigin); 0169 safe = doc->origin()->isSameSchemeHostPort(targetCtx.get()); 0170 } 0171 0172 if (safe) { 0173 RefPtr<MessageEventImpl> msg = new MessageEventImpl(); 0174 0175 DOM::MessageEventImpl::Data *data = 0176 encapsulateMessageEventData(js->interpreter()->globalExec(), 0177 js->interpreter(), payload); 0178 0179 msg->initMessageEvent("message", 0180 false, false, // doesn't bubble or cancel 0181 data, 0182 sourceOrigin, 0183 DOMString(), // lastEventId -- not here 0184 source.data()); 0185 doc->dispatchWindowEvent(msg.get()); 0186 } else { 0187 qCWarning(KHTML_LOG) << "PostMessage XSS check failed;" 0188 << "target mask:" << targetOrigin 0189 << "actual:" << doc->origin()->toString() 0190 << "source:" << sourceOrigin; 0191 } 0192 } 0193 0194 return true; 0195 } 0196 0197 } // namespace KJS 0198