2009-03-13 Mike Belshe <mike@belshe.com>
[WebKit-https.git] / WebCore / bindings / v8 / custom / V8LocationCustom.cpp
1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "Location.h"
33
34 #include "V8Binding.h"
35 #include "V8CustomBinding.h"
36 #include "V8CustomEventListener.h"
37 #include "V8Location.h"
38 #include "V8Proxy.h"
39
40 #include "PlatformString.h"
41 #include "KURL.h"
42 #include "Document.h"
43 #include "FrameLoader.h"
44 #include "ScriptController.h"
45 #include "CSSHelper.h"
46 #include "Frame.h"
47
48 namespace WebCore {
49
50 // Notes about V8/JSC porting of this file.
51 // This class is not very JS-engine specific.  If we can move a couple of
52 // methods to the scriptController, we should be able to unify the code
53 // between JSC and V8:
54 //    retrieveActiveFrame()   - in JSC, this needs an ExecState.
55 //    isSafeScript()
56 // Since JSC and V8 have different mechanisms for getting at the ActiveFrame,
57 // we're just making all these custom for now.  The functionality is simple
58 // and mirrors JSLocationCustom.cpp.
59
60 static void navigateIfAllowed(Frame* frame, const KURL& url, bool lockHistory, bool lockBackForwardList)
61 {
62     if (url.isEmpty())
63         return;
64
65     Frame* activeFrame = ScriptController::retrieveActiveFrame();
66     if (!activeFrame)
67         return;
68
69     if (!url.protocolIs("javascript") || ScriptController::isSafeScript(frame)) {
70         bool userGesture = activeFrame->script()->processingUserGesture();
71         frame->loader()->scheduleLocationChange(url.string(), activeFrame->loader()->outgoingReferrer(), lockHistory, lockBackForwardList, userGesture);
72     }
73 }
74
75 ACCESSOR_SETTER(LocationHash)
76 {
77     INC_STATS("DOM.Location.hash._set");
78     v8::Handle<v8::Object> holder = info.Holder();
79     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
80     String hash = toWebCoreString(value);
81
82     Frame* frame = imp->frame();
83     if (!frame)
84         return;
85
86     KURL url = frame->loader()->url();
87     String oldRef = url.ref();
88
89     if (hash.startsWith("#"))
90         hash = hash.substring(1);
91     if (oldRef == hash || (oldRef.isNull() && hash.isEmpty()))
92         return;
93     url.setRef(hash);
94
95     navigateIfAllowed(frame, url, false, false);
96 }
97
98 ACCESSOR_SETTER(LocationHost)
99 {
100     INC_STATS("DOM.Location.host._set");
101     v8::Handle<v8::Object> holder = info.Holder();
102     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
103     String host = toWebCoreString(value);
104
105     Frame* frame = imp->frame();
106     if (!frame)
107         return;
108
109     KURL url = frame->loader()->url();
110     String newHost = host.left(host.find(":"));
111     String newPort = host.substring(host.find(":") + 1);
112     url.setHost(newHost);
113     url.setPort(newPort.toUInt());
114
115     navigateIfAllowed(frame, url, false, false);
116 }
117
118 ACCESSOR_SETTER(LocationHostname)
119 {
120     INC_STATS("DOM.Location.hostname._set");
121     v8::Handle<v8::Object> holder = info.Holder();
122     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
123     String hostname = toWebCoreString(value);
124
125     Frame* frame = imp->frame();
126     if (!frame)
127         return;
128
129     KURL url = frame->loader()->url();
130     url.setHost(hostname);
131
132     navigateIfAllowed(frame, url, false, false);
133 }
134
135 ACCESSOR_SETTER(LocationHref)
136 {
137     INC_STATS("DOM.Location.href._set");
138     v8::Handle<v8::Object> holder = info.Holder();
139     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
140     String href = toWebCoreString(value);
141
142     Frame* frame = imp->frame();
143     if (!frame)
144         return;
145
146     Frame* activeFrame = ScriptController::retrieveActiveFrame();
147     if (!activeFrame)
148         return;
149
150     if (!activeFrame->loader()->shouldAllowNavigation(frame))
151         return;
152
153     navigateIfAllowed(frame, activeFrame->loader()->completeURL(href), false, false);
154 }
155
156 ACCESSOR_SETTER(LocationPathname)
157 {
158     INC_STATS("DOM.Location.pathname._set");
159     v8::Handle<v8::Object> holder = info.Holder();
160     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
161     String pathname = toWebCoreString(value);
162
163     Frame* frame = imp->frame();
164     if (!frame)
165         return;
166
167     KURL url = frame->loader()->url();
168     url.setPath(pathname);
169
170     navigateIfAllowed(frame, url, false, false);
171 }
172
173 ACCESSOR_SETTER(LocationPort)
174 {
175     INC_STATS("DOM.Location.port._set");
176     v8::Handle<v8::Object> holder = info.Holder();
177     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
178     String port = toWebCoreString(value);
179
180     Frame* frame = imp->frame();
181     if (!frame)
182         return;
183
184     KURL url = frame->loader()->url();
185     url.setPort(port.toUInt());
186
187     navigateIfAllowed(frame, url, false, false);
188 }
189
190 ACCESSOR_SETTER(LocationProtocol)
191 {
192     INC_STATS("DOM.Location.protocol._set");
193     v8::Handle<v8::Object> holder = info.Holder();
194     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
195     String protocol = toWebCoreString(value);
196
197     Frame* frame = imp->frame();
198     if (!frame)
199         return;
200
201     KURL url = frame->loader()->url();
202     url.setProtocol(protocol);
203
204     navigateIfAllowed(frame, url, false, false);
205 }
206
207 ACCESSOR_SETTER(LocationSearch)
208 {
209     INC_STATS("DOM.Location.search._set");
210     v8::Handle<v8::Object> holder = info.Holder();
211     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
212     String query = toWebCoreString(value);
213
214     Frame* frame = imp->frame();
215     if (!frame)
216         return;
217
218     KURL url = frame->loader()->url();
219     url.setQuery(query);
220
221     navigateIfAllowed(frame, url, false, false);
222 }
223
224 ACCESSOR_GETTER(LocationReload)
225 {
226     INC_STATS("DOM.Location.reload._get");
227     static v8::Persistent<v8::FunctionTemplate> privateTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(v8LocationReloadCallback, v8::Handle<v8::Value>(), v8::Signature::New(V8Location::GetRawTemplate())));
228     v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper(V8ClassIndex::LOCATION, info.This());
229     if (holder.IsEmpty()) {
230         // can only reach here by 'object.__proto__.func', and it should passed
231         // domain security check already
232         return privateTemplate->GetFunction();
233     }
234     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
235     if (!V8Proxy::CanAccessFrame(imp->frame(), false)) {
236         static v8::Persistent<v8::FunctionTemplate> sharedTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(v8LocationReloadCallback, v8::Handle<v8::Value>(), v8::Signature::New(V8Location::GetRawTemplate())));
237         return sharedTemplate->GetFunction();
238     } else
239         return privateTemplate->GetFunction();
240 }
241
242 ACCESSOR_GETTER(LocationReplace)
243 {
244     INC_STATS("DOM.Location.replace._get");
245     static v8::Persistent<v8::FunctionTemplate> privateTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(v8LocationReplaceCallback, v8::Handle<v8::Value>(), v8::Signature::New(V8Location::GetRawTemplate())));
246     v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper(V8ClassIndex::LOCATION, info.This());
247     if (holder.IsEmpty()) {
248         // can only reach here by 'object.__proto__.func', and it should passed
249         // domain security check already
250         return privateTemplate->GetFunction();
251     }
252     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
253     if (!V8Proxy::CanAccessFrame(imp->frame(), false)) {
254         static v8::Persistent<v8::FunctionTemplate> sharedTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(v8LocationReplaceCallback, v8::Handle<v8::Value>(), v8::Signature::New(V8Location::GetRawTemplate())));
255         return sharedTemplate->GetFunction();
256     } else
257         return privateTemplate->GetFunction();
258 }
259
260 ACCESSOR_GETTER(LocationAssign)
261 {
262     INC_STATS("DOM.Location.assign._get");
263     static v8::Persistent<v8::FunctionTemplate> privateTemplate =
264     v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(v8LocationAssignCallback, v8::Handle<v8::Value>(), v8::Signature::New(V8Location::GetRawTemplate())));
265     v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper(V8ClassIndex::LOCATION, info.This());
266     if (holder.IsEmpty()) {
267         // can only reach here by 'object.__proto__.func', and it should passed
268         // domain security check already
269         return privateTemplate->GetFunction();
270     }
271     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
272     if (!V8Proxy::CanAccessFrame(imp->frame(), false)) {
273         static v8::Persistent<v8::FunctionTemplate> sharedTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(v8LocationAssignCallback, v8::Handle<v8::Value>(), v8::Signature::New(V8Location::GetRawTemplate())));
274         return sharedTemplate->GetFunction();
275     } else
276         return privateTemplate->GetFunction();
277 }
278
279 CALLBACK_FUNC_DECL(LocationReload)
280 {
281     // FIXME: we ignore the "forceget" parameter.
282
283     INC_STATS("DOM.Location.reload");
284     v8::Handle<v8::Value> holder = args.Holder();
285     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
286
287     Frame* frame = imp->frame();
288     if (!frame)
289         return v8::Undefined();
290
291     Frame* activeFrame = ScriptController::retrieveActiveFrame();
292     if (!activeFrame)
293         return v8::Undefined();
294
295     if (!ScriptController::isSafeScript(frame))
296         return v8::Undefined();
297
298     bool userGesture = activeFrame->script()->processingUserGesture();
299     frame->loader()->scheduleRefresh(userGesture);
300     return v8::Undefined();
301 }
302
303 CALLBACK_FUNC_DECL(LocationReplace)
304 {
305     INC_STATS("DOM.Location.replace");
306     v8::Handle<v8::Value> holder = args.Holder();
307     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
308     String url = toWebCoreString(args[0]);
309
310     Frame* frame = imp->frame();
311     if (!frame)
312         return v8::Undefined();
313
314     Frame* activeFrame = ScriptController::retrieveActiveFrame();
315     if (!activeFrame)
316         return v8::Undefined();
317
318     if (!activeFrame->loader()->shouldAllowNavigation(frame))
319         return v8::Undefined();
320
321     navigateIfAllowed(frame, activeFrame->loader()->completeURL(url), true, true);
322     return v8::Undefined();
323 }
324
325 CALLBACK_FUNC_DECL(LocationAssign)
326 {
327     INC_STATS("DOM.Location.assign");
328     v8::Handle<v8::Value> holder = args.Holder();
329     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
330     String url = toWebCoreString(args[0]);
331
332     Frame* frame = imp->frame();
333     if (!frame)
334         return v8::Undefined();
335
336     Frame* activeFrame = ScriptController::retrieveActiveFrame();
337     if (!activeFrame)
338         return v8::Undefined();
339
340     if (!activeFrame->loader()->shouldAllowNavigation(frame))
341         return v8::Undefined();
342
343     navigateIfAllowed(frame, activeFrame->loader()->completeURL(url), false, false);
344     return v8::Undefined();
345 }
346
347 CALLBACK_FUNC_DECL(LocationValueOf)
348 {
349     // Just return the this object the way the normal valueOf function
350     // on the Object prototype would.  The valueOf function is only
351     // added to make sure that it cannot be overwritten on location
352     // objects, since that would provide a hook to change the string
353     // conversion behavior of location objects.
354     return args.This();
355 }
356
357 CALLBACK_FUNC_DECL(LocationToString)
358 {
359     INC_STATS("DOM.Location.toString");
360     v8::Handle<v8::Value> holder = args.Holder();
361     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, holder);
362     if (!V8Proxy::CanAccessFrame(imp->frame(), true))
363         return v8::Undefined();
364     String result = imp->href();
365     return v8String(result);
366 }
367
368 INDEXED_ACCESS_CHECK(Location)
369 {
370     ASSERT(V8ClassIndex::FromInt(data->Int32Value()) == V8ClassIndex::LOCATION);
371     // Only allow same origin access
372     Location* imp =  V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, host);
373     return V8Proxy::CanAccessFrame(imp->frame(), false);
374 }
375
376 NAMED_ACCESS_CHECK(Location)
377 {
378     ASSERT(V8ClassIndex::FromInt(data->Int32Value()) == V8ClassIndex::LOCATION);
379     // Only allow same origin access
380     Location* imp = V8Proxy::ToNativeObject<Location>(V8ClassIndex::LOCATION, host);
381     return V8Proxy::CanAccessFrame(imp->frame(), false);
382 }
383
384 }  // namespace WebCore
385