• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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