77be340a040df1c73d8f13338453d2144563686c
[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 str = value->toString(exec);
217     if (str.startsWith("#"))
218         str = str.substring(1);
219     if (url.ref() == str)
220         return;
221     url.setRef(str);
222
223     navigateIfAllowed(exec, frame, url, false);
224 }
225
226 JSValue* JSLocation::replace(ExecState* exec, const ArgList& args)
227 {
228     Frame* frame = impl()->frame();
229     if (!frame)
230         return jsUndefined();
231
232     Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
233     if (!activeFrame) 
234         return jsUndefined();
235     if (!activeFrame->loader()->shouldAllowNavigation(frame))
236         return jsUndefined();
237
238     navigateIfAllowed(exec, frame, activeFrame->loader()->completeURL(args[0]->toString(exec)), true);
239     return jsUndefined();
240 }
241
242 JSValue* JSLocation::reload(ExecState* exec, const ArgList& args)
243 {
244     Frame* frame = impl()->frame();
245     if (!frame)
246         return jsUndefined();
247
248     JSDOMWindow* window = toJSDOMWindow(frame);
249     if (!window->allowsAccessFrom(exec))
250         return jsUndefined();
251
252     if (!frame->loader()->url().protocolIs("javascript") || (window && window->allowsAccessFrom(exec))) {
253         bool userGesture = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame()->script()->processingUserGesture();
254         frame->loader()->scheduleRefresh(userGesture);
255     }
256     return jsUndefined();
257 }
258
259 JSValue* JSLocation::assign(ExecState* exec, const ArgList& args)
260 {
261     Frame* frame = impl()->frame();
262     if (!frame)
263         return jsUndefined();
264
265     Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
266     if (!activeFrame)
267         return jsUndefined();
268     if (!activeFrame->loader()->shouldAllowNavigation(frame))
269         return jsUndefined();
270
271     // We want a new history item if this JS was called via a user gesture
272     navigateIfAllowed(exec, frame, activeFrame->loader()->completeURL(args[0]->toString(exec)), false);
273     return jsUndefined();
274 }
275
276 JSValue* JSLocation::toString(ExecState* exec, const ArgList&)
277 {
278     Frame* frame = impl()->frame();
279     if (!frame)
280         return jsUndefined();
281     if (!allowsAccessFromFrame(exec, frame))
282         return jsUndefined();
283
284     return jsString(exec, impl()->toString());
285 }
286
287 } // namespace WebCore