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