• 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 "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.
70 static const int popupTilePixels = 10;
71 
72 namespace WebCore {
73 
WindowSetTimeoutImpl(const v8::Arguments & args,bool singleShot)74 v8::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 
isAscii(const String & str)141 static 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 
convertBase64(const String & str,bool encode)150 static 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 
eventAccessorGetter(v8::Local<v8::String> name,const v8::AccessorInfo & info)172 v8::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 
eventAccessorSetter(v8::Local<v8::String> name,v8::Local<v8::Value> value,const v8::AccessorInfo & info)193 void 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 
cryptoAccessorGetter(v8::Local<v8::String> name,const v8::AccessorInfo & info)211 v8::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 
locationAccessorSetter(v8::Local<v8::String> name,v8::Local<v8::Value> value,const v8::AccessorInfo & info)217 void 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 
openerAccessorSetter(v8::Local<v8::String> name,v8::Local<v8::Value> value,const v8::AccessorInfo & info)224 void 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 
AudioAccessorGetter(v8::Local<v8::String> name,const v8::AccessorInfo & info)250 v8::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 
ImageAccessorGetter(v8::Local<v8::String> name,const v8::AccessorInfo & info)258 v8::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 
OptionAccessorGetter(v8::Local<v8::String> name,const v8::AccessorInfo & info)264 v8::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 
addEventListenerCallback(const v8::Arguments & args)270 v8::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 
removeEventListenerCallback(const v8::Arguments & args)303 v8::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 
postMessageCallback(const v8::Arguments & args)334 v8::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 
atobCallback(const v8::Arguments & args)367 v8::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 
btoaCallback(const v8::Arguments & args)386 v8::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.
toStringCallback(const v8::Arguments & args)409 v8::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 
releaseEventsCallback(const v8::Arguments & args)418 v8::Handle<v8::Value> V8DOMWindow::releaseEventsCallback(const v8::Arguments& args)
419 {
420     INC_STATS("DOM.DOMWindow.nop()");
421     return v8::Undefined();
422 }
423 
captureEventsCallback(const v8::Arguments & args)424 v8::Handle<v8::Value> V8DOMWindow::captureEventsCallback(const v8::Arguments& args)
425 {
426     INC_STATS("DOM.DOMWindow.nop()");
427     return v8::Undefined();
428 }
429 
canShowModalDialogNow(const Frame * frame)430 static 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 
allowPopUp()438 static 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 
parseModalDialogFeatures(const String & featuresArg)449 static 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 
showModalDialogCallback(const v8::Arguments & args)480 v8::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 
openCallback(const v8::Arguments & args)569 v8::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 
indexedPropertyGetter(uint32_t index,const v8::AccessorInfo & info)696 v8::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 
namedPropertyGetter(v8::Local<v8::String> name,const v8::AccessorInfo & info)716 v8::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 
setTimeoutCallback(const v8::Arguments & args)758 v8::Handle<v8::Value> V8DOMWindow::setTimeoutCallback(const v8::Arguments& args)
759 {
760     INC_STATS("DOM.DOMWindow.setTimeout()");
761     return WindowSetTimeoutImpl(args, true);
762 }
763 
764 
setIntervalCallback(const v8::Arguments & args)765 v8::Handle<v8::Value> V8DOMWindow::setIntervalCallback(const v8::Arguments& args)
766 {
767     INC_STATS("DOM.DOMWindow.setInterval()");
768     return WindowSetTimeoutImpl(args, false);
769 }
770 
771 
ClearTimeoutImpl(const v8::Arguments & args)772 void 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 
clearTimeoutCallback(const v8::Arguments & args)787 v8::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 
clearIntervalCallback(const v8::Arguments & args)794 v8::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 
namedSecurityCheck(v8::Local<v8::Object> host,v8::Local<v8::Value> key,v8::AccessType type,v8::Local<v8::Value> data)801 bool 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 
indexedSecurityCheck(v8::Local<v8::Object> host,uint32_t index,v8::AccessType type,v8::Local<v8::Value> data)827 bool 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 
toV8(DOMWindow * window)849 v8::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