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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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. 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 418 v8::Handle<v8::Value> V8DOMWindow::releaseEventsCallback(const v8::Arguments& args) 419 { 420 INC_STATS("DOM.DOMWindow.nop()"); 421 return v8::Undefined(); 422 } 423 424 v8::Handle<v8::Value> V8DOMWindow::captureEventsCallback(const v8::Arguments& args) 425 { 426 INC_STATS("DOM.DOMWindow.nop()"); 427 return v8::Undefined(); 428 } 429 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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