aa423f0cf52e021688b985bd43d39191b9882189
[WebKit-https.git] / WebCore / bindings / js / JSLocation.cpp
1 // -*- c-basic-offset: 4 -*-
2 /*
3  *  Copyright (C) 2000 Harri Porten (porten@kde.org)
4  *  Copyright (C) 2006 Jon Shier (jshier@iastate.edu)
5  *  Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reseved.
6  *  Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
21  *  USA
22  */
23
24 #include "config.h"
25 #include "JSLocation.h"
26
27 #include "DOMWindow.h"
28 #include "Frame.h"
29 #include "FrameLoader.h"
30 #include "PlatformString.h"
31 #include "kjs_proxy.h"
32 #include "kjs_window.h"
33
34 #include "JSLocation.lut.h"
35
36 using namespace KJS;
37
38 namespace WebCore {
39
40 const ClassInfo JSLocation::info = { "Location", 0, &JSLocationTable };
41
42 /*
43 @begin JSLocationTable 12
44   assign        WebCore::jsLocationProtoFuncAssign        DontDelete|Function 1
45   hash          WebCore::JSLocation::Hash                 DontDelete
46   host          WebCore::JSLocation::Host                 DontDelete
47   hostname      WebCore::JSLocation::Hostname             DontDelete
48   href          WebCore::JSLocation::Href                 DontDelete
49   pathname      WebCore::JSLocation::Pathname             DontDelete
50   port          WebCore::JSLocation::Port                 DontDelete
51   protocol      WebCore::JSLocation::Protocol             DontDelete
52   search        WebCore::JSLocation::Search               DontDelete
53   toString      WebCore::jsLocationProtoFuncToString      DontEnum|DontDelete|Function 0
54   replace       WebCore::jsLocationProtoFuncReplace       DontDelete|Function 1
55   reload        WebCore::jsLocationProtoFuncReload        DontDelete|Function 0
56 @end
57 */
58
59 JSLocation::JSLocation(JSObject* /*prototype*/, Frame* frame)
60     : DOMObject(jsNull()) // FIXME: this needs to take a real prototype
61     , m_frame(frame)
62 {
63 }
64
65 JSValue* JSLocation::getValueProperty(ExecState* exec, int token) const
66 {
67   const KURL& url = m_frame->loader()->url();
68   switch (token) {
69   case Hash:
70     return jsString(url.ref().isNull() ? "" : "#" + url.ref());
71   case Host: {
72     // Note: this is the IE spec. The NS spec swaps the two, it says
73     // "The hostname property is the concatenation of the host and port properties, separated by a colon."
74     // Bleh.
75     UString str = url.host();
76     if (url.port())
77         str += ":" + String::number((int)url.port());
78     return jsString(str);
79   }
80   case Hostname:
81     return jsString(url.host());
82   case Href:
83     if (!url.hasPath())
84       return jsString(url.prettyURL() + "/");
85     return jsString(url.prettyURL());
86   case Pathname:
87     return jsString(url.path().isEmpty() ? "/" : url.path());
88   case Port:
89     return jsString(url.port() ? String::number((int)url.port()) : "");
90   case Protocol:
91     return jsString(url.protocol() + ":");
92   case Search:
93     return jsString(url.query());
94   default:
95     ASSERT_NOT_REACHED();
96     return jsUndefined();
97   }
98 }
99
100 bool JSLocation::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
101 {
102   if (!m_frame)
103     return false;
104
105   const Window* window = Window::retrieveWindow(m_frame);
106
107   const HashEntry* entry = Lookup::findEntry(&JSLocationTable, propertyName);
108   if (!entry || !(entry->attr & KJS::Function) || (entry->value.functionValue != &jsLocationProtoFuncReplace
109                                                    && entry->value.functionValue != &jsLocationProtoFuncReload
110                                                    && entry->value.functionValue != &jsLocationProtoFuncAssign))  {
111     if (!window || !window->allowsAccessFrom(exec)) {
112       slot.setUndefined(this);
113       return true;
114     }
115   }
116
117   return getStaticPropertySlot<JSLocation, JSObject>(exec, &JSLocationTable, this, propertyName, slot);
118 }
119
120 void JSLocation::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
121 {
122   if (!m_frame)
123     return;
124
125   DeprecatedString str = value->toString(exec);
126   KURL url = m_frame->loader()->url();
127   const Window* window = Window::retrieveWindow(m_frame);
128   bool sameDomainAccess = window && window->allowsAccessFrom(exec);
129
130   const HashEntry* entry = Lookup::findEntry(&JSLocationTable, propertyName);
131
132   if (entry) {
133       // cross-domain access to the location is allowed when assigning the whole location,
134       // but not when assigning the individual pieces, since that might inadvertently
135       // disclose other parts of the original location.
136       if (entry->value.intValue != Href && !sameDomainAccess)
137           return;
138
139       switch (entry->value.intValue) {
140       case Href: {
141           Frame* frame = Window::retrieveActive(exec)->impl()->frame();
142           if (!frame)
143               return;
144           if (!frame->loader()->shouldAllowNavigation(m_frame))
145               return;
146           url = frame->loader()->completeURL(str);
147           break;
148       }
149       case Hash: {
150           if (str.startsWith("#"))
151               str = str.mid(1);
152           if (url.ref() == str)
153               return;
154           url.setRef(str);
155           break;
156       }
157       case Host: {
158           url.setHostAndPort(str);
159           break;
160       }
161       case Hostname:
162           url.setHost(str);
163           break;
164       case Pathname:
165           url.setPath(str);
166           break;
167       case Port:
168           url.setPort(str.toUInt());
169           break;
170       case Protocol:
171           url.setProtocol(str);
172           break;
173       case Search:
174           url.setQuery(str);
175           break;
176       default:
177           // Disallow changing other properties in JSLocationTable. e.g., "window.location.toString = ...".
178           // <http://bugs.webkit.org/show_bug.cgi?id=12720>
179           return;
180       }
181   } else {
182       if (sameDomainAccess)
183           JSObject::put(exec, propertyName, value, attr);
184       return;
185   }
186
187   Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
188   if (!url.deprecatedString().startsWith("javascript:", false) || sameDomainAccess) {
189     bool userGesture = activeFrame->scriptProxy()->processingUserGesture();
190     m_frame->loader()->scheduleLocationChange(url.string(), activeFrame->loader()->outgoingReferrer(), false, userGesture);
191   }
192 }
193
194 JSValue* jsLocationProtoFuncReplace(ExecState* exec, JSObject* thisObj, const List& args)
195 {
196     if (!thisObj->inherits(&JSLocation::info))
197         return throwError(exec, TypeError);
198     JSLocation* location = static_cast<JSLocation*>(thisObj);
199     Frame* frame = location->frame();
200     if (!frame)
201         return jsUndefined();
202
203     Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
204     if (activeFrame) {
205         if (!activeFrame->loader()->shouldAllowNavigation(frame))
206             return jsUndefined();
207         DeprecatedString str = args[0]->toString(exec);
208         const Window* window = Window::retrieveWindow(frame);
209         if (!str.startsWith("javascript:", false) || (window && window->allowsAccessFrom(exec))) {
210             bool userGesture = activeFrame->scriptProxy()->processingUserGesture();
211             frame->loader()->scheduleLocationChange(activeFrame->loader()->completeURL(str).string(), activeFrame->loader()->outgoingReferrer(), true, userGesture);
212         }
213     }
214
215     return jsUndefined();
216 }
217
218 JSValue* jsLocationProtoFuncReload(ExecState* exec, JSObject* thisObj, const List& args)
219 {
220     if (!thisObj->inherits(&JSLocation::info))
221         return throwError(exec, TypeError);
222     JSLocation* location = static_cast<JSLocation*>(thisObj);
223     Frame* frame = location->frame();
224     if (!frame)
225         return jsUndefined();
226
227     Window* window = Window::retrieveWindow(frame);
228     if (!window->allowsAccessFrom(exec))
229         return jsUndefined();
230
231     if (!frame->loader()->url().deprecatedString().startsWith("javascript:", false) || (window && window->allowsAccessFrom(exec))) {
232         bool userGesture = Window::retrieveActive(exec)->impl()->frame()->scriptProxy()->processingUserGesture();
233         frame->loader()->scheduleRefresh(userGesture);
234     }
235     return jsUndefined();
236 }
237
238 JSValue* jsLocationProtoFuncAssign(ExecState* exec, JSObject* thisObj, const List& args)
239 {
240     if (!thisObj->inherits(&JSLocation::info))
241         return throwError(exec, TypeError);
242     JSLocation* location = static_cast<JSLocation*>(thisObj);
243     Frame* frame = location->frame();
244     if (!frame)
245         return jsUndefined();
246
247     Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
248     if (activeFrame) {
249         if (!activeFrame->loader()->shouldAllowNavigation(frame))
250             return jsUndefined();
251         const Window* window = Window::retrieveWindow(frame);
252         String dstUrl = activeFrame->loader()->completeURL(args[0]->toString(exec)).string();
253         if (!dstUrl.startsWith("javascript:", false) || (window && window->allowsAccessFrom(exec))) {
254             bool userGesture = activeFrame->scriptProxy()->processingUserGesture();
255             // We want a new history item if this JS was called via a user gesture
256             frame->loader()->scheduleLocationChange(dstUrl, activeFrame->loader()->outgoingReferrer(), false, userGesture);
257         }
258     }
259
260     return jsUndefined();
261 }
262
263 JSValue* jsLocationProtoFuncToString(ExecState* exec, JSObject* thisObj, const List& args)
264 {
265     if (!thisObj->inherits(&JSLocation::info))
266         return throwError(exec, TypeError);
267     JSLocation* location = static_cast<JSLocation*>(thisObj);
268     Frame* frame = location->frame();
269     if (!frame)
270         return jsUndefined();
271
272     const KURL& url = frame->loader()->url();
273     if (!url.hasPath())
274         return jsString(url.prettyURL() + "/");
275     return jsString(url.prettyURL());
276 }
277
278 } // namespace WebCore