2008-07-01 Cameron Zwarich <cwzwarich@uwaterloo.ca>
[WebKit-https.git] / WebCore / bindings / js / JSLocationCustom.cpp
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 "JSDOMWindowCustom.h"
30 #include "KURL.h"
31 #include "Location.h"
32 #include "JSDOMBinding.h"
33 #include "ScriptController.h"
34
35 using namespace KJS;
36
37 namespace WebCore {
38
39 bool JSLocation::customGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
40 {
41     Frame* frame = impl()->frame();
42     if (!frame) {
43         slot.setUndefined();
44         return true;
45     }
46
47     // When accessing Location cross-domain, functions are always the native built-in ones.
48     // See JSDOMWindow::customGetOwnPropertySlot for additional details.
49
50     // Our custom code is only needed to implement the Window cross-domain scheme, so if access is
51     // allowed, return false so the normal lookup will take place.
52     String message;
53     if (allowsAccessFromFrame(exec, frame, message))
54         return false;
55
56     // Check for the few functions that we allow, even when called cross-domain.
57     const HashEntry* entry = JSLocationPrototype::s_info.propHashTable(exec)->entry(exec, propertyName);
58     if (entry && (entry->attributes & Function)
59             && (entry->functionValue == jsLocationPrototypeFunctionReplace
60                 || entry->functionValue == jsLocationPrototypeFunctionReload
61                 || entry->functionValue == jsLocationPrototypeFunctionAssign)) {
62         slot.setStaticEntry(this, entry, nonCachingStaticFunctionGetter);
63         return true;
64     }
65
66     // FIXME: Other implementers of the Window cross-domain scheme (Window, History) allow toString,
67     // but for now we have decided not to, partly because it seems silly to return "[Object Location]" in
68     // such cases when normally the string form of Location would be the URL.
69
70     printErrorMessageForFrame(frame, message);
71     slot.setUndefined();
72     return true;
73 }
74
75 bool JSLocation::customPut(ExecState* exec, const Identifier& propertyName, JSValue* value)
76 {
77     Frame* frame = impl()->frame();
78     if (!frame)
79         return true;
80
81     bool sameDomainAccess = allowsAccessFromFrame(exec, frame);
82
83     const HashEntry* entry = JSLocation::s_info.propHashTable(exec)->entry(exec, propertyName);
84     if (!entry) {
85         if (sameDomainAccess)
86             JSObject::put(exec, propertyName, value);
87         return true;
88     }
89
90     // Cross-domain access to the location is allowed when assigning the whole location,
91     // but not when assigning the individual pieces, since that might inadvertently
92     // disclose other parts of the original location.
93     if (entry->integerValue != HrefAttrNum && !sameDomainAccess)
94         return true;
95
96     return false;
97 }
98
99 bool JSLocation::deleteProperty(ExecState* exec, const Identifier& propertyName)
100 {
101     // Only allow deleting by frames in the same origin.
102     if (!allowsAccessFromFrame(exec, impl()->frame()))
103         return false;
104     return Base::deleteProperty(exec, propertyName);
105 }
106
107 bool JSLocation::customGetPropertyNames(ExecState* exec, PropertyNameArray&)
108 {
109     // Only allow the location object to enumerated by frames in the same origin.
110     if (!allowsAccessFromFrame(exec, impl()->frame()))
111         return true;
112     return false;
113 }
114
115 static void navigateIfAllowed(ExecState* exec, Frame* frame, const KURL& url, bool lockHistory)
116 {
117     Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
118     if (!url.protocolIs("javascript") || allowsAccessFromFrame(exec, frame)) {
119         bool userGesture = activeFrame->script()->processingUserGesture();
120         frame->loader()->scheduleLocationChange(url.string(), activeFrame->loader()->outgoingReferrer(), lockHistory, userGesture);
121     }
122 }
123
124 void JSLocation::setHref(ExecState* exec, JSValue* value)
125 {
126     Frame* frame = impl()->frame();
127     ASSERT(frame);
128
129     Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
130     if (!activeFrame)
131         return;
132     if (!activeFrame->loader()->shouldAllowNavigation(frame))
133         return;
134
135     KURL url = activeFrame->loader()->completeURL(value->toString(exec));
136     navigateIfAllowed(exec, frame, url, false);
137 }
138
139 void JSLocation::setProtocol(ExecState* exec, JSValue* value)
140 {
141     Frame* frame = impl()->frame();
142     ASSERT(frame);
143
144     KURL url = frame->loader()->url();
145     url.setProtocol(value->toString(exec));
146
147     navigateIfAllowed(exec, frame, url, false);
148 }
149
150 void JSLocation::setHost(ExecState* exec, JSValue* value)
151 {
152     Frame* frame = impl()->frame();
153     ASSERT(frame);
154
155     KURL url = frame->loader()->url();
156     url.setHostAndPort(value->toString(exec));
157
158     navigateIfAllowed(exec, frame, url, false);
159 }
160
161 void JSLocation::setHostname(ExecState* exec, JSValue* value)
162 {
163     Frame* frame = impl()->frame();
164     ASSERT(frame);
165
166     KURL url = frame->loader()->url();
167     url.setHost(value->toString(exec));
168
169     navigateIfAllowed(exec, frame, url, false);
170 }
171
172 void JSLocation::setPort(ExecState* exec, JSValue* value)
173 {
174     Frame* frame = impl()->frame();
175     ASSERT(frame);
176
177     KURL url = frame->loader()->url();
178     // FIXME: Could make this a little less ugly if String provided a toUnsignedShort function.
179     const UString& portString = value->toString(exec);
180     int port = charactersToInt(portString.data(), portString.size());
181     if (port < 0 || port > 0xFFFF)
182         port = 0;
183     url.setPort(port);
184
185     navigateIfAllowed(exec, frame, url, false);
186 }
187
188 void JSLocation::setPathname(ExecState* exec, JSValue* value)
189 {
190     Frame* frame = impl()->frame();
191     ASSERT(frame);
192
193     KURL url = frame->loader()->url();
194     url.setPath(value->toString(exec));
195
196     navigateIfAllowed(exec, frame, url, false);
197 }
198
199 void JSLocation::setSearch(ExecState* exec, JSValue* value)
200 {
201     Frame* frame = impl()->frame();
202     ASSERT(frame);
203
204     KURL url = frame->loader()->url();
205     url.setQuery(value->toString(exec));
206
207     navigateIfAllowed(exec, frame, url, false);
208 }
209
210 void JSLocation::setHash(ExecState* exec, JSValue* value)
211 {
212     Frame* frame = impl()->frame();
213     ASSERT(frame);
214
215     KURL url = frame->loader()->url();
216     String oldRef = url.ref();
217     String str = value->toString(exec);
218     if (str.startsWith("#"))
219         str = str.substring(1);
220     if (url.ref() == str || (oldRef.isNull() && str.isEmpty()))
221         return;
222     url.setRef(str);
223
224     navigateIfAllowed(exec, frame, url, false);
225 }
226
227 JSValue* JSLocation::replace(ExecState* exec, const ArgList& args)
228 {
229     Frame* frame = impl()->frame();
230     if (!frame)
231         return jsUndefined();
232
233     Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
234     if (!activeFrame) 
235         return jsUndefined();
236     if (!activeFrame->loader()->shouldAllowNavigation(frame))
237         return jsUndefined();
238
239     navigateIfAllowed(exec, frame, activeFrame->loader()->completeURL(args[0]->toString(exec)), true);
240     return jsUndefined();
241 }
242
243 JSValue* JSLocation::reload(ExecState* exec, const ArgList& args)
244 {
245     Frame* frame = impl()->frame();
246     if (!frame)
247         return jsUndefined();
248
249     JSDOMWindow* window = toJSDOMWindow(frame);
250     if (!window->allowsAccessFrom(exec))
251         return jsUndefined();
252
253     if (!frame->loader()->url().protocolIs("javascript") || (window && window->allowsAccessFrom(exec))) {
254         bool userGesture = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame()->script()->processingUserGesture();
255         frame->loader()->scheduleRefresh(userGesture);
256     }
257     return jsUndefined();
258 }
259
260 JSValue* JSLocation::assign(ExecState* exec, const ArgList& args)
261 {
262     Frame* frame = impl()->frame();
263     if (!frame)
264         return jsUndefined();
265
266     Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
267     if (!activeFrame)
268         return jsUndefined();
269     if (!activeFrame->loader()->shouldAllowNavigation(frame))
270         return jsUndefined();
271
272     // We want a new history item if this JS was called via a user gesture
273     navigateIfAllowed(exec, frame, activeFrame->loader()->completeURL(args[0]->toString(exec)), false);
274     return jsUndefined();
275 }
276
277 JSValue* JSLocation::toString(ExecState* exec, const ArgList&)
278 {
279     Frame* frame = impl()->frame();
280     if (!frame)
281         return jsUndefined();
282     if (!allowsAccessFromFrame(exec, frame))
283         return jsUndefined();
284
285     return jsString(exec, impl()->toString());
286 }
287
288 } // namespace WebCore