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