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