1 /*
2 * Copyright (C) 2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2006 Jon Shier (jshier@iastate.edu)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reseved.
5 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20 * USA
21 */
22
23 #include "config.h"
24 #include "JSLocationCustom.h"
25
26 #include "DOMWindow.h"
27 #include "Frame.h"
28 #include "FrameLoader.h"
29 #include "JSDOMBinding.h"
30 #include "JSDOMWindowCustom.h"
31 #include "KURL.h"
32 #include "Location.h"
33 #include "ScriptController.h"
34 #include <runtime/PrototypeFunction.h>
35
36 using namespace JSC;
37
38 namespace WebCore {
39
nonCachingStaticReplaceFunctionGetter(ExecState * exec,const Identifier & propertyName,const PropertySlot &)40 static JSValue nonCachingStaticReplaceFunctionGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot&)
41 {
42 return new (exec) NativeFunctionWrapper(exec, exec->lexicalGlobalObject()->prototypeFunctionStructure(), 1, propertyName, jsLocationPrototypeFunctionReplace);
43 }
44
nonCachingStaticReloadFunctionGetter(ExecState * exec,const Identifier & propertyName,const PropertySlot &)45 static JSValue nonCachingStaticReloadFunctionGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot&)
46 {
47 return new (exec) NativeFunctionWrapper(exec, exec->lexicalGlobalObject()->prototypeFunctionStructure(), 0, propertyName, jsLocationPrototypeFunctionReload);
48 }
49
nonCachingStaticAssignFunctionGetter(ExecState * exec,const Identifier & propertyName,const PropertySlot &)50 static JSValue nonCachingStaticAssignFunctionGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot&)
51 {
52 return new (exec) NativeFunctionWrapper(exec, exec->lexicalGlobalObject()->prototypeFunctionStructure(), 1, propertyName, jsLocationPrototypeFunctionAssign);
53 }
54
getOwnPropertySlotDelegate(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)55 bool JSLocation::getOwnPropertySlotDelegate(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
56 {
57 Frame* frame = impl()->frame();
58 if (!frame) {
59 slot.setUndefined();
60 return true;
61 }
62
63 // When accessing Location cross-domain, functions are always the native built-in ones.
64 // See JSDOMWindow::getOwnPropertySlotDelegate for additional details.
65
66 // Our custom code is only needed to implement the Window cross-domain scheme, so if access is
67 // allowed, return false so the normal lookup will take place.
68 String message;
69 if (allowsAccessFromFrame(exec, frame, message))
70 return false;
71
72 // Check for the few functions that we allow, even when called cross-domain.
73 const HashEntry* entry = JSLocationPrototype::s_info.propHashTable(exec)->entry(exec, propertyName);
74 if (entry && (entry->attributes() & Function)) {
75 if (entry->function() == jsLocationPrototypeFunctionReplace) {
76 slot.setCustom(this, nonCachingStaticReplaceFunctionGetter);
77 return true;
78 } else if (entry->function() == jsLocationPrototypeFunctionReload) {
79 slot.setCustom(this, nonCachingStaticReloadFunctionGetter);
80 return true;
81 } else if (entry->function() == jsLocationPrototypeFunctionAssign) {
82 slot.setCustom(this, nonCachingStaticAssignFunctionGetter);
83 return true;
84 }
85 }
86
87 // FIXME: Other implementers of the Window cross-domain scheme (Window, History) allow toString,
88 // but for now we have decided not to, partly because it seems silly to return "[Object Location]" in
89 // such cases when normally the string form of Location would be the URL.
90
91 printErrorMessageForFrame(frame, message);
92 slot.setUndefined();
93 return true;
94 }
95
putDelegate(ExecState * exec,const Identifier & propertyName,JSValue value,PutPropertySlot & slot)96 bool JSLocation::putDelegate(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
97 {
98 Frame* frame = impl()->frame();
99 if (!frame)
100 return true;
101
102 if (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf)
103 return true;
104
105 bool sameDomainAccess = allowsAccessFromFrame(exec, frame);
106
107 const HashEntry* entry = JSLocation::s_info.propHashTable(exec)->entry(exec, propertyName);
108 if (!entry) {
109 if (sameDomainAccess)
110 JSObject::put(exec, propertyName, value, slot);
111 return true;
112 }
113
114 // Cross-domain access to the location is allowed when assigning the whole location,
115 // but not when assigning the individual pieces, since that might inadvertently
116 // disclose other parts of the original location.
117 if (entry->propertyPutter() != setJSLocationHref && !sameDomainAccess)
118 return true;
119
120 return false;
121 }
122
deleteProperty(ExecState * exec,const Identifier & propertyName)123 bool JSLocation::deleteProperty(ExecState* exec, const Identifier& propertyName)
124 {
125 // Only allow deleting by frames in the same origin.
126 if (!allowsAccessFromFrame(exec, impl()->frame()))
127 return false;
128 return Base::deleteProperty(exec, propertyName);
129 }
130
getPropertyNames(ExecState * exec,PropertyNameArray & propertyNames)131 void JSLocation::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
132 {
133 // Only allow the location object to enumerated by frames in the same origin.
134 if (!allowsAccessFromFrame(exec, impl()->frame()))
135 return;
136 Base::getPropertyNames(exec, propertyNames);
137 }
138
defineGetter(ExecState * exec,const Identifier & propertyName,JSObject * getterFunction)139 void JSLocation::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
140 {
141 if (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf)
142 return;
143 Base::defineGetter(exec, propertyName, getterFunction);
144 }
145
navigateIfAllowed(ExecState * exec,Frame * frame,const KURL & url,bool lockHistory,bool lockBackForwardList)146 static void navigateIfAllowed(ExecState* exec, Frame* frame, const KURL& url, bool lockHistory, bool lockBackForwardList)
147 {
148 Frame* lexicalFrame = toLexicalFrame(exec);
149 if (!lexicalFrame)
150 return;
151
152 if (!protocolIsJavaScript(url) || allowsAccessFromFrame(exec, frame))
153 frame->loader()->scheduleLocationChange(url.string(), lexicalFrame->loader()->outgoingReferrer(), lockHistory, lockBackForwardList, processingUserGesture(exec));
154 }
155
setHref(ExecState * exec,JSValue value)156 void JSLocation::setHref(ExecState* exec, JSValue value)
157 {
158 Frame* frame = impl()->frame();
159 ASSERT(frame);
160
161 if (!shouldAllowNavigation(exec, frame))
162 return;
163
164 KURL url = completeURL(exec, value.toString(exec));
165 if (url.isNull())
166 return;
167
168 navigateIfAllowed(exec, frame, url, !frame->script()->anyPageIsProcessingUserGesture(), false);
169 }
170
setProtocol(ExecState * exec,JSValue value)171 void JSLocation::setProtocol(ExecState* exec, JSValue value)
172 {
173 Frame* frame = impl()->frame();
174 ASSERT(frame);
175
176 KURL url = frame->loader()->url();
177 url.setProtocol(value.toString(exec));
178
179 navigateIfAllowed(exec, frame, url, !frame->script()->anyPageIsProcessingUserGesture(), false);
180 }
181
setHost(ExecState * exec,JSValue value)182 void JSLocation::setHost(ExecState* exec, JSValue value)
183 {
184 Frame* frame = impl()->frame();
185 ASSERT(frame);
186
187 KURL url = frame->loader()->url();
188 url.setHostAndPort(value.toString(exec));
189
190 navigateIfAllowed(exec, frame, url, !frame->script()->anyPageIsProcessingUserGesture(), false);
191 }
192
setHostname(ExecState * exec,JSValue value)193 void JSLocation::setHostname(ExecState* exec, JSValue value)
194 {
195 Frame* frame = impl()->frame();
196 ASSERT(frame);
197
198 KURL url = frame->loader()->url();
199 url.setHost(value.toString(exec));
200
201 navigateIfAllowed(exec, frame, url, !frame->script()->anyPageIsProcessingUserGesture(), false);
202 }
203
setPort(ExecState * exec,JSValue value)204 void JSLocation::setPort(ExecState* exec, JSValue value)
205 {
206 Frame* frame = impl()->frame();
207 ASSERT(frame);
208
209 KURL url = frame->loader()->url();
210 // FIXME: Could make this a little less ugly if String provided a toUnsignedShort function.
211 const UString& portString = value.toString(exec);
212 int port = charactersToInt(portString.data(), portString.size());
213 if (port < 0 || port > 0xFFFF)
214 port = 0;
215 url.setPort(port);
216
217 navigateIfAllowed(exec, frame, url, !frame->script()->anyPageIsProcessingUserGesture(), false);
218 }
219
setPathname(ExecState * exec,JSValue value)220 void JSLocation::setPathname(ExecState* exec, JSValue value)
221 {
222 Frame* frame = impl()->frame();
223 ASSERT(frame);
224
225 KURL url = frame->loader()->url();
226 url.setPath(value.toString(exec));
227
228 navigateIfAllowed(exec, frame, url, !frame->script()->anyPageIsProcessingUserGesture(), false);
229 }
230
setSearch(ExecState * exec,JSValue value)231 void JSLocation::setSearch(ExecState* exec, JSValue value)
232 {
233 Frame* frame = impl()->frame();
234 ASSERT(frame);
235
236 KURL url = frame->loader()->url();
237 url.setQuery(value.toString(exec));
238
239 navigateIfAllowed(exec, frame, url, !frame->script()->anyPageIsProcessingUserGesture(), false);
240 }
241
setHash(ExecState * exec,JSValue value)242 void JSLocation::setHash(ExecState* exec, JSValue value)
243 {
244 Frame* frame = impl()->frame();
245 ASSERT(frame);
246
247 KURL url = frame->loader()->url();
248 String oldFragmentIdentifier = url.fragmentIdentifier();
249 String str = value.toString(exec);
250 if (str.startsWith("#"))
251 str = str.substring(1);
252 if (equalIgnoringNullity(oldFragmentIdentifier, str))
253 return;
254 url.setFragmentIdentifier(str);
255
256 navigateIfAllowed(exec, frame, url, !frame->script()->anyPageIsProcessingUserGesture(), false);
257 }
258
replace(ExecState * exec,const ArgList & args)259 JSValue JSLocation::replace(ExecState* exec, const ArgList& args)
260 {
261 Frame* frame = impl()->frame();
262 if (!frame)
263 return jsUndefined();
264
265 if (!shouldAllowNavigation(exec, frame))
266 return jsUndefined();
267
268 KURL url = completeURL(exec, args.at(0).toString(exec));
269 if (url.isNull())
270 return jsUndefined();
271
272 navigateIfAllowed(exec, frame, url, true, true);
273 return jsUndefined();
274 }
275
reload(ExecState * exec,const ArgList &)276 JSValue JSLocation::reload(ExecState* exec, const ArgList&)
277 {
278 Frame* frame = impl()->frame();
279 if (!frame || !allowsAccessFromFrame(exec, frame))
280 return jsUndefined();
281
282 if (!protocolIsJavaScript(frame->loader()->url()))
283 frame->loader()->scheduleRefresh(processingUserGesture(exec));
284 return jsUndefined();
285 }
286
assign(ExecState * exec,const ArgList & args)287 JSValue JSLocation::assign(ExecState* exec, const ArgList& args)
288 {
289 Frame* frame = impl()->frame();
290 if (!frame)
291 return jsUndefined();
292
293 if (!shouldAllowNavigation(exec, frame))
294 return jsUndefined();
295
296 KURL url = completeURL(exec, args.at(0).toString(exec));
297 if (url.isNull())
298 return jsUndefined();
299
300 // We want a new history item if this JS was called via a user gesture
301 navigateIfAllowed(exec, frame, url, !frame->script()->anyPageIsProcessingUserGesture(), false);
302 return jsUndefined();
303 }
304
toString(ExecState * exec,const ArgList &)305 JSValue JSLocation::toString(ExecState* exec, const ArgList&)
306 {
307 Frame* frame = impl()->frame();
308 if (!frame || !allowsAccessFromFrame(exec, frame))
309 return jsUndefined();
310
311 return jsString(exec, impl()->toString());
312 }
313
putDelegate(ExecState * exec,const Identifier & propertyName,JSValue,PutPropertySlot &)314 bool JSLocationPrototype::putDelegate(ExecState* exec, const Identifier& propertyName, JSValue, PutPropertySlot&)
315 {
316 return (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf);
317 }
318
defineGetter(ExecState * exec,const Identifier & propertyName,JSObject * getterFunction)319 void JSLocationPrototype::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
320 {
321 if (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf)
322 return;
323 Base::defineGetter(exec, propertyName, getterFunction);
324 }
325
326 } // namespace WebCore
327