2009-07-07 Nate Chapin <japhet@chromium.org>
[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 #include "V8Utilities.h"
39
40 #include "Base64.h"
41 #include "ExceptionCode.h"
42 #include "DOMTimer.h"
43 #include "Frame.h"
44 #include "FrameLoadRequest.h"
45 #include "FrameView.h"
46 #include "HTMLCollection.h"
47 #include "Page.h"
48 #include "PlatformScreen.h"
49 #include "ScheduledAction.h"
50 #include "ScriptSourceCode.h"
51 #include "Settings.h"
52 #include "WindowFeatures.h"
53
54 // Horizontal and vertical offset, from the parent content area, around newly
55 // opened popups that don't specify a location.
56 static const int popupTilePixels = 10;
57
58 namespace WebCore {
59
60 v8::Handle<v8::Value> V8Custom::WindowSetTimeoutImpl(const v8::Arguments& args, bool singleShot)
61 {
62     int argumentCount = args.Length();
63
64     if (argumentCount < 1)
65         return v8::Undefined();
66
67     DOMWindow* imp = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, args.Holder());
68
69     if (!imp->frame())
70         return v8::Undefined();
71
72     if (!V8Proxy::canAccessFrame(imp->frame(), true))
73         return v8::Undefined();
74
75     ScriptExecutionContext* scriptContext = static_cast<ScriptExecutionContext*>(imp->frame()->document());
76
77     v8::Handle<v8::Value> function = args[0];
78
79     int32_t timeout = 0;
80     if (argumentCount >= 2) 
81         timeout = args[1]->Int32Value();
82
83     int id;
84     if (function->IsString()) {
85         // Don't allow setting timeouts to run empty functions!
86         // (Bug 1009597)
87         WebCore::String functionString = toWebCoreString(function);
88         if (functionString.length() == 0)
89             return v8::Undefined();
90
91         id = DOMTimer::install(scriptContext, new ScheduledAction(functionString), timeout, singleShot);
92     } else if (function->IsFunction()) {
93         int paramCount = argumentCount >= 2 ? argumentCount - 2 : 0;
94         v8::Local<v8::Value>* params = 0;
95         if (paramCount > 0) {
96             params = new v8::Local<v8::Value>[paramCount];
97             for (int i = 0; i < paramCount; i++)
98                 // parameters must be globalized
99                 params[i] = args[i+2];
100         }
101
102         // params is passed to action, and released in action's destructor
103         ScheduledAction* action = new ScheduledAction(v8::Handle<v8::Function>::Cast(function), paramCount, params);
104
105         delete[] params;
106
107         id = DOMTimer::install(scriptContext, action, timeout, singleShot);
108     } else
109         // FIXME(fqian): what's the right return value if failed.
110         return v8::Undefined();
111
112     return v8::Integer::New(id);
113 }
114
115 static bool isAscii(const String& str)
116 {
117     for (size_t i = 0; i < str.length(); i++) {
118         if (str[i] > 0xFF)
119             return false;
120     }
121     return true;
122 }
123
124 static v8::Handle<v8::Value> convertBase64(const String& str, bool encode)
125 {
126     if (!isAscii(str)) {
127         V8Proxy::setDOMException(INVALID_CHARACTER_ERR);
128         return notHandledByInterceptor();
129     }
130
131     Vector<char> inputCharacters(str.length());
132     for (size_t i = 0; i < str.length(); i++)
133         inputCharacters[i] = static_cast<char>(str[i]);
134     Vector<char> outputCharacters;
135
136     if (encode)
137         base64Encode(inputCharacters, outputCharacters);
138     else {
139         if (!base64Decode(inputCharacters, outputCharacters))
140             return throwError("Cannot decode base64", V8Proxy::GeneralError);
141     }
142
143     return v8String(String(outputCharacters.data(), outputCharacters.size()));
144 }
145
146 ACCESSOR_GETTER(DOMWindowEvent)
147 {
148     v8::Local<v8::String> eventSymbol = v8::String::NewSymbol("event");
149     v8::Local<v8::Context> context = v8::Context::GetCurrent();
150     v8::Handle<v8::Value> jsEvent = context->Global()->GetHiddenValue(eventSymbol);
151     if (jsEvent.IsEmpty())
152         return v8::Undefined();
153     return jsEvent;
154 }
155
156 ACCESSOR_GETTER(DOMWindowCrypto)
157 {
158     // FIXME: Implement me.
159     return v8::Undefined();
160 }
161
162 ACCESSOR_SETTER(DOMWindowLocation)
163 {
164     v8::Handle<v8::Object> holder = V8Proxy::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, info.This());
165     if (holder.IsEmpty())
166         return;
167
168     DOMWindow* imp = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, holder);
169     WindowSetLocation(imp, toWebCoreString(value));
170 }
171
172
173 ACCESSOR_SETTER(DOMWindowOpener)
174 {
175     DOMWindow* imp = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, info.Holder());
176
177     if (!V8Proxy::canAccessFrame(imp->frame(), true))
178         return;
179   
180     // Opener can be shadowed if it is in the same domain.
181     // Have a special handling of null value to behave
182     // like Firefox. See bug http://b/1224887 & http://b/791706.
183     if (value->IsNull()) {
184         // imp->frame() cannot be null,
185         // otherwise, SameOrigin check would have failed.
186         ASSERT(imp->frame());
187         imp->frame()->loader()->setOpener(0);
188     }
189
190     // Delete the accessor from this object.
191     info.Holder()->Delete(name);
192
193     // Put property on the front (this) object.
194     info.This()->Set(name, value);
195 }
196
197 CALLBACK_FUNC_DECL(DOMWindowAddEventListener)
198 {
199     INC_STATS("DOM.DOMWindow.addEventListener()");
200     DOMWindow* imp = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, args.Holder());
201
202     if (!V8Proxy::canAccessFrame(imp->frame(), true))
203         return v8::Undefined();
204
205     if (!imp->frame())
206         return v8::Undefined();  // DOMWindow could be disconnected from the frame
207   
208     Document* doc = imp->frame()->document();
209     if (!doc)
210         return v8::Undefined();
211
212     // FIXME: Check if there is not enough arguments
213     V8Proxy* proxy = V8Proxy::retrieve(imp->frame());
214     if (!proxy)
215         return v8::Undefined();
216
217     RefPtr<EventListener> listener = proxy->findOrCreateV8EventListener(args[1], false);
218
219     if (listener) {
220         String eventType = toWebCoreString(args[0]);
221         bool useCapture = args[2]->BooleanValue();
222         imp->addEventListener(eventType, listener, useCapture);
223     }
224
225     return v8::Undefined();
226 }
227
228
229 CALLBACK_FUNC_DECL(DOMWindowRemoveEventListener)
230 {
231     INC_STATS("DOM.DOMWindow.removeEventListener()");
232     DOMWindow* imp = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, args.Holder());
233
234     if (!V8Proxy::canAccessFrame(imp->frame(), true))
235         return v8::Undefined();
236
237     if (!imp->frame())
238         return v8::Undefined();
239
240     Document* doc = imp->frame()->document();
241     if (!doc)
242         return v8::Undefined();
243
244     V8Proxy* proxy = V8Proxy::retrieve(imp->frame());
245     if (!proxy)
246         return v8::Undefined();
247
248     RefPtr<EventListener> listener = proxy->findV8EventListener(args[1], false);
249
250     if (listener) {
251         String eventType = toWebCoreString(args[0]);
252         bool useCapture = args[2]->BooleanValue();
253         imp->removeEventListener(eventType, listener.get(), useCapture);
254     }
255
256     return v8::Undefined();
257 }
258
259 CALLBACK_FUNC_DECL(DOMWindowPostMessage)
260 {
261     INC_STATS("DOM.DOMWindow.postMessage()");
262     DOMWindow* window = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, args.Holder());
263
264     DOMWindow* source = V8Proxy::retrieveFrameForCallingContext()->domWindow();
265     ASSERT(source->frame());
266
267     v8::TryCatch tryCatch;
268
269     String message = toWebCoreString(args[0]);
270     MessagePort* port = 0;
271     String targetOrigin;
272
273     // This function has variable arguments and can either be:
274     //   postMessage(message, port, targetOrigin);
275     // or
276     //   postMessage(message, targetOrigin);
277     if (args.Length() > 2) {
278         if (V8Proxy::isWrapperOfType(args[1], V8ClassIndex::MESSAGEPORT))
279             port = V8Proxy::convertToNativeObject<MessagePort>(V8ClassIndex::MESSAGEPORT, args[1]);
280         targetOrigin = toWebCoreStringWithNullOrUndefinedCheck(args[2]);
281     } else {
282         targetOrigin = toWebCoreStringWithNullOrUndefinedCheck(args[1]);
283     }
284
285     if (tryCatch.HasCaught())
286         return v8::Undefined();
287
288     ExceptionCode ec = 0;
289     window->postMessage(message, port, targetOrigin, source, ec);
290     if (ec)
291         V8Proxy::setDOMException(ec);
292
293     return v8::Undefined();
294 }
295
296 CALLBACK_FUNC_DECL(DOMWindowAtob)
297 {
298     INC_STATS("DOM.DOMWindow.atob()");
299     DOMWindow* imp = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, args.Holder());
300
301     if (!V8Proxy::canAccessFrame(imp->frame(), true))
302         return v8::Undefined();
303
304     if (args.Length() < 1)
305         return throwError("Not enough arguments", V8Proxy::SyntaxError);
306
307     if (args[0]->IsNull())
308         return v8String("");
309
310     String str = toWebCoreString(args[0]);
311     return convertBase64(str, false);
312 }
313
314 CALLBACK_FUNC_DECL(DOMWindowBtoa)
315 {
316     INC_STATS("DOM.DOMWindow.btoa()");
317     DOMWindow* imp = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, args.Holder());
318
319     if (!V8Proxy::canAccessFrame(imp->frame(), true))
320         return v8::Undefined();
321
322     if (args.Length() < 1)
323         return throwError("Not enough arguments", V8Proxy::SyntaxError);
324
325     if (args[0]->IsNull())
326         return v8String("");
327
328     String str = toWebCoreString(args[0]);
329     return convertBase64(str, true);
330 }
331
332 // FIXME(fqian): returning string is cheating, and we should
333 // fix this by calling toString function on the receiver.
334 // However, V8 implements toString in JavaScript, which requires
335 // switching context of receiver. I consider it is dangerous.
336 CALLBACK_FUNC_DECL(DOMWindowToString)
337 {
338     INC_STATS("DOM.DOMWindow.toString()");
339     return args.This()->ObjectProtoToString();
340 }
341
342 CALLBACK_FUNC_DECL(DOMWindowNOP)
343 {
344     INC_STATS("DOM.DOMWindow.nop()");
345     return v8::Undefined();
346 }
347
348 static String eventNameFromAttributeName(const String& name)
349 {
350     ASSERT(name.startsWith("on"));
351     String eventType = name.substring(2);
352
353     if (eventType.startsWith("w")) {
354         switch(eventType[eventType.length() - 1]) {
355         case 't':
356             eventType = "webkitAnimationStart";
357             break;
358         case 'n':
359             eventType = "webkitAnimationIteration";
360             break;
361         case 'd':
362             ASSERT(eventType.length() > 7);
363             if (eventType[7] == 'a')
364                 eventType = "webkitAnimationEnd";
365             else
366                 eventType = "webkitTransitionEnd";
367             break;
368         }
369     }
370
371     return eventType;
372 }
373
374 ACCESSOR_SETTER(DOMWindowEventHandler)
375 {
376     v8::Handle<v8::Object> holder = V8Proxy::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, info.This());
377     if (holder.IsEmpty())
378         return;
379
380     DOMWindow* imp = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, holder);
381     if (!imp->frame())
382         return;
383
384     Document* doc = imp->frame()->document();
385     if (!doc)
386         return;
387
388     String key = toWebCoreString(name);
389     String eventType = eventNameFromAttributeName(key);
390
391     if (value->IsNull()) {
392         // Clear the event listener
393         imp->clearAttributeEventListener(eventType);
394     } else {
395         V8Proxy* proxy = V8Proxy::retrieve(imp->frame());
396         if (!proxy)
397             return;
398
399         RefPtr<EventListener> listener =
400             proxy->findOrCreateV8EventListener(value, true);
401         if (listener)
402             imp->setAttributeEventListener(eventType, listener);
403     }
404 }
405
406 ACCESSOR_GETTER(DOMWindowEventHandler)
407 {
408     v8::Handle<v8::Object> holder = V8Proxy::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, info.This());
409     if (holder.IsEmpty())
410         return v8::Undefined();
411
412     DOMWindow* imp = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, holder);
413     if (!imp->frame())
414         return v8::Undefined();
415
416     Document* doc = imp->frame()->document();
417     if (!doc)
418         return v8::Undefined();
419
420     String key = toWebCoreString(name);
421     String eventType = eventNameFromAttributeName(key);
422
423     EventListener* listener = imp->getAttributeEventListener(eventType);
424     return V8Proxy::convertEventListenerToV8Object(listener);
425 }
426
427 static bool canShowModalDialogNow(const Frame* frame)
428 {
429     // A frame can out live its page. See bug 1219613.
430     if (!frame || !frame->page())
431         return false;
432     return frame->page()->chrome()->canRunModalNow();
433 }
434
435 static bool allowPopUp()
436 {
437     Frame* frame = V8Proxy::retrieveFrameForEnteredContext();
438
439     ASSERT(frame);
440     if (frame->script()->processingUserGesture())
441         return true;
442     Settings* settings = frame->settings();
443     return settings && settings->javaScriptCanOpenWindowsAutomatically();
444 }
445
446 static HashMap<String, String> parseModalDialogFeatures(const String& featuresArg)
447 {
448     HashMap<String, String> map;
449
450     Vector<String> features;
451     featuresArg.split(';', features);
452     Vector<String>::const_iterator end = features.end();
453     for (Vector<String>::const_iterator it = features.begin(); it != end; ++it) {
454         String featureString = *it;
455         int pos = featureString.find('=');
456         int colonPos = featureString.find(':');
457         if (pos >= 0 && colonPos >= 0)
458             continue;  // ignore any strings that have both = and :
459         if (pos < 0)
460             pos = colonPos;
461         if (pos < 0) {
462             // null string for value means key without value
463             map.set(featureString.stripWhiteSpace().lower(), String());
464         } else {
465             String key = featureString.left(pos).stripWhiteSpace().lower();
466             String val = featureString.substring(pos + 1).stripWhiteSpace().lower();
467             int spacePos = val.find(' ');
468             if (spacePos != -1)
469                 val = val.left(spacePos);
470             map.set(key, val);
471         }
472     }
473
474     return map;
475 }
476
477
478 static Frame* createWindow(Frame* callingFrame,
479                            Frame* enteredFrame,
480                            Frame* openerFrame,
481                            const String& url,
482                            const String& frameName,
483                            const WindowFeatures& windowFeatures,
484                            v8::Local<v8::Value> dialogArgs)
485 {
486     ASSERT(callingFrame);
487     ASSERT(enteredFrame);
488
489     ResourceRequest request;
490
491     // For whatever reason, Firefox uses the entered frame to determine
492     // the outgoingReferrer.  We replicate that behavior here.
493     String referrer = enteredFrame->loader()->outgoingReferrer();
494     request.setHTTPReferrer(referrer);
495     FrameLoader::addHTTPOriginIfNeeded(request, enteredFrame->loader()->outgoingOrigin());
496     FrameLoadRequest frameRequest(request, frameName);
497
498     // FIXME: It's much better for client API if a new window starts with a URL,
499     // here where we know what URL we are going to open. Unfortunately, this
500     // code passes the empty string for the URL, but there's a reason for that.
501     // Before loading we have to set up the opener, openedByDOM,
502     // and dialogArguments values. Also, to decide whether to use the URL
503     // we currently do an allowsAccessFrom call using the window we create,
504     // which can't be done before creating it. We'd have to resolve all those
505     // issues to pass the URL instead of "".
506
507     bool created;
508     // We pass in the opener frame here so it can be used for looking up the
509     // frame name, in case the active frame is different from the opener frame,
510     // and the name references a frame relative to the opener frame, for example
511     // "_self" or "_parent".
512     Frame* newFrame = callingFrame->loader()->createWindow(openerFrame->loader(), frameRequest, windowFeatures, created);
513     if (!newFrame)
514         return 0;
515
516     newFrame->loader()->setOpener(openerFrame);
517     newFrame->loader()->setOpenedByDOM();
518
519     // Set dialog arguments on the global object of the new frame.
520     if (!dialogArgs.IsEmpty()) {
521         v8::Local<v8::Context> context = V8Proxy::context(newFrame);
522         if (!context.IsEmpty()) {
523             v8::Context::Scope scope(context);
524             context->Global()->Set(v8::String::New("dialogArguments"), dialogArgs);
525         }
526     }
527
528     if (protocolIsJavaScript(url) || ScriptController::isSafeScript(newFrame)) {
529         KURL completedUrl =
530             url.isEmpty() ? KURL("") : completeURL(url);
531         bool userGesture = processingUserGesture();
532
533         if (created)
534             newFrame->loader()->changeLocation(completedUrl, referrer, false, false, userGesture);
535         else if (!url.isEmpty())
536             newFrame->loader()->scheduleLocationChange(completedUrl.string(), referrer, false, userGesture);
537     }
538
539     return newFrame;
540 }
541
542
543
544 CALLBACK_FUNC_DECL(DOMWindowShowModalDialog)
545 {
546     INC_STATS("DOM.DOMWindow.showModalDialog()");
547     DOMWindow* window = V8Proxy::convertToNativeObject<DOMWindow>(
548         V8ClassIndex::DOMWINDOW, args.Holder());
549     Frame* frame = window->frame();
550
551     if (!frame || !V8Proxy::canAccessFrame(frame, true)) 
552         return v8::Undefined();
553
554     Frame* callingFrame = V8Proxy::retrieveFrameForCallingContext();
555     if (!callingFrame)
556         return v8::Undefined();
557
558     Frame* enteredFrame = V8Proxy::retrieveFrameForEnteredContext();
559     if (!enteredFrame)
560         return v8::Undefined();
561
562     if (!canShowModalDialogNow(frame) || !allowPopUp())
563         return v8::Undefined();
564
565     String url = toWebCoreStringWithNullOrUndefinedCheck(args[0]);
566     v8::Local<v8::Value> dialogArgs = args[1];
567     String featureArgs = toWebCoreStringWithNullOrUndefinedCheck(args[2]);
568
569     const HashMap<String, String> features = parseModalDialogFeatures(featureArgs);
570
571     const bool trusted = false;
572
573     FloatRect screenRect = screenAvailableRect(frame->view());
574
575     WindowFeatures windowFeatures;
576     // default here came from frame size of dialog in MacIE.
577     windowFeatures.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620);
578     windowFeatures.widthSet = true;
579     // default here came from frame size of dialog in MacIE.
580     windowFeatures.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450);
581     windowFeatures.heightSet = true;
582   
583     windowFeatures.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - windowFeatures.width, -1);
584     windowFeatures.xSet = windowFeatures.x > 0;
585     windowFeatures.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - windowFeatures.height, -1);
586     windowFeatures.ySet = windowFeatures.y > 0;
587
588     if (WindowFeatures::boolFeature(features, "center", true)) {
589         if (!windowFeatures.xSet) {
590             windowFeatures.x = screenRect.x() + (screenRect.width() - windowFeatures.width) / 2;
591             windowFeatures.xSet = true;
592         }
593         if (!windowFeatures.ySet) {
594             windowFeatures.y = screenRect.y() + (screenRect.height() - windowFeatures.height) / 2;
595             windowFeatures.ySet = true;
596         }
597     }
598
599     windowFeatures.dialog = true;
600     windowFeatures.resizable = WindowFeatures::boolFeature(features, "resizable");
601     windowFeatures.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true);
602     windowFeatures.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted);
603     windowFeatures.menuBarVisible = false;
604     windowFeatures.toolBarVisible = false;
605     windowFeatures.locationBarVisible = false;
606     windowFeatures.fullscreen = false;
607
608     Frame* dialogFrame = createWindow(callingFrame, enteredFrame, frame, url, "", windowFeatures, dialogArgs);
609     if (!dialogFrame)
610         return v8::Undefined();
611
612     // Hold on to the context of the dialog window long enough to retrieve the
613     // value of the return value property.
614     v8::Local<v8::Context> context = V8Proxy::context(dialogFrame);
615
616     // Run the dialog.
617     dialogFrame->page()->chrome()->runModal();
618
619     // Extract the return value property from the dialog window.
620     v8::Local<v8::Value> returnValue;
621     if (!context.IsEmpty()) {
622         v8::Context::Scope scope(context);
623         returnValue = context->Global()->Get(v8::String::New("returnValue"));
624     }
625
626     if (!returnValue.IsEmpty())
627         return returnValue;
628
629     return v8::Undefined();
630 }
631
632
633 CALLBACK_FUNC_DECL(DOMWindowOpen)
634 {
635     INC_STATS("DOM.DOMWindow.open()");
636     DOMWindow* parent = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, args.Holder());
637     Frame* frame = parent->frame();
638
639     if (!frame || !V8Proxy::canAccessFrame(frame, true))
640         return v8::Undefined();
641
642     Frame* callingFrame = V8Proxy::retrieveFrameForCallingContext();
643     if (!callingFrame)
644         return v8::Undefined();
645
646     Frame* enteredFrame = V8Proxy::retrieveFrameForEnteredContext();
647     if (!enteredFrame)
648         return v8::Undefined();
649
650     Page* page = frame->page();
651     if (!page)
652         return v8::Undefined();
653
654     String urlString = toWebCoreStringWithNullOrUndefinedCheck(args[0]);
655     AtomicString frameName = (args[1]->IsUndefined() || args[1]->IsNull()) ? "_blank" : AtomicString(toWebCoreString(args[1]));
656
657     // Because FrameTree::find() returns true for empty strings, we must check
658     // for empty framenames. Otherwise, illegitimate window.open() calls with
659     // no name will pass right through the popup blocker.
660     if (!allowPopUp() &&
661         (frameName.isEmpty() || !frame->tree()->find(frameName))) {
662         return v8::Undefined();
663     }
664
665     // Get the target frame for the special cases of _top and _parent.  In those
666     // cases, we can schedule a location change right now and return early.
667     bool topOrParent = false;
668     if (frameName == "_top") {
669         frame = frame->tree()->top();
670         topOrParent = true;
671     } else if (frameName == "_parent") {
672         if (Frame* parent = frame->tree()->parent())
673             frame = parent;
674         topOrParent = true;
675     }
676     if (topOrParent) {
677         if (!shouldAllowNavigation(frame))
678             return v8::Undefined();
679     
680         String completedUrl;
681         if (!urlString.isEmpty())
682             completedUrl = completeURL(urlString);
683     
684         if (!completedUrl.isEmpty() &&
685             (!protocolIsJavaScript(completedUrl) || ScriptController::isSafeScript(frame))) {
686             bool userGesture = processingUserGesture();
687
688             // For whatever reason, Firefox uses the entered frame to determine
689             // the outgoingReferrer.  We replicate that behavior here.
690             String referrer = enteredFrame->loader()->outgoingReferrer();
691
692             frame->loader()->scheduleLocationChange(completedUrl, referrer, false, userGesture);
693         }
694         return V8Proxy::convertToV8Object(V8ClassIndex::DOMWINDOW, frame->domWindow());
695     }
696
697     // In the case of a named frame or a new window, we'll use the
698     // createWindow() helper.
699
700     // Parse the values, and then work with a copy of the parsed values
701     // so we can restore the values we may not want to overwrite after
702     // we do the multiple monitor fixes.
703     WindowFeatures rawFeatures(toWebCoreStringWithNullOrUndefinedCheck(args[2]));
704     WindowFeatures windowFeatures(rawFeatures);
705     FloatRect screenRect = screenAvailableRect(page->mainFrame()->view());
706
707     // Set default size and location near parent window if none were specified.
708     // These may be further modified by adjustWindowRect, below.
709     if (!windowFeatures.xSet) {
710         windowFeatures.x = parent->screenX() - screenRect.x() + popupTilePixels;
711         windowFeatures.xSet = true;
712     }
713     if (!windowFeatures.ySet) {
714         windowFeatures.y = parent->screenY() - screenRect.y() + popupTilePixels;
715         windowFeatures.ySet = true;
716     }
717     if (!windowFeatures.widthSet) {
718         windowFeatures.width = parent->innerWidth();
719         windowFeatures.widthSet = true;
720     }
721     if (!windowFeatures.heightSet) {
722         windowFeatures.height = parent->innerHeight();
723         windowFeatures.heightSet = true;
724     }
725
726     FloatRect windowRect(windowFeatures.x, windowFeatures.y, windowFeatures.width, windowFeatures.height);
727
728     // The new window's location is relative to its current screen, so shift
729     // it in case it's on a secondary monitor. See http://b/viewIssue?id=967905.
730     windowRect.move(screenRect.x(), screenRect.y());
731     WebCore::DOMWindow::adjustWindowRect(screenRect, windowRect, windowRect);
732
733     windowFeatures.x = windowRect.x();
734     windowFeatures.y = windowRect.y();
735     windowFeatures.height = windowRect.height();
736     windowFeatures.width = windowRect.width();
737
738     // If either of the origin coordinates weren't set in the original
739     // string, make sure they aren't set now.
740     if (!rawFeatures.xSet) {
741         windowFeatures.x = 0;
742         windowFeatures.xSet = false;
743     }
744     if (!rawFeatures.ySet) {
745         windowFeatures.y = 0;
746         windowFeatures.ySet = false;
747     }
748
749     frame = createWindow(callingFrame, enteredFrame, frame, urlString, frameName, windowFeatures, v8::Local<v8::Value>());
750
751     if (!frame)
752         return v8::Undefined();
753
754     return V8Proxy::convertToV8Object(V8ClassIndex::DOMWINDOW, frame->domWindow());
755 }
756
757
758 INDEXED_PROPERTY_GETTER(DOMWindow)
759 {
760     INC_STATS("DOM.DOMWindow.IndexedPropertyGetter");
761     v8::Handle<v8::Object> holder = V8Proxy::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, info.This());
762     if (holder.IsEmpty())
763         return notHandledByInterceptor();
764
765     DOMWindow* window = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, holder);
766     if (!window)
767         return notHandledByInterceptor();
768
769     Frame* frame = window->frame();
770     if (!frame)
771         return notHandledByInterceptor();
772
773     Frame* child = frame->tree()->child(index);
774     if (child)
775         return V8Proxy::convertToV8Object(V8ClassIndex::DOMWINDOW, child->domWindow());
776
777     return notHandledByInterceptor();
778 }
779
780
781 NAMED_PROPERTY_GETTER(DOMWindow)
782 {
783     INC_STATS("DOM.DOMWindow.NamedPropertyGetter");
784
785     v8::Handle<v8::Object> holder = V8Proxy::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, info.This());
786     if (holder.IsEmpty())
787         return notHandledByInterceptor();
788
789     DOMWindow* window = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, holder);
790     if (!window)
791         return notHandledByInterceptor();
792
793     Frame* frame = window->frame();
794     // window is detached from a frame.
795     if (!frame)
796         return notHandledByInterceptor();
797
798     // Search sub-frames.
799     AtomicString propName = v8StringToAtomicWebCoreString(name);
800     Frame* child = frame->tree()->child(propName);
801     if (child)
802         return V8Proxy::convertToV8Object(V8ClassIndex::DOMWINDOW, child->domWindow());
803
804     // Search IDL functions defined in the prototype
805     v8::Handle<v8::Value> result = holder->GetRealNamedPropertyInPrototypeChain(name);
806     if (!result.IsEmpty())
807         return result;
808
809     // Search named items in the document.
810     Document* doc = frame->document();
811     if (doc) {
812         RefPtr<HTMLCollection> items = doc->windowNamedItems(propName);
813         if (items->length() >= 1) {
814             if (items->length() == 1)
815                 return V8Proxy::convertNodeToV8Object(items->firstItem());
816             else
817                 return V8Proxy::convertToV8Object(V8ClassIndex::HTMLCOLLECTION, items.get());
818         }
819     }
820
821     return notHandledByInterceptor();
822 }
823
824
825 void V8Custom::WindowSetLocation(DOMWindow* window, const String& relativeURL)
826 {
827     Frame* frame = window->frame();
828     if (!frame)
829         return;
830
831     if (!shouldAllowNavigation(frame))
832         return;
833
834     KURL url = completeURL(relativeURL);
835     if (url.isNull())
836         return;
837
838     navigateIfAllowed(frame, url, false, false);
839 }
840
841
842 CALLBACK_FUNC_DECL(DOMWindowSetTimeout)
843 {
844     INC_STATS("DOM.DOMWindow.setTimeout()");
845     return WindowSetTimeoutImpl(args, true);
846 }
847
848
849 CALLBACK_FUNC_DECL(DOMWindowSetInterval)
850 {
851     INC_STATS("DOM.DOMWindow.setInterval()");
852     return WindowSetTimeoutImpl(args, false);
853 }
854
855
856 void V8Custom::ClearTimeoutImpl(const v8::Arguments& args)
857 {
858     v8::Handle<v8::Value> holder = args.Holder();
859     DOMWindow* imp = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, holder);
860     if (!V8Proxy::canAccessFrame(imp->frame(), true))
861         return;
862     ScriptExecutionContext* context = static_cast<ScriptExecutionContext*>(imp->frame()->document());
863     int handle = toInt32(args[0]);
864     DOMTimer::removeById(context, handle);
865 }
866
867
868 CALLBACK_FUNC_DECL(DOMWindowClearTimeout)
869 {
870     INC_STATS("DOM.DOMWindow.clearTimeout");
871     ClearTimeoutImpl(args);
872     return v8::Undefined();
873 }
874
875 CALLBACK_FUNC_DECL(DOMWindowClearInterval)
876 {
877     INC_STATS("DOM.DOMWindow.clearInterval");
878     ClearTimeoutImpl(args);
879     return v8::Undefined();
880 }
881
882 NAMED_ACCESS_CHECK(DOMWindow)
883 {
884     ASSERT(V8ClassIndex::FromInt(data->Int32Value()) == V8ClassIndex::DOMWINDOW);
885     v8::Handle<v8::Value> window = V8Proxy::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, host);
886     if (window.IsEmpty())
887         return false;  // the frame is gone.
888
889     DOMWindow* targetWindow = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, window);
890
891     ASSERT(targetWindow);
892
893     Frame* target = targetWindow->frame();
894     if (!target)
895         return false;
896
897     if (key->IsString()) {
898         String name = toWebCoreString(key);
899
900         // Allow access of GET and HAS if index is a subframe.
901         if ((type == v8::ACCESS_GET || type == v8::ACCESS_HAS) && target->tree()->child(name))
902             return true;
903     }
904
905     return V8Proxy::canAccessFrame(target, false);
906 }
907
908 INDEXED_ACCESS_CHECK(DOMWindow)
909 {
910     ASSERT(V8ClassIndex::FromInt(data->Int32Value()) == V8ClassIndex::DOMWINDOW);
911     v8::Handle<v8::Value> window = V8Proxy::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, host);
912     if (window.IsEmpty())
913         return false;
914
915     DOMWindow* targetWindow = V8Proxy::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, window);
916
917     ASSERT(targetWindow);
918
919     Frame* target = targetWindow->frame();
920     if (!target)
921         return false;
922
923     // Allow access of GET and HAS if index is a subframe.
924     if ((type == v8::ACCESS_GET || type == v8::ACCESS_HAS) && target->tree()->child(index))
925         return true;
926
927     return V8Proxy::canAccessFrame(target, false);
928 }
929
930 } // namespace WebCore