1 /* 2 * Copyright (C) 2009, 2011 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 "V8Window.h" 33 34 #include "V8HTMLCollection.h" 35 #include "V8Node.h" 36 #include "bindings/v8/BindingSecurity.h" 37 #include "bindings/v8/ExceptionState.h" 38 #include "bindings/v8/ScheduledAction.h" 39 #include "bindings/v8/ScriptController.h" 40 #include "bindings/v8/ScriptSourceCode.h" 41 #include "bindings/v8/SerializedScriptValue.h" 42 #include "bindings/v8/V8Binding.h" 43 #include "bindings/v8/V8EventListener.h" 44 #include "bindings/v8/V8EventListenerList.h" 45 #include "bindings/v8/V8GCForContextDispose.h" 46 #include "bindings/v8/V8HiddenPropertyName.h" 47 #include "bindings/v8/V8Utilities.h" 48 #include "core/dom/ExceptionCode.h" 49 #include "core/dom/MessagePort.h" 50 #include "core/html/HTMLCollection.h" 51 #include "core/html/HTMLDocument.h" 52 #include "core/inspector/ScriptCallStack.h" 53 #include "core/loader/FrameLoadRequest.h" 54 #include "core/loader/FrameLoader.h" 55 #include "core/page/Chrome.h" 56 #include "core/page/ContentSecurityPolicy.h" 57 #include "core/page/DOMTimer.h" 58 #include "core/page/DOMWindow.h" 59 #include "core/page/DOMWindowTimers.h" 60 #include "core/page/Frame.h" 61 #include "core/page/FrameView.h" 62 #include "core/page/Location.h" 63 #include "core/page/Page.h" 64 #include "core/page/Settings.h" 65 #include "core/page/WindowFeatures.h" 66 #include "core/platform/PlatformScreen.h" 67 #include "core/platform/graphics/MediaPlayer.h" 68 #include "core/storage/Storage.h" 69 #include "core/workers/SharedWorkerRepository.h" 70 #include "wtf/ArrayBuffer.h" 71 #include "wtf/OwnArrayPtr.h" 72 73 namespace WebCore { 74 75 // FIXME: There is a lot of duplication with SetTimeoutOrInterval() in V8WorkerGlobalScopeCustom.cpp. 76 // We should refactor this. 77 void WindowSetTimeoutImpl(const v8::FunctionCallbackInfo<v8::Value>& args, bool singleShot) 78 { 79 int argumentCount = args.Length(); 80 81 if (argumentCount < 1) 82 return; 83 84 DOMWindow* imp = V8Window::toNative(args.Holder()); 85 ScriptExecutionContext* scriptContext = static_cast<ScriptExecutionContext*>(imp->document()); 86 87 if (!scriptContext) { 88 setDOMException(InvalidAccessError, args.GetIsolate()); 89 return; 90 } 91 92 v8::Handle<v8::Value> function = args[0]; 93 String functionString; 94 if (!function->IsFunction()) { 95 if (function->IsString()) { 96 functionString = toWebCoreString(function); 97 } else { 98 v8::Handle<v8::Value> v8String = function->ToString(); 99 100 // Bail out if string conversion failed. 101 if (v8String.IsEmpty()) 102 return; 103 104 functionString = toWebCoreString(v8String); 105 } 106 107 // Don't allow setting timeouts to run empty functions! 108 // (Bug 1009597) 109 if (!functionString.length()) 110 return; 111 } 112 113 if (!BindingSecurity::shouldAllowAccessToFrame(imp->frame())) 114 return; 115 116 OwnPtr<ScheduledAction> action; 117 if (function->IsFunction()) { 118 int paramCount = argumentCount >= 2 ? argumentCount - 2 : 0; 119 OwnArrayPtr<v8::Local<v8::Value> > params; 120 if (paramCount > 0) { 121 params = adoptArrayPtr(new v8::Local<v8::Value>[paramCount]); 122 for (int i = 0; i < paramCount; i++) { 123 // parameters must be globalized 124 params[i] = args[i+2]; 125 } 126 } 127 128 // params is passed to action, and released in action's destructor 129 ASSERT(imp->frame()); 130 action = adoptPtr(new ScheduledAction(imp->frame()->script()->currentWorldContext(), v8::Handle<v8::Function>::Cast(function), paramCount, params.get(), args.GetIsolate())); 131 } else { 132 if (imp->document() && !imp->document()->contentSecurityPolicy()->allowEval()) { 133 v8SetReturnValue(args, 0); 134 return; 135 } 136 ASSERT(imp->frame()); 137 action = adoptPtr(new ScheduledAction(imp->frame()->script()->currentWorldContext(), functionString, KURL(), args.GetIsolate())); 138 } 139 140 int32_t timeout = argumentCount >= 2 ? args[1]->Int32Value() : 0; 141 int timerId; 142 if (singleShot) 143 timerId = DOMWindowTimers::setTimeout(imp, action.release(), timeout); 144 else 145 timerId = DOMWindowTimers::setInterval(imp, action.release(), timeout); 146 147 // Try to do the idle notification before the timeout expires to get better 148 // use of any idle time. Aim for the middle of the interval for simplicity. 149 if (timeout >= 0) { 150 double maximumFireInterval = static_cast<double>(timeout) / 1000 / 2; 151 V8GCForContextDispose::instance().notifyIdleSooner(maximumFireInterval); 152 } 153 154 v8SetReturnValue(args, timerId); 155 } 156 157 void V8Window::eventAttrGetterCustom(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info) 158 { 159 v8::Handle<v8::Object> holder = info.This()->FindInstanceInPrototypeChain(V8Window::GetTemplate(info.GetIsolate(), worldTypeInMainThread(info.GetIsolate()))); 160 if (holder.IsEmpty()) 161 return; 162 163 Frame* frame = V8Window::toNative(holder)->frame(); 164 if (!BindingSecurity::shouldAllowAccessToFrame(frame)) 165 return; 166 167 ASSERT(frame); 168 v8::Local<v8::Context> context = frame->script()->currentWorldContext(); 169 if (context.IsEmpty()) 170 return; 171 172 v8::Handle<v8::String> eventSymbol = V8HiddenPropertyName::event(); 173 v8::Handle<v8::Value> jsEvent = context->Global()->GetHiddenValue(eventSymbol); 174 if (jsEvent.IsEmpty()) 175 return; 176 v8SetReturnValue(info, jsEvent); 177 } 178 179 void V8Window::eventAttrSetterCustom(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info) 180 { 181 v8::Handle<v8::Object> holder = info.This()->FindInstanceInPrototypeChain(V8Window::GetTemplate(info.GetIsolate(), worldTypeInMainThread(info.GetIsolate()))); 182 if (holder.IsEmpty()) 183 return; 184 185 Frame* frame = V8Window::toNative(holder)->frame(); 186 if (!BindingSecurity::shouldAllowAccessToFrame(frame)) 187 return; 188 189 ASSERT(frame); 190 v8::Local<v8::Context> context = frame->script()->currentWorldContext(); 191 if (context.IsEmpty()) 192 return; 193 194 v8::Handle<v8::String> eventSymbol = V8HiddenPropertyName::event(); 195 context->Global()->SetHiddenValue(eventSymbol, value); 196 } 197 198 void V8Window::locationAttrSetterCustom(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info) 199 { 200 DOMWindow* imp = V8Window::toNative(info.Holder()); 201 202 DOMWindow* active = activeDOMWindow(); 203 if (!active) 204 return; 205 206 DOMWindow* first = firstDOMWindow(); 207 if (!first) 208 return; 209 210 if (Location* location = imp->location()) 211 location->setHref(active, first, toWebCoreString(value)); 212 } 213 214 void V8Window::openerAttrSetterCustom(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info) 215 { 216 DOMWindow* imp = V8Window::toNative(info.Holder()); 217 218 if (!BindingSecurity::shouldAllowAccessToFrame(imp->frame())) 219 return; 220 221 // Opener can be shadowed if it is in the same domain. 222 // Have a special handling of null value to behave 223 // like Firefox. See bug http://b/1224887 & http://b/791706. 224 if (value->IsNull()) { 225 // imp->frame() cannot be null, 226 // otherwise, SameOrigin check would have failed. 227 ASSERT(imp->frame()); 228 imp->frame()->loader()->setOpener(0); 229 } 230 231 // Delete the accessor from this object. 232 info.Holder()->Delete(name); 233 234 // Put property on the front (this) object. 235 info.This()->Set(name, value); 236 } 237 238 static bool isLegacyTargetOriginDesignation(v8::Handle<v8::Value> value) 239 { 240 if (value->IsString() || value->IsStringObject()) 241 return true; 242 return false; 243 } 244 245 246 void V8Window::postMessageMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) 247 { 248 // None of these need to be RefPtr because args and context are guaranteed 249 // to hold on to them. 250 DOMWindow* window = V8Window::toNative(args.Holder()); 251 DOMWindow* source = activeDOMWindow(); 252 253 // If called directly by WebCore we don't have a calling context. 254 if (!source) { 255 throwTypeError(args.GetIsolate()); 256 return; 257 } 258 259 // This function has variable arguments and can be: 260 // Per current spec: 261 // postMessage(message, targetOrigin) 262 // postMessage(message, targetOrigin, {sequence of transferrables}) 263 // Legacy non-standard implementations in webkit allowed: 264 // postMessage(message, {sequence of transferrables}, targetOrigin); 265 MessagePortArray portArray; 266 ArrayBufferArray arrayBufferArray; 267 int targetOriginArgIndex = 1; 268 if (args.Length() > 2) { 269 int transferablesArgIndex = 2; 270 if (isLegacyTargetOriginDesignation(args[2])) { 271 targetOriginArgIndex = 2; 272 transferablesArgIndex = 1; 273 } 274 if (!extractTransferables(args[transferablesArgIndex], portArray, arrayBufferArray, args.GetIsolate())) 275 return; 276 } 277 V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource<WithUndefinedOrNullCheck>, targetOrigin, args[targetOriginArgIndex]); 278 279 bool didThrow = false; 280 RefPtr<SerializedScriptValue> message = 281 SerializedScriptValue::create(args[0], &portArray, &arrayBufferArray, didThrow, args.GetIsolate()); 282 if (didThrow) 283 return; 284 285 ExceptionState es(args.GetIsolate()); 286 window->postMessage(message.release(), &portArray, targetOrigin, source, es); 287 es.throwIfNeeded(); 288 } 289 290 // FIXME(fqian): returning string is cheating, and we should 291 // fix this by calling toString function on the receiver. 292 // However, V8 implements toString in JavaScript, which requires 293 // switching context of receiver. I consider it is dangerous. 294 void V8Window::toStringMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) 295 { 296 v8::Handle<v8::Object> domWrapper = args.This()->FindInstanceInPrototypeChain(V8Window::GetTemplate(args.GetIsolate(), worldTypeInMainThread(args.GetIsolate()))); 297 if (domWrapper.IsEmpty()) { 298 v8SetReturnValue(args, args.This()->ObjectProtoToString()); 299 return; 300 } 301 v8SetReturnValue(args, domWrapper->ObjectProtoToString()); 302 } 303 304 class DialogHandler { 305 public: 306 explicit DialogHandler(v8::Handle<v8::Value> dialogArguments) 307 : m_dialogArguments(dialogArguments) 308 { 309 } 310 311 void dialogCreated(DOMWindow*); 312 v8::Handle<v8::Value> returnValue() const; 313 314 private: 315 v8::Handle<v8::Value> m_dialogArguments; 316 v8::Handle<v8::Context> m_dialogContext; 317 }; 318 319 inline void DialogHandler::dialogCreated(DOMWindow* dialogFrame) 320 { 321 m_dialogContext = dialogFrame->frame() ? dialogFrame->frame()->script()->currentWorldContext() : v8::Local<v8::Context>(); 322 if (m_dialogContext.IsEmpty()) 323 return; 324 if (m_dialogArguments.IsEmpty()) 325 return; 326 v8::Context::Scope scope(m_dialogContext); 327 m_dialogContext->Global()->Set(v8::String::NewSymbol("dialogArguments"), m_dialogArguments); 328 } 329 330 inline v8::Handle<v8::Value> DialogHandler::returnValue() const 331 { 332 if (m_dialogContext.IsEmpty()) 333 return v8::Undefined(); 334 v8::Context::Scope scope(m_dialogContext); 335 v8::Handle<v8::Value> returnValue = m_dialogContext->Global()->Get(v8::String::NewSymbol("returnValue")); 336 if (returnValue.IsEmpty()) 337 return v8::Undefined(); 338 return returnValue; 339 } 340 341 static void setUpDialog(DOMWindow* dialog, void* handler) 342 { 343 static_cast<DialogHandler*>(handler)->dialogCreated(dialog); 344 } 345 346 void V8Window::showModalDialogMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) 347 { 348 DOMWindow* impl = V8Window::toNative(args.Holder()); 349 if (!BindingSecurity::shouldAllowAccessToFrame(impl->frame())) 350 return; 351 352 // FIXME: Handle exceptions properly. 353 String urlString = toWebCoreStringWithUndefinedOrNullCheck(args[0]); 354 DialogHandler handler(args[1]); 355 String dialogFeaturesString = toWebCoreStringWithUndefinedOrNullCheck(args[2]); 356 357 impl->showModalDialog(urlString, dialogFeaturesString, activeDOMWindow(), firstDOMWindow(), setUpDialog, &handler); 358 359 v8SetReturnValue(args, handler.returnValue()); 360 } 361 362 void V8Window::openMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) 363 { 364 DOMWindow* impl = V8Window::toNative(args.Holder()); 365 if (!BindingSecurity::shouldAllowAccessToFrame(impl->frame())) 366 return; 367 368 // FIXME: Handle exceptions properly. 369 String urlString = toWebCoreStringWithUndefinedOrNullCheck(args[0]); 370 AtomicString frameName = (args[1]->IsUndefined() || args[1]->IsNull()) ? "_blank" : AtomicString(toWebCoreString(args[1])); 371 String windowFeaturesString = toWebCoreStringWithUndefinedOrNullCheck(args[2]); 372 373 RefPtr<DOMWindow> openedWindow = impl->open(urlString, frameName, windowFeaturesString, activeDOMWindow(), firstDOMWindow()); 374 if (!openedWindow) 375 return; 376 377 v8SetReturnValue(args, toV8Fast(openedWindow.release(), args, impl)); 378 } 379 380 void V8Window::namedPropertyGetterCustom(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info) 381 { 382 383 DOMWindow* window = V8Window::toNative(info.Holder()); 384 if (!window) 385 return; 386 387 Frame* frame = window->frame(); 388 // window is detached from a frame. 389 if (!frame) 390 return; 391 392 // Search sub-frames. 393 AtomicString propName = toWebCoreAtomicString(name); 394 Frame* child = frame->tree()->scopedChild(propName); 395 if (child) { 396 v8SetReturnValue(info, toV8Fast(child->domWindow(), info, window)); 397 return; 398 } 399 400 // Search IDL functions defined in the prototype 401 if (!info.Holder()->GetRealNamedProperty(name).IsEmpty()) 402 return; 403 404 // Search named items in the document. 405 Document* doc = frame->document(); 406 407 if (doc && doc->isHTMLDocument()) { 408 if (toHTMLDocument(doc)->hasNamedItem(propName.impl()) || doc->hasElementWithId(propName.impl())) { 409 RefPtr<HTMLCollection> items = doc->windowNamedItems(propName); 410 if (!items->isEmpty()) { 411 if (items->hasExactlyOneItem()) { 412 v8SetReturnValue(info, toV8Fast(items->item(0), info, window)); 413 return; 414 } 415 v8SetReturnValue(info, toV8Fast(items.release(), info, window)); 416 return; 417 } 418 } 419 } 420 } 421 422 423 void V8Window::setTimeoutMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) 424 { 425 WindowSetTimeoutImpl(args, true); 426 } 427 428 429 void V8Window::setIntervalMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& args) 430 { 431 WindowSetTimeoutImpl(args, false); 432 } 433 434 bool V8Window::namedSecurityCheckCustom(v8::Local<v8::Object> host, v8::Local<v8::Value> key, v8::AccessType type, v8::Local<v8::Value>) 435 { 436 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 437 v8::Handle<v8::Object> window = host->FindInstanceInPrototypeChain(V8Window::GetTemplate(isolate, worldTypeInMainThread(isolate))); 438 if (window.IsEmpty()) 439 return false; // the frame is gone. 440 441 DOMWindow* targetWindow = V8Window::toNative(window); 442 443 ASSERT(targetWindow); 444 445 Frame* target = targetWindow->frame(); 446 if (!target) 447 return false; 448 449 // Notify the loader's client if the initial document has been accessed. 450 if (target->loader()->stateMachine()->isDisplayingInitialEmptyDocument()) 451 target->loader()->didAccessInitialDocument(); 452 453 if (key->IsString()) { 454 DEFINE_STATIC_LOCAL(AtomicString, nameOfProtoProperty, ("__proto__", AtomicString::ConstructFromLiteral)); 455 456 String name = toWebCoreString(key); 457 Frame* childFrame = target->tree()->scopedChild(name); 458 // Notice that we can't call HasRealNamedProperty for ACCESS_HAS 459 // because that would generate infinite recursion. 460 if (type == v8::ACCESS_HAS && childFrame) 461 return true; 462 // We need to explicitly compare against nameOfProtoProperty because 463 // V8's JSObject::LocalLookup finds __proto__ before 464 // interceptors and even when __proto__ isn't a "real named property". 465 v8::Handle<v8::String> keyString = key->ToString(); 466 if (type == v8::ACCESS_GET 467 && childFrame 468 && !host->HasRealNamedProperty(keyString) 469 && !window->HasRealNamedProperty(keyString) 470 && name != nameOfProtoProperty) 471 return true; 472 } 473 474 return BindingSecurity::shouldAllowAccessToFrame(target, DoNotReportSecurityError); 475 } 476 477 bool V8Window::indexedSecurityCheckCustom(v8::Local<v8::Object> host, uint32_t index, v8::AccessType type, v8::Local<v8::Value>) 478 { 479 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 480 v8::Handle<v8::Object> window = host->FindInstanceInPrototypeChain(V8Window::GetTemplate(isolate, worldTypeInMainThread(isolate))); 481 if (window.IsEmpty()) 482 return false; 483 484 DOMWindow* targetWindow = V8Window::toNative(window); 485 486 ASSERT(targetWindow); 487 488 Frame* target = targetWindow->frame(); 489 if (!target) 490 return false; 491 492 // Notify the loader's client if the initial document has been accessed. 493 if (target->loader()->stateMachine()->isDisplayingInitialEmptyDocument()) 494 target->loader()->didAccessInitialDocument(); 495 496 Frame* childFrame = target->tree()->scopedChild(index); 497 498 // Notice that we can't call HasRealNamedProperty for ACCESS_HAS 499 // because that would generate infinite recursion. 500 if (type == v8::ACCESS_HAS && childFrame) 501 return true; 502 if (type == v8::ACCESS_GET 503 && childFrame 504 && !host->HasRealIndexedProperty(index) 505 && !window->HasRealIndexedProperty(index)) 506 return true; 507 508 return BindingSecurity::shouldAllowAccessToFrame(target, DoNotReportSecurityError); 509 } 510 511 v8::Handle<v8::Value> toV8(DOMWindow* window, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate) 512 { 513 // Notice that we explicitly ignore creationContext because the DOMWindow is its own creationContext. 514 515 if (!window) 516 return v8NullWithCheck(isolate); 517 // Initializes environment of a frame, and return the global object 518 // of the frame. 519 Frame* frame = window->frame(); 520 if (!frame) 521 return v8Undefined(); 522 523 // Special case: Because of executeScriptInIsolatedWorld() one DOMWindow can have 524 // multiple contexts and multiple global objects associated with it. When 525 // code running in one of those contexts accesses the window object, we 526 // want to return the global object associated with that context, not 527 // necessarily the first global object associated with that DOMWindow. 528 v8::Handle<v8::Context> currentContext = v8::Context::GetCurrent(); 529 v8::Handle<v8::Object> currentGlobal = currentContext->Global(); 530 v8::Handle<v8::Object> windowWrapper = currentGlobal->FindInstanceInPrototypeChain(V8Window::GetTemplate(isolate, worldTypeInMainThread(isolate))); 531 if (!windowWrapper.IsEmpty()) { 532 if (V8Window::toNative(windowWrapper) == window) 533 return currentGlobal; 534 } 535 536 // Otherwise, return the global object associated with this frame. 537 v8::Handle<v8::Context> context = frame->script()->currentWorldContext(); 538 if (context.IsEmpty()) 539 return v8Undefined(); 540 541 v8::Handle<v8::Object> global = context->Global(); 542 ASSERT(!global.IsEmpty()); 543 return global; 544 } 545 546 v8::Handle<v8::Value> toV8ForMainWorld(DOMWindow* window, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate) 547 { 548 return toV8(window, creationContext, isolate); 549 } 550 551 } // namespace WebCore 552