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