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