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