d031a50f9409d518709ab3ca111396745e349a55
[WebKit-https.git] / WebCore / bindings / v8 / custom / V8DOMWindowCustom.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 "DOMWindow.h"
33
34 #include "V8Binding.h"
35 #include "V8CustomBinding.h"
36 #include "V8CustomEventListener.h"
37 #include "V8Proxy.h"
38
39 #include "DOMTimer.h"
40 #include "Frame.h"
41 #include "FrameLoadRequest.h"
42 #include "FrameView.h"
43 #include "Page.h"
44 #include "PlatformScreen.h"
45 #include "ScriptSourceCode.h"
46 #include "Settings.h"
47 #include "WindowFeatures.h"
48
49
50 // Horizontal and vertical offset, from the parent content area, around newly
51 // opened popups that don't specify a location.
52 static const int popupTilePixels = 10;
53
54 namespace WebCore {
55
56 ACCESSOR_SETTER(DOMWindowLocation)
57 {
58     v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper(V8ClassIndex::DOMWINDOW, info.This());
59     if (holder.IsEmpty())
60         return;
61
62     DOMWindow* imp = V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, holder);
63     WindowSetLocation(imp, toWebCoreString(value));
64 }
65
66
67 ACCESSOR_SETTER(DOMWindowOpener)
68 {
69     DOMWindow* imp = V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, info.Holder());
70
71     if (!V8Proxy::CanAccessFrame(imp->frame(), true))
72         return;
73   
74     // Opener can be shadowed if it is in the same domain.
75     // Have a special handling of null value to behave
76     // like Firefox. See bug http://b/1224887 & http://b/791706.
77     if (value->IsNull()) {
78         // imp->frame() cannot be null,
79         // otherwise, SameOrigin check would have failed.
80         ASSERT(imp->frame());
81         imp->frame()->loader()->setOpener(0);
82     }
83
84     // Delete the accessor from this object.
85     info.Holder()->Delete(name);
86
87     // Put property on the front (this) object.
88     info.This()->Set(name, value);
89 }
90
91 CALLBACK_FUNC_DECL(DOMWindowAddEventListener)
92 {
93     INC_STATS("DOM.DOMWindow.addEventListener()");
94     DOMWindow* imp = V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, args.Holder());
95
96     if (!V8Proxy::CanAccessFrame(imp->frame(), true))
97         return v8::Undefined();
98
99     if (!imp->frame())
100         return v8::Undefined();  // DOMWindow could be disconnected from the frame
101   
102     Document* doc = imp->frame()->document();
103     if (!doc)
104         return v8::Undefined();
105
106     // TODO: Check if there is not enough arguments
107     V8Proxy* proxy = V8Proxy::retrieve(imp->frame());
108     if (!proxy)
109         return v8::Undefined();
110
111     RefPtr<EventListener> listener = proxy->FindOrCreateV8EventListener(args[1], false);
112
113     if (listener) {
114         String eventType = toWebCoreString(args[0]);
115         bool useCapture = args[2]->BooleanValue();
116         doc->addWindowEventListener(eventType, listener, useCapture);
117     }
118
119     return v8::Undefined();
120 }
121
122
123 CALLBACK_FUNC_DECL(DOMWindowRemoveEventListener)
124 {
125     INC_STATS("DOM.DOMWindow.removeEventListener()");
126     DOMWindow* imp = V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, args.Holder());
127
128     if (!V8Proxy::CanAccessFrame(imp->frame(), true))
129         return v8::Undefined();
130
131     if (!imp->frame())
132         return v8::Undefined();
133
134     Document* doc = imp->frame()->document();
135     if (!doc)
136         return v8::Undefined();
137
138     V8Proxy* proxy = V8Proxy::retrieve(imp->frame());
139     if (!proxy)
140         return v8::Undefined();
141
142     RefPtr<EventListener> listener = proxy->FindV8EventListener(args[1], false);
143
144     if (listener) {
145         String eventType = toWebCoreString(args[0]);
146         bool useCapture = args[2]->BooleanValue();
147         doc->removeWindowEventListener(eventType, listener.get(), useCapture);
148     }
149
150     return v8::Undefined();
151 }
152
153 CALLBACK_FUNC_DECL(DOMWindowPostMessage)
154 {
155     INC_STATS("DOM.DOMWindow.postMessage()");
156     DOMWindow* window = V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, args.Holder());
157
158     DOMWindow* source = V8Proxy::retrieveActiveFrame()->domWindow();
159     ASSERT(source->frame());
160
161     String uri = source->frame()->loader()->url().string();
162
163     v8::TryCatch tryCatch;
164
165     String message = toWebCoreString(args[0]);
166     MessagePort* port = 0;
167     String domain;
168
169     // This function has variable arguments and can either be:
170     //   postMessage(message, port, domain);
171     // or
172     //   postMessage(message, domain);
173     if (args.Length() > 2) {
174         if (V8Proxy::IsWrapperOfType(args[1], V8ClassIndex::MESSAGEPORT))
175             port = V8Proxy::ToNativeObject<MessagePort>(V8ClassIndex::MESSAGEPORT, args[1]);
176         domain = valueToStringWithNullOrUndefinedCheck(args[2]);
177     } else
178         domain = valueToStringWithNullOrUndefinedCheck(args[1]);
179
180     if (tryCatch.HasCaught())
181         return v8::Undefined();
182
183     ExceptionCode ec = 0;
184     window->postMessage(message, port, domain, source, ec);
185     if (ec)
186         V8Proxy::SetDOMException(ec);
187
188     return v8::Undefined();
189 }
190
191
192 static bool canShowModalDialogNow(const Frame* frame)
193 {
194     // A frame can out live its page. See bug 1219613.
195     if (!frame || !frame->page())
196         return false;
197     return frame->page()->chrome()->canRunModalNow();
198 }
199
200 static bool allowPopUp()
201 {
202     Frame* frame = V8Proxy::retrieveActiveFrame();
203
204     ASSERT(frame);
205     if (frame->script()->processingUserGesture())
206         return true;
207     Settings* settings = frame->settings();
208     return settings && settings->JavaScriptCanOpenWindowsAutomatically();
209 }
210
211 static HashMap<String, String> parseModalDialogFeatures(const String& featuresArg)
212 {
213     HashMap<String, String> map;
214
215     Vector<String> features;
216     featuresArg.split(';', features);
217     Vector<String>::const_iterator end = features.end();
218     for (Vector<String>::const_iterator it = features.begin(); it != end; ++it) {
219         String featureString = *it;
220         int pos = featureString.find('=');
221         int colonPos = featureString.find(':');
222         if (pos >= 0 && colonPos >= 0)
223             continue;  // ignore any strings that have both = and :
224         if (pos < 0)
225             pos = colonPos;
226         if (pos < 0) {
227             // null string for value means key without value
228             map.set(featureString.stripWhiteSpace().lower(), String());
229         } else {
230             String key = featureString.left(pos).stripWhiteSpace().lower();
231             String val = featureString.substring(pos + 1).stripWhiteSpace().lower();
232             int spacePos = val.find(' ');
233             if (spacePos != -1)
234                 val = val.left(spacePos);
235             map.set(key, val);
236         }
237     }
238
239     return map;
240 }
241
242
243 static Frame* createWindow(Frame* openerFrame,
244                            const String& url,
245                            const String& frameName,
246                            const WindowFeatures& windowFeatures,
247                            v8::Local<v8::Value> dialogArgs)
248 {
249     Frame* activeFrame = V8Proxy::retrieveActiveFrame();
250
251     ResourceRequest request;
252     if (activeFrame)
253         request.setHTTPReferrer(activeFrame->loader()->outgoingReferrer());
254     FrameLoadRequest frameRequest(request, frameName);
255
256     // FIXME: It's much better for client API if a new window starts with a URL,
257     // here where we know what URL we are going to open. Unfortunately, this
258     // code passes the empty string for the URL, but there's a reason for that.
259     // Before loading we have to set up the opener, openedByDOM,
260     // and dialogArguments values. Also, to decide whether to use the URL
261     // we currently do an allowsAccessFrom call using the window we create,
262     // which can't be done before creating it. We'd have to resolve all those
263     // issues to pass the URL instead of "".
264
265     bool created;
266     // We pass in the opener frame here so it can be used for looking up the
267     // frame name, in case the active frame is different from the opener frame,
268     // and the name references a frame relative to the opener frame, for example
269     // "_self" or "_parent".
270     Frame* newFrame = activeFrame->loader()->createWindow(openerFrame->loader(), frameRequest, windowFeatures, created);
271     if (!newFrame)
272         return 0;
273
274     newFrame->loader()->setOpener(openerFrame);
275     newFrame->loader()->setOpenedByDOM();
276
277     // Set dialog arguments on the global object of the new frame.
278     if (!dialogArgs.IsEmpty()) {
279         v8::Local<v8::Context> context = V8Proxy::GetContext(newFrame);
280         if (!context.IsEmpty()) {
281             v8::Context::Scope scope(context);
282             context->Global()->Set(v8::String::New("dialogArguments"), dialogArgs);
283         }
284     }
285
286     if (!parseURL(url).startsWith("javascript:", false)
287         || ScriptController::isSafeScript(newFrame)) {
288         KURL completedUrl =
289             url.isEmpty() ? KURL("") : activeFrame->document()->completeURL(url);
290         bool userGesture = activeFrame->script()->processingUserGesture();
291
292         if (created)
293             newFrame->loader()->changeLocation(completedUrl, activeFrame->loader()->outgoingReferrer(), false, false, userGesture);
294         else if (!url.isEmpty())
295             newFrame->loader()->scheduleLocationChange(completedUrl.string(), activeFrame->loader()->outgoingReferrer(), false, userGesture);
296     }
297
298     return newFrame;
299 }
300
301
302
303 CALLBACK_FUNC_DECL(DOMWindowShowModalDialog)
304 {
305     INC_STATS("DOM.DOMWindow.showModalDialog()");
306     DOMWindow* window = V8Proxy::ToNativeObject<DOMWindow>(
307         V8ClassIndex::DOMWINDOW, args.Holder());
308     Frame* frame = window->frame();
309
310     if (!frame || !V8Proxy::CanAccessFrame(frame, true)) 
311         return v8::Undefined();
312
313     if (!canShowModalDialogNow(frame) || !allowPopUp())
314         return v8::Undefined();
315
316     String url = valueToStringWithNullOrUndefinedCheck(args[0]);
317     v8::Local<v8::Value> dialogArgs = args[1];
318     String featureArgs = valueToStringWithNullOrUndefinedCheck(args[2]);
319
320     const HashMap<String, String> features = parseModalDialogFeatures(featureArgs);
321
322     const bool trusted = false;
323
324     FloatRect screenRect = screenAvailableRect(frame->view());
325
326     WindowFeatures windowFeatures;
327     // default here came from frame size of dialog in MacIE.
328     windowFeatures.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620);
329     windowFeatures.widthSet = true;
330     // default here came from frame size of dialog in MacIE.
331     windowFeatures.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450);
332     windowFeatures.heightSet = true;
333   
334     windowFeatures.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - windowFeatures.width, -1);
335     windowFeatures.xSet = windowFeatures.x > 0;
336     windowFeatures.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - windowFeatures.height, -1);
337     windowFeatures.ySet = windowFeatures.y > 0;
338
339     if (WindowFeatures::boolFeature(features, "center", true)) {
340         if (!windowFeatures.xSet) {
341             windowFeatures.x = screenRect.x() + (screenRect.width() - windowFeatures.width) / 2;
342             windowFeatures.xSet = true;
343         }
344         if (!windowFeatures.ySet) {
345             windowFeatures.y = screenRect.y() + (screenRect.height() - windowFeatures.height) / 2;
346             windowFeatures.ySet = true;
347         }
348     }
349
350     windowFeatures.dialog = true;
351     windowFeatures.resizable = WindowFeatures::boolFeature(features, "resizable");
352     windowFeatures.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true);
353     windowFeatures.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted);
354     windowFeatures.menuBarVisible = false;
355     windowFeatures.toolBarVisible = false;
356     windowFeatures.locationBarVisible = false;
357     windowFeatures.fullscreen = false;
358
359     Frame* dialogFrame = createWindow(frame, url, "", windowFeatures, dialogArgs);
360     if (!dialogFrame)
361         return v8::Undefined();
362
363     // Hold on to the context of the dialog window long enough to retrieve the
364     // value of the return value property.
365     v8::Local<v8::Context> context = V8Proxy::GetContext(dialogFrame);
366
367     // Run the dialog.
368     dialogFrame->page()->chrome()->runModal();
369
370     // Extract the return value property from the dialog window.
371     v8::Local<v8::Value> returnValue;
372     if (!context.IsEmpty()) {
373         v8::Context::Scope scope(context);
374         returnValue = context->Global()->Get(v8::String::New("returnValue"));
375     }
376
377     if (!returnValue.IsEmpty())
378         return returnValue;
379
380     return v8::Undefined();
381 }
382
383
384 CALLBACK_FUNC_DECL(DOMWindowOpen)
385 {
386     INC_STATS("DOM.DOMWindow.open()");
387     DOMWindow* parent = V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, args.Holder());
388     Frame* frame = parent->frame();
389
390     if (!V8Proxy::CanAccessFrame(frame, true))
391       return v8::Undefined();
392
393     Frame* activeFrame = V8Proxy::retrieveActiveFrame();
394     if (!activeFrame)
395       return v8::Undefined();
396
397     Page* page = frame->page();
398     if (!page)
399       return v8::Undefined();
400
401     String urlString = valueToStringWithNullOrUndefinedCheck(args[0]);
402     AtomicString frameName = (args[1]->IsUndefined() || args[1]->IsNull()) ? "_blank" : AtomicString(toWebCoreString(args[1]));
403
404     // Because FrameTree::find() returns true for empty strings, we must check
405     // for empty framenames. Otherwise, illegitimate window.open() calls with
406     // no name will pass right through the popup blocker.
407     if (!allowPopUp() &&
408         (frameName.isEmpty() || !frame->tree()->find(frameName))) {
409         return v8::Undefined();
410     }
411
412     // Get the target frame for the special cases of _top and _parent.  In those
413     // cases, we can schedule a location change right now and return early.
414     bool topOrParent = false;
415     if (frameName == "_top") {
416         frame = frame->tree()->top();
417         topOrParent = true;
418     } else if (frameName == "_parent") {
419         if (Frame* parent = frame->tree()->parent())
420             frame = parent;
421         topOrParent = true;
422     }
423     if (topOrParent) {
424         if (!activeFrame->loader()->shouldAllowNavigation(frame))
425             return v8::Undefined();
426     
427         String completedUrl;
428         if (!urlString.isEmpty())
429             completedUrl = activeFrame->document()->completeURL(urlString);
430     
431         if (!completedUrl.isEmpty() &&
432             (!parseURL(urlString).startsWith("javascript:", false)
433              || ScriptController::isSafeScript(frame))) {
434             bool userGesture = activeFrame->script()->processingUserGesture();
435             frame->loader()->scheduleLocationChange(completedUrl, activeFrame->loader()->outgoingReferrer(), false, userGesture);
436         }
437         return V8Proxy::ToV8Object(V8ClassIndex::DOMWINDOW, frame->domWindow());
438     }
439
440     // In the case of a named frame or a new window, we'll use the
441     // createWindow() helper.
442
443     // Parse the values, and then work with a copy of the parsed values
444     // so we can restore the values we may not want to overwrite after
445     // we do the multiple monitor fixes.
446     WindowFeatures rawFeatures(valueToStringWithNullOrUndefinedCheck(args[2]));
447     WindowFeatures windowFeatures(rawFeatures);
448     FloatRect screenRect = screenAvailableRect(page->mainFrame()->view());
449
450     // Set default size and location near parent window if none were specified.
451     // These may be further modified by adjustWindowRect, below.
452     if (!windowFeatures.xSet) {
453         windowFeatures.x = parent->screenX() - screenRect.x() + popupTilePixels;
454         windowFeatures.xSet = true;
455     }
456     if (!windowFeatures.ySet) {
457         windowFeatures.y = parent->screenY() - screenRect.y() + popupTilePixels;
458         windowFeatures.ySet = true;
459     }
460     if (!windowFeatures.widthSet) {
461         windowFeatures.width = parent->innerWidth();
462         windowFeatures.widthSet = true;
463     }
464     if (!windowFeatures.heightSet) {
465         windowFeatures.height = parent->innerHeight();
466         windowFeatures.heightSet = true;
467     }
468
469     FloatRect windowRect(windowFeatures.x, windowFeatures.y, windowFeatures.width, windowFeatures.height);
470
471     // The new window's location is relative to its current screen, so shift
472     // it in case it's on a secondary monitor. See http://b/viewIssue?id=967905.
473     windowRect.move(screenRect.x(), screenRect.y());
474     WebCore::DOMWindow::adjustWindowRect(screenRect, windowRect, windowRect);
475
476     windowFeatures.x = windowRect.x();
477     windowFeatures.y = windowRect.y();
478     windowFeatures.height = windowRect.height();
479     windowFeatures.width = windowRect.width();
480
481     // If either of the origin coordinates weren't set in the original
482     // string, make sure they aren't set now.
483     if (!rawFeatures.xSet) {
484         windowFeatures.x = 0;
485         windowFeatures.xSet = false;
486     }
487     if (!rawFeatures.ySet) {
488         windowFeatures.y = 0;
489         windowFeatures.ySet = false;
490     }
491
492     frame = createWindow(frame, urlString, frameName, windowFeatures, v8::Local<v8::Value>());
493
494     if (!frame)
495         return v8::Undefined();
496
497     return V8Proxy::ToV8Object(V8ClassIndex::DOMWINDOW, frame->domWindow());
498 }
499
500
501 INDEXED_PROPERTY_GETTER(DOMWindow)
502 {
503     INC_STATS("DOM.DOMWindow.IndexedPropertyGetter");
504     v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper(V8ClassIndex::DOMWINDOW, info.This());
505     if (holder.IsEmpty())
506         return notHandledByInterceptor();
507
508     DOMWindow* window = V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, holder);
509     if (!window)
510         return notHandledByInterceptor();
511
512     Frame* frame = window->frame();
513     if (!frame)
514         return notHandledByInterceptor();
515
516     Frame* child = frame->tree()->child(index);
517     if (child)
518         return V8Proxy::ToV8Object(V8ClassIndex::DOMWINDOW, child->domWindow());
519
520     return notHandledByInterceptor();
521 }
522
523
524 NAMED_PROPERTY_GETTER(DOMWindow)
525 {
526     INC_STATS("DOM.DOMWindow.NamedPropertyGetter");
527     // The key must be a string.
528     if (!name->IsString())
529         return notHandledByInterceptor();
530
531     v8::Handle<v8::Object> holder = V8Proxy::LookupDOMWrapper(V8ClassIndex::DOMWINDOW, info.This());
532     if (holder.IsEmpty())
533         return notHandledByInterceptor();
534
535     DOMWindow* window = V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, holder);
536     if (!window)
537         return notHandledByInterceptor();
538
539     String propName = toWebCoreString(name);
540
541     Frame* frame = window->frame();
542     // window is detached from a frame.
543     if (!frame)
544         return notHandledByInterceptor();
545
546     // Search sub-frames.
547     Frame* child = frame->tree()->child(propName);
548     if (child)
549         return V8Proxy::ToV8Object(V8ClassIndex::DOMWINDOW, child->domWindow());
550
551     // Search IDL functions defined in the prototype
552     v8::Handle<v8::Value> result = holder->GetRealNamedPropertyInPrototypeChain(name);
553     if (!result.IsEmpty())
554         return result;
555
556     // Lazy initialization map keeps global properties that can be lazily
557     // initialized. The value is the code to instantiate the property.
558     // It must return the value of property after initialization.
559     static HashMap<String, String> lazyInitMap;
560     if (lazyInitMap.isEmpty()) {
561       // "new Image()" does not appear to be well-defined in a spec, but Safari,
562       // Opera, and Firefox all consider it to always create an HTML image
563       // element, regardless of the current doctype.
564       lazyInitMap.set("Image",
565                        "function Image() { \
566                           return document.createElementNS( \
567                             'http://www.w3.org/1999/xhtml', 'img'); \
568                         }; \
569                         Image");
570       lazyInitMap.set("Option",
571         "function Option(text, value, defaultSelected, selected) { \
572            var option = document.createElement('option'); \
573            if (text == null) return option; \
574            option.text = text; \
575            if (value == null) return option; \
576            option.value = value; \
577            if (defaultSelected == null) return option; \
578            option.defaultSelected = defaultSelected; \
579            if (selected == null) return option; \
580            option.selected = selected; \
581            return option; \
582          }; \
583          Option");
584     }
585
586     String code = lazyInitMap.get(propName);
587     if (!code.isEmpty()) {
588         v8::Local<v8::Context> context = V8Proxy::GetContext(window->frame());
589         // Bail out if we cannot get the context for the frame.
590         if (context.IsEmpty())
591             return notHandledByInterceptor();
592   
593         // switch to the target object's environment.
594         v8::Context::Scope scope(context);
595
596         // Set the property name to undefined to make sure that the
597         // property exists.  This is necessary because this getter
598         // might be called when evaluating 'var RangeException = value'
599         // to figure out if we have a property named 'RangeException' before
600         // we set RangeException to the new value.  In that case, we will
601         // evaluate 'var RangeException = {}' and enter an infinite loop.
602         // Setting the property name to undefined on the global object
603         // ensures that we do not have to ask this getter to figure out
604         // that we have the property.
605         //
606         // TODO(ager): We probably should implement the Has method
607         // for the interceptor instead of using the default Has method
608         // that calls Get.
609         context->Global()->Set(v8String(propName), v8::Undefined());
610         V8Proxy* proxy = V8Proxy::retrieve(window->frame());
611         ASSERT(proxy);
612
613         return proxy->evaluate(WebCore::ScriptSourceCode(code), 0);
614     }
615
616     // Search named items in the document.
617     Document* doc = frame->document();
618     if (doc) {
619         RefPtr<HTMLCollection> items = doc->windowNamedItems(propName);
620         if (items->length() >= 1) {
621             if (items->length() == 1)
622                 return V8Proxy::NodeToV8Object(items->firstItem());
623             else
624                 return V8Proxy::ToV8Object(V8ClassIndex::HTMLCOLLECTION, items.get());
625         }
626     }
627
628     return notHandledByInterceptor();
629 }
630
631
632 void V8Custom::WindowSetLocation(DOMWindow* window, const String& v)
633 {
634     if (!window->frame())
635         return;
636
637     Frame* activeFrame = ScriptController::retrieveActiveFrame();
638     if (!activeFrame)
639         return;
640
641     if (!activeFrame->loader()->shouldAllowNavigation(window->frame()))
642         return;
643
644     if (!parseURL(v).startsWith("javascript:", false)
645         || ScriptController::isSafeScript(window->frame())) {
646         String completedUrl = activeFrame->loader()->completeURL(v).string();
647   
648         // FIXME: The JSC bindings pass !anyPageIsProcessingUserGesture() for
649         // the lockHistory parameter.  We should probably do something similar.
650   
651         window->frame()->loader()->scheduleLocationChange(completedUrl,
652             activeFrame->loader()->outgoingReferrer(), false, false,
653             activeFrame->script()->processingUserGesture());
654     }
655 }
656
657
658 CALLBACK_FUNC_DECL(DOMWindowSetTimeout)
659 {
660     INC_STATS("DOM.DOMWindow.setTimeout()");
661     return WindowSetTimeoutImpl(args, true);
662 }
663
664
665 CALLBACK_FUNC_DECL(DOMWindowSetInterval)
666 {
667     INC_STATS("DOM.DOMWindow.setInterval()");
668     return WindowSetTimeoutImpl(args, false);
669 }
670
671
672 void V8Custom::ClearTimeoutImpl(const v8::Arguments& args)
673 {
674     v8::Handle<v8::Value> holder = args.Holder();
675     DOMWindow* imp = V8Proxy::ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, holder);
676     if (!V8Proxy::CanAccessFrame(imp->frame(), true))
677         return;
678     ScriptExecutionContext* context = static_cast<ScriptExecutionContext*>(imp->frame()->document());
679     int handle = toInt32(args[0]);
680     DOMTimer::removeById(context, handle);
681 }
682
683
684 CALLBACK_FUNC_DECL(DOMWindowClearTimeout)
685 {
686     INC_STATS("DOM.DOMWindow.clearTimeout");
687     ClearTimeoutImpl(args);
688     return v8::Undefined();
689 }
690
691 CALLBACK_FUNC_DECL(DOMWindowClearInterval)
692 {
693     INC_STATS("DOM.DOMWindow.clearInterval");
694     ClearTimeoutImpl(args);
695     return v8::Undefined();
696 }
697
698 } // namespace WebCore