• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
56 static const int popupTilePixels = 10;
57 
58 namespace WebCore {
59 
WindowSetTimeoutImpl(const v8::Arguments & args,bool singleShot)60 v8::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 
isAscii(const String & str)115 static 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 
convertBase64(const String & str,bool encode)124 static 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 
ACCESSOR_GETTER(DOMWindowEvent)146 ACCESSOR_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 
ACCESSOR_GETTER(DOMWindowCrypto)156 ACCESSOR_GETTER(DOMWindowCrypto)
157 {
158     // FIXME: Implement me.
159     return v8::Undefined();
160 }
161 
ACCESSOR_SETTER(DOMWindowLocation)162 ACCESSOR_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 
ACCESSOR_SETTER(DOMWindowOpener)173 ACCESSOR_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 
CALLBACK_FUNC_DECL(DOMWindowAddEventListener)197 CALLBACK_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 
CALLBACK_FUNC_DECL(DOMWindowRemoveEventListener)229 CALLBACK_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 
CALLBACK_FUNC_DECL(DOMWindowPostMessage)259 CALLBACK_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 
CALLBACK_FUNC_DECL(DOMWindowAtob)295 CALLBACK_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 
CALLBACK_FUNC_DECL(DOMWindowBtoa)313 CALLBACK_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.
CALLBACK_FUNC_DECL(DOMWindowToString)335 CALLBACK_FUNC_DECL(DOMWindowToString)
336 {
337     INC_STATS("DOM.DOMWindow.toString()");
338     return args.This()->ObjectProtoToString();
339 }
340 
CALLBACK_FUNC_DECL(DOMWindowNOP)341 CALLBACK_FUNC_DECL(DOMWindowNOP)
342 {
343     INC_STATS("DOM.DOMWindow.nop()");
344     return v8::Undefined();
345 }
346 
eventNameFromAttributeName(const String & name)347 static 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 
ACCESSOR_SETTER(DOMWindowEventHandler)373 ACCESSOR_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 
ACCESSOR_GETTER(DOMWindowEventHandler)404 ACCESSOR_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 
canShowModalDialogNow(const Frame * frame)425 static 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 
allowPopUp()433 static 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 
parseModalDialogFeatures(const String & featuresArg)444 static 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 
createWindow(Frame * callingFrame,Frame * enteredFrame,Frame * openerFrame,const String & url,const String & frameName,const WindowFeatures & windowFeatures,v8::Local<v8::Value> dialogArgs)476 static 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 
CALLBACK_FUNC_DECL(DOMWindowShowModalDialog)542 CALLBACK_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 
CALLBACK_FUNC_DECL(DOMWindowOpen)631 CALLBACK_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 
INDEXED_PROPERTY_GETTER(DOMWindow)756 INDEXED_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 
NAMED_PROPERTY_GETTER(DOMWindow)779 NAMED_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 
WindowSetLocation(DOMWindow * window,const String & relativeURL)823 void 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 
CALLBACK_FUNC_DECL(DOMWindowSetTimeout)840 CALLBACK_FUNC_DECL(DOMWindowSetTimeout)
841 {
842     INC_STATS("DOM.DOMWindow.setTimeout()");
843     return WindowSetTimeoutImpl(args, true);
844 }
845 
846 
CALLBACK_FUNC_DECL(DOMWindowSetInterval)847 CALLBACK_FUNC_DECL(DOMWindowSetInterval)
848 {
849     INC_STATS("DOM.DOMWindow.setInterval()");
850     return WindowSetTimeoutImpl(args, false);
851 }
852 
853 
ClearTimeoutImpl(const v8::Arguments & args)854 void 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 
CALLBACK_FUNC_DECL(DOMWindowClearTimeout)866 CALLBACK_FUNC_DECL(DOMWindowClearTimeout)
867 {
868     INC_STATS("DOM.DOMWindow.clearTimeout");
869     ClearTimeoutImpl(args);
870     return v8::Undefined();
871 }
872 
CALLBACK_FUNC_DECL(DOMWindowClearInterval)873 CALLBACK_FUNC_DECL(DOMWindowClearInterval)
874 {
875     INC_STATS("DOM.DOMWindow.clearInterval");
876     ClearTimeoutImpl(args);
877     return v8::Undefined();
878 }
879 
NAMED_ACCESS_CHECK(DOMWindow)880 NAMED_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 
INDEXED_ACCESS_CHECK(DOMWindow)906 INDEXED_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