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, 2010 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 "JSLocation.h"
25
26#include "JSDOMBinding.h"
27#include <runtime/JSFunction.h>
28
29using namespace JSC;
30
31namespace WebCore {
32
33bool JSLocation::getOwnPropertySlotDelegate(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
34{
35    Frame* frame = impl().frame();
36    if (!frame) {
37        slot.setUndefined();
38        return true;
39    }
40
41    // When accessing Location cross-domain, functions are always the native built-in ones.
42    // See JSDOMWindow::getOwnPropertySlotDelegate for additional details.
43
44    // Our custom code is only needed to implement the Window cross-domain scheme, so if access is
45    // allowed, return false so the normal lookup will take place.
46    String message;
47    if (shouldAllowAccessToFrame(exec, frame, message))
48        return false;
49
50    // Check for the few functions that we allow, even when called cross-domain.
51    // Make these read-only / non-configurable to prevent writes via defineProperty.
52    if (propertyName == exec->propertyNames().replace) {
53        slot.setCustom(this, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsLocationPrototypeFunctionReplace, 1>);
54        return true;
55    }
56    if (propertyName == exec->propertyNames().reload) {
57        slot.setCustom(this, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsLocationPrototypeFunctionReload, 0>);
58        return true;
59    }
60    if (propertyName == exec->propertyNames().assign) {
61        slot.setCustom(this, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsLocationPrototypeFunctionAssign, 1>);
62        return true;
63    }
64
65    // FIXME: Other implementers of the Window cross-domain scheme (Window, History) allow toString,
66    // but for now we have decided not to, partly because it seems silly to return "[Object Location]" in
67    // such cases when normally the string form of Location would be the URL.
68
69    printErrorMessageForFrame(frame, message);
70    slot.setUndefined();
71    return true;
72}
73
74bool JSLocation::putDelegate(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
75{
76    Frame* frame = impl().frame();
77    if (!frame)
78        return true;
79
80    if (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf)
81        return true;
82
83    bool sameDomainAccess = shouldAllowAccessToFrame(exec, frame);
84
85    const HashTableValue* entry = JSLocation::info()->propHashTable(exec)->entry(exec, propertyName);
86    if (!entry) {
87        if (sameDomainAccess)
88            JSObject::put(this, exec, propertyName, value, slot);
89        return true;
90    }
91
92    // Cross-domain access to the location is allowed when assigning the whole location,
93    // but not when assigning the individual pieces, since that might inadvertently
94    // disclose other parts of the original location.
95    if (propertyName != exec->propertyNames().href && !sameDomainAccess)
96        return true;
97
98    return false;
99}
100
101bool JSLocation::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
102{
103    JSLocation* thisObject = jsCast<JSLocation*>(cell);
104    // Only allow deleting by frames in the same origin.
105    if (!shouldAllowAccessToFrame(exec, thisObject->impl().frame()))
106        return false;
107    return Base::deleteProperty(thisObject, exec, propertyName);
108}
109
110bool JSLocation::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
111{
112    JSLocation* thisObject = jsCast<JSLocation*>(cell);
113    // Only allow deleting by frames in the same origin.
114    if (!shouldAllowAccessToFrame(exec, thisObject->impl().frame()))
115        return false;
116    return Base::deletePropertyByIndex(thisObject, exec, propertyName);
117}
118
119void JSLocation::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
120{
121    JSLocation* thisObject = jsCast<JSLocation*>(object);
122    // Only allow the location object to enumerated by frames in the same origin.
123    if (!shouldAllowAccessToFrame(exec, thisObject->impl().frame()))
124        return;
125    Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
126}
127
128bool JSLocation::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
129{
130    if (descriptor.isAccessorDescriptor() && (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf))
131        return false;
132    return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
133}
134
135void JSLocation::setHref(ExecState* exec, JSValue value)
136{
137    String href = value.toString(exec)->value(exec);
138    if (exec->hadException())
139        return;
140    impl().setHref(href, activeDOMWindow(exec), firstDOMWindow(exec));
141}
142
143void JSLocation::setProtocol(ExecState* exec, JSValue value)
144{
145    String protocol = value.toString(exec)->value(exec);
146    if (exec->hadException())
147        return;
148    ExceptionCode ec = 0;
149    impl().setProtocol(protocol, activeDOMWindow(exec), firstDOMWindow(exec), ec);
150    setDOMException(exec, ec);
151}
152
153void JSLocation::setHost(ExecState* exec, JSValue value)
154{
155    String host = value.toString(exec)->value(exec);
156    if (exec->hadException())
157        return;
158    impl().setHost(host, activeDOMWindow(exec), firstDOMWindow(exec));
159}
160
161void JSLocation::setHostname(ExecState* exec, JSValue value)
162{
163    String hostname = value.toString(exec)->value(exec);
164    if (exec->hadException())
165        return;
166    impl().setHostname(hostname, activeDOMWindow(exec), firstDOMWindow(exec));
167}
168
169void JSLocation::setPort(ExecState* exec, JSValue value)
170{
171    String port = value.toWTFString(exec);
172    if (exec->hadException())
173        return;
174    impl().setPort(port, activeDOMWindow(exec), firstDOMWindow(exec));
175}
176
177void JSLocation::setPathname(ExecState* exec, JSValue value)
178{
179    String pathname = value.toString(exec)->value(exec);
180    if (exec->hadException())
181        return;
182    impl().setPathname(pathname, activeDOMWindow(exec), firstDOMWindow(exec));
183}
184
185void JSLocation::setSearch(ExecState* exec, JSValue value)
186{
187    String pathname = value.toString(exec)->value(exec);
188    if (exec->hadException())
189        return;
190    impl().setSearch(pathname, activeDOMWindow(exec), firstDOMWindow(exec));
191}
192
193void JSLocation::setHash(ExecState* exec, JSValue value)
194{
195    String hash = value.toString(exec)->value(exec);
196    if (exec->hadException())
197        return;
198    impl().setHash(hash, activeDOMWindow(exec), firstDOMWindow(exec));
199}
200
201JSValue JSLocation::replace(ExecState* exec)
202{
203    String urlString = exec->argument(0).toString(exec)->value(exec);
204    if (exec->hadException())
205        return jsUndefined();
206    impl().replace(urlString, activeDOMWindow(exec), firstDOMWindow(exec));
207    return jsUndefined();
208}
209
210JSValue JSLocation::reload(ExecState* exec)
211{
212    impl().reload(activeDOMWindow(exec));
213    return jsUndefined();
214}
215
216JSValue JSLocation::assign(ExecState* exec)
217{
218    String urlString = exec->argument(0).toString(exec)->value(exec);
219    if (exec->hadException())
220        return jsUndefined();
221    impl().assign(urlString, activeDOMWindow(exec), firstDOMWindow(exec));
222    return jsUndefined();
223}
224
225JSValue JSLocation::toStringFunction(ExecState* exec)
226{
227    Frame* frame = impl().frame();
228    if (!frame || !shouldAllowAccessToFrame(exec, frame))
229        return jsUndefined();
230
231    return jsStringWithCache(exec, impl().toString());
232}
233
234bool JSLocationPrototype::putDelegate(ExecState* exec, PropertyName propertyName, JSValue, PutPropertySlot&)
235{
236    return (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf);
237}
238
239bool JSLocationPrototype::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
240{
241    if (descriptor.isAccessorDescriptor() && (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf))
242        return false;
243    return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
244}
245
246} // namespace WebCore
247