KHtml

kjs_data.cpp
1 /*
2  * This file is part of the KDE libraries
3  * Copyright (C) 2010 Maksim Orlovich <[email protected]>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 #include "khtml_part.h"
20 #include "kjs_data.h"
21 #include <dom/dom_exception.h>
22 #include <kjs/array_instance.h>
23 
24 #include <QSet>
25 
26 using namespace DOM;
27 using namespace khtml;
28 
29 namespace KJS
30 {
31 
32 // HTML5 deep copy algorithm, as described in "2.7.5 Safe passing of structured data"
33 static JSValue *cloneInternal(ExecState *exec, Interpreter *ctx, JSValue *in, QSet<JSObject *> &path)
34 {
35  if (exec->hadException()) { // e.g. OOM or DATA_CLONE_ERR
36  return jsUndefined();
37  }
38 
39  switch (in->type()) {
40  case NumberType:
41  case BooleanType:
42  case UndefinedType:
43  case NullType:
44  case StringType:
45  // Values -> can pass straight through.
46  return in;
47 
48  case ObjectType: {
49  JSObject *obj = in->getObject();
50 
51  // Some things are handled by creating a new wrapper for their value;
52  // this includes both JS-builtin types like autoboxing wrappers, and
53  // those of HTML5 types we support that have deep cloning specified.
54  // This goes through valueClone.
55  if (JSObject *copy = obj->valueClone(ctx)) {
56  return copy;
57  }
58 
59  // Otherwise, we can only clone if it it's an array or plain object
60  // that isn't already on our path from the root
61  if (path.contains(obj)) {
62  setDOMException(exec, DOM::DOMException::DATA_CLONE_ERR);
63  break;
64  }
65 
66  path.insert(obj);
67 
68  JSObject *clone = nullptr;
69  if (obj->inherits(&ArrayInstance::info)) {
70  clone = new ArrayInstance(ctx->builtinArrayPrototype(), 0);
71  } else if (!obj->classInfo()) { // plain object
72  clone = new JSObject(ctx->builtinObjectPrototype());
73  } else {
74  // Something complicated and native -> error out
75  setDOMException(exec, DOM::DOMException::DATA_CLONE_ERR);
76  break;
77  }
78 
79  // Copy over clones of properties
80  PropertyNameArray props;
81  obj->getOwnPropertyNames(exec, props, PropertyMap::ExcludeDontEnumProperties);
82  for (PropertyNameArrayIterator i = props.begin(); i != props.end(); ++i) {
83  JSValue *propVal = obj->get(exec, *i);
84  clone->put(exec, *i, cloneInternal(exec, ctx, propVal, path)); // ### flags?
85  }
86 
87  path.remove(obj);
88 
89  return clone;
90  }
91 
92  default: // shouldn't happen!
93  setDOMException(exec, DOM::DOMException::DATA_CLONE_ERR);
94  }
95 
96  return jsUndefined();
97 }
98 
99 JSValue *cloneData(ExecState *exec, JSValue *data)
100 {
101  QSet<JSObject *> visited;
102  return cloneInternal(exec, exec->dynamicInterpreter(), data, visited);
103 }
104 
105 class JSMessageData : public DOM::MessageEventImpl::Data
106 {
107 public:
108  DOM::MessageEventImpl::DataType messageDataType() const override
109  {
110  return DOM::MessageEventImpl::JS_VALUE;
111  }
112 
113  JSMessageData(JSValue *val): m_value(val) {}
114 
115  ProtectedPtr<JSValue> m_value;
116 };
117 
118 DOM::MessageEventImpl::Data *encapsulateMessageEventData(ExecState *exec, Interpreter *ctx,
119  JSValue *data)
120 {
121  QSet<JSObject *> visited;
122  JSValue *copy = cloneInternal(exec, ctx, data, visited);
123  if (exec->hadException()) {
124  return nullptr;
125  } else {
126  return new JSMessageData(copy);
127  }
128 }
129 
130 JSValue *getMessageEventData(ExecState * /*exec*/, DOM::MessageEventImpl::Data *data)
131 {
132  if (data && data->messageDataType() == DOM::MessageEventImpl::JS_VALUE) {
133  return static_cast<JSMessageData *>(data)->m_value.get();
134  } else {
135  return jsUndefined();
136  }
137 }
138 
139 //------------------------------------------------------------------------------
140 DelayedPostMessage::DelayedPostMessage(KHTMLPart *_source,
141  const QString &_sourceOrigin,
142  const QString &_targetOrigin,
143  JSValue *_payload):
144  sourceOrigin(_sourceOrigin), targetOrigin(_targetOrigin), payload(_payload), source(_source)
145 {}
146 
147 void DelayedPostMessage::mark()
148 {
149  if (!payload->marked()) {
150  payload->mark();
151  }
152 }
153 
154 bool DelayedPostMessage::execute(Window *w)
155 {
156  KHTMLPart *part = qobject_cast<KHTMLPart *>(w->part());
157  DOM::DocumentImpl *doc = part ? static_cast<DOM::DocumentImpl *>(part->document().handle()) : nullptr;
158  KJSProxy *js = part ? KJSProxy::proxy(part) : nullptr;
159 
160  // qCDebug(KHTML_LOG) << doc << js << sourceOrigin << targetOrigin;
161  if (doc && js) {
162  // Verify destination.
163  bool safe = false;
164  if (targetOrigin == QLatin1String("*")) {
165  safe = true;
166  } else {
167  RefPtr<SecurityOrigin> targetCtx =
168  SecurityOrigin::createFromString(targetOrigin);
169  safe = doc->origin()->isSameSchemeHostPort(targetCtx.get());
170  }
171 
172  if (safe) {
173  RefPtr<MessageEventImpl> msg = new MessageEventImpl();
174 
175  DOM::MessageEventImpl::Data *data =
176  encapsulateMessageEventData(js->interpreter()->globalExec(),
177  js->interpreter(), payload);
178 
179  msg->initMessageEvent("message",
180  false, false, // doesn't bubble or cancel
181  data,
182  sourceOrigin,
183  DOMString(), // lastEventId -- not here
184  source.data());
185  doc->dispatchWindowEvent(msg.get());
186  } else {
187  qCWarning(KHTML_LOG) << "PostMessage XSS check failed;"
188  << "target mask:" << targetOrigin
189  << "actual:" << doc->origin()->toString()
190  << "source:" << sourceOrigin;
191  }
192  }
193 
194  return true;
195 }
196 
197 } // namespace KJS
198 
This file is part of the HTML rendering engine for KDE.
Proxy class serving as interface when being dlopen&#39;ed.
Definition: kjs_proxy.h:61
This class is khtml&#39;s main class.
Definition: khtml_part.h:208
QSet::iterator insert(const T &value)
KIOCORE_EXPORT CopyJob * copy(const QUrl &src, const QUrl &dest, JobFlags flags=DefaultFlags)
This class implements the basic string we use in the DOM.
Definition: dom_string.h:44
bool contains(const T &value) const const
This library provides a full-featured HTML parser and widget.
bool remove(const T &value)
ObjectType
DOM::Document document() const
Returns a reference to the DOM document.
NodeImpl * handle() const
Definition: dom_node.h:936
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:05 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.