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