1/* 2 * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20#include "config.h" 21#include "JSDOMWindowCustom.h" 22 23#include "AtomicString.h" 24#include "Base64.h" 25#include "Chrome.h" 26#include "DOMWindow.h" 27#include "Document.h" 28#include "ExceptionCode.h" 29#include "FloatRect.h" 30#include "Frame.h" 31#include "FrameLoadRequest.h" 32#include "FrameLoader.h" 33#include "FrameTree.h" 34#include "FrameView.h" 35#include "HTMLCollection.h" 36#include "HTMLDocument.h" 37#include "History.h" 38#include "JSAudioConstructor.h" 39#include "JSDOMWindowShell.h" 40#include "JSEvent.h" 41#include "JSEventListener.h" 42#include "JSEventSourceConstructor.h" 43#include "JSHTMLCollection.h" 44#include "JSHistory.h" 45#include "JSImageConstructor.h" 46#include "JSLocation.h" 47#include "JSMessageChannelConstructor.h" 48#include "JSMessagePort.h" 49#include "JSMessagePortCustom.h" 50#include "JSOptionConstructor.h" 51 52#if ENABLE(SHARED_WORKERS) 53#include "JSSharedWorkerConstructor.h" 54#endif 55 56#if ENABLE(3D_CANVAS) 57#include "JSWebGLArrayBufferConstructor.h" 58#include "JSWebGLByteArrayConstructor.h" 59#include "JSWebGLUnsignedByteArrayConstructor.h" 60#include "JSWebGLIntArrayConstructor.h" 61#include "JSWebGLUnsignedIntArrayConstructor.h" 62#include "JSWebGLShortArrayConstructor.h" 63#include "JSWebGLUnsignedShortArrayConstructor.h" 64#include "JSWebGLFloatArrayConstructor.h" 65#endif 66#include "JSWebKitCSSMatrixConstructor.h" 67#include "JSWebKitPointConstructor.h" 68#if ENABLE(WEB_SOCKETS) 69#include "JSWebSocketConstructor.h" 70#endif 71#include "JSWorkerConstructor.h" 72#include "JSXMLHttpRequestConstructor.h" 73#include "JSXSLTProcessorConstructor.h" 74#include "Location.h" 75#include "MediaPlayer.h" 76#include "MessagePort.h" 77#include "NotificationCenter.h" 78#include "Page.h" 79#include "PlatformScreen.h" 80#include "RegisteredEventListener.h" 81#include "ScheduledAction.h" 82#include "ScriptController.h" 83#include "SerializedScriptValue.h" 84#include "Settings.h" 85#include "SharedWorkerRepository.h" 86#include "WindowFeatures.h" 87#include <runtime/Error.h> 88#include <runtime/JSFunction.h> 89#include <runtime/JSObject.h> 90#include <runtime/PrototypeFunction.h> 91 92using namespace JSC; 93 94namespace WebCore { 95 96void JSDOMWindow::markChildren(MarkStack& markStack) 97{ 98 Base::markChildren(markStack); 99 100 impl()->markJSEventListeners(markStack); 101 102 JSGlobalData& globalData = *Heap::heap(this)->globalData(); 103 104 markDOMObjectWrapper(markStack, globalData, impl()->optionalConsole()); 105 markDOMObjectWrapper(markStack, globalData, impl()->optionalHistory()); 106 markDOMObjectWrapper(markStack, globalData, impl()->optionalLocationbar()); 107 markDOMObjectWrapper(markStack, globalData, impl()->optionalMenubar()); 108 markDOMObjectWrapper(markStack, globalData, impl()->optionalNavigator()); 109 markDOMObjectWrapper(markStack, globalData, impl()->optionalPersonalbar()); 110 markDOMObjectWrapper(markStack, globalData, impl()->optionalScreen()); 111 markDOMObjectWrapper(markStack, globalData, impl()->optionalScrollbars()); 112 markDOMObjectWrapper(markStack, globalData, impl()->optionalSelection()); 113 markDOMObjectWrapper(markStack, globalData, impl()->optionalStatusbar()); 114 markDOMObjectWrapper(markStack, globalData, impl()->optionalToolbar()); 115 markDOMObjectWrapper(markStack, globalData, impl()->optionalLocation()); 116 markDOMObjectWrapper(markStack, globalData, impl()->optionalMedia()); 117#if ENABLE(DOM_STORAGE) 118 markDOMObjectWrapper(markStack, globalData, impl()->optionalSessionStorage()); 119 markDOMObjectWrapper(markStack, globalData, impl()->optionalLocalStorage()); 120#endif 121#if ENABLE(OFFLINE_WEB_APPLICATIONS) 122 markDOMObjectWrapper(markStack, globalData, impl()->optionalApplicationCache()); 123#endif 124} 125 126template<NativeFunction nativeFunction, int length> 127JSValue nonCachingStaticFunctionGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot&) 128{ 129 return new (exec) NativeFunctionWrapper(exec, exec->lexicalGlobalObject()->prototypeFunctionStructure(), length, propertyName, nativeFunction); 130} 131 132static JSValue childFrameGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot) 133{ 134 return toJS(exec, static_cast<JSDOMWindow*>(asObject(slot.slotBase()))->impl()->frame()->tree()->child(AtomicString(propertyName))->domWindow()); 135} 136 137static JSValue indexGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) 138{ 139 return toJS(exec, static_cast<JSDOMWindow*>(asObject(slot.slotBase()))->impl()->frame()->tree()->child(slot.index())->domWindow()); 140} 141 142static JSValue namedItemGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot) 143{ 144 JSDOMWindowBase* thisObj = static_cast<JSDOMWindow*>(asObject(slot.slotBase())); 145 Document* document = thisObj->impl()->frame()->document(); 146 147 ASSERT(thisObj->allowsAccessFrom(exec)); 148 ASSERT(document); 149 ASSERT(document->isHTMLDocument()); 150 151 RefPtr<HTMLCollection> collection = document->windowNamedItems(propertyName); 152 if (collection->length() == 1) 153 return toJS(exec, collection->firstItem()); 154 return toJS(exec, collection.get()); 155} 156 157bool JSDOMWindow::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) 158{ 159 // When accessing a Window cross-domain, functions are always the native built-in ones, and they 160 // are not affected by properties changed on the Window or anything in its prototype chain. 161 // This is consistent with the behavior of Firefox. 162 163 const HashEntry* entry; 164 165 // We don't want any properties other than "close" and "closed" on a closed window. 166 if (!impl()->frame()) { 167 // The following code is safe for cross-domain and same domain use. 168 // It ignores any custom properties that might be set on the DOMWindow (including a custom prototype). 169 entry = s_info.propHashTable(exec)->entry(exec, propertyName); 170 if (entry && !(entry->attributes() & Function) && entry->propertyGetter() == jsDOMWindowClosed) { 171 slot.setCustom(this, entry->propertyGetter()); 172 return true; 173 } 174 entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName); 175 if (entry && (entry->attributes() & Function) && entry->function() == jsDOMWindowPrototypeFunctionClose) { 176 slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); 177 return true; 178 } 179 180 // FIXME: We should have a message here that explains why the property access/function call was 181 // not allowed. 182 slot.setUndefined(); 183 return true; 184 } 185 186 // We need to check for cross-domain access here without printing the generic warning message 187 // because we always allow access to some function, just different ones depending whether access 188 // is allowed. 189 String errorMessage; 190 bool allowsAccess = allowsAccessFrom(exec, errorMessage); 191 192 // Look for overrides before looking at any of our own properties, but ignore overrides completely 193 // if this is cross-domain access. 194 if (allowsAccess && JSGlobalObject::getOwnPropertySlot(exec, propertyName, slot)) 195 return true; 196 197 // We need this code here because otherwise JSDOMWindowBase will stop the search before we even get to the 198 // prototype due to the blanket same origin (allowsAccessFrom) check at the end of getOwnPropertySlot. 199 // Also, it's important to get the implementation straight out of the DOMWindow prototype regardless of 200 // what prototype is actually set on this object. 201 entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName); 202 if (entry) { 203 if (entry->attributes() & Function) { 204 if (entry->function() == jsDOMWindowPrototypeFunctionBlur) { 205 if (!allowsAccess) { 206 slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionBlur, 0>); 207 return true; 208 } 209 } else if (entry->function() == jsDOMWindowPrototypeFunctionClose) { 210 if (!allowsAccess) { 211 slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); 212 return true; 213 } 214 } else if (entry->function() == jsDOMWindowPrototypeFunctionFocus) { 215 if (!allowsAccess) { 216 slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionFocus, 0>); 217 return true; 218 } 219 } else if (entry->function() == jsDOMWindowPrototypeFunctionPostMessage) { 220 if (!allowsAccess) { 221 slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionPostMessage, 2>); 222 return true; 223 } 224 } else if (entry->function() == jsDOMWindowPrototypeFunctionShowModalDialog) { 225 if (!DOMWindow::canShowModalDialog(impl()->frame())) { 226 slot.setUndefined(); 227 return true; 228 } 229 } 230 } 231 } else { 232 // Allow access to toString() cross-domain, but always Object.prototype.toString. 233 if (propertyName == exec->propertyNames().toString) { 234 if (!allowsAccess) { 235 slot.setCustom(this, objectToStringFunctionGetter); 236 return true; 237 } 238 } 239 } 240 241 entry = JSDOMWindow::s_info.propHashTable(exec)->entry(exec, propertyName); 242 if (entry) { 243 slot.setCustom(this, entry->propertyGetter()); 244 return true; 245 } 246 247 // Check for child frames by name before built-in properties to 248 // match Mozilla. This does not match IE, but some sites end up 249 // naming frames things that conflict with window properties that 250 // are in Moz but not IE. Since we have some of these, we have to do 251 // it the Moz way. 252 if (impl()->frame()->tree()->child(propertyName)) { 253 slot.setCustom(this, childFrameGetter); 254 return true; 255 } 256 257 // Do prototype lookup early so that functions and attributes in the prototype can have 258 // precedence over the index and name getters. 259 JSValue proto = prototype(); 260 if (proto.isObject()) { 261 if (asObject(proto)->getPropertySlot(exec, propertyName, slot)) { 262 if (!allowsAccess) { 263 printErrorMessage(errorMessage); 264 slot.setUndefined(); 265 } 266 return true; 267 } 268 } 269 270 // FIXME: Search the whole frame hierarchy somewhere around here. 271 // We need to test the correct priority order. 272 273 // allow window[1] or parent[1] etc. (#56983) 274 bool ok; 275 unsigned i = propertyName.toArrayIndex(&ok); 276 if (ok && i < impl()->frame()->tree()->childCount()) { 277 slot.setCustomIndex(this, i, indexGetter); 278 return true; 279 } 280 281 if (!allowsAccess) { 282 printErrorMessage(errorMessage); 283 slot.setUndefined(); 284 return true; 285 } 286 287 // Allow shortcuts like 'Image1' instead of document.images.Image1 288 Document* document = impl()->frame()->document(); 289 if (document->isHTMLDocument()) { 290 AtomicStringImpl* atomicPropertyName = AtomicString::find(propertyName); 291 if (atomicPropertyName && (static_cast<HTMLDocument*>(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) { 292 slot.setCustom(this, namedItemGetter); 293 return true; 294 } 295 } 296 297 return Base::getOwnPropertySlot(exec, propertyName, slot); 298} 299 300bool JSDOMWindow::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) 301{ 302 // Never allow cross-domain getOwnPropertyDescriptor 303 if (!allowsAccessFrom(exec)) 304 return false; 305 306 const HashEntry* entry; 307 308 // We don't want any properties other than "close" and "closed" on a closed window. 309 if (!impl()->frame()) { 310 // The following code is safe for cross-domain and same domain use. 311 // It ignores any custom properties that might be set on the DOMWindow (including a custom prototype). 312 entry = s_info.propHashTable(exec)->entry(exec, propertyName); 313 if (entry && !(entry->attributes() & Function) && entry->propertyGetter() == jsDOMWindowClosed) { 314 descriptor.setDescriptor(jsBoolean(true), ReadOnly | DontDelete | DontEnum); 315 return true; 316 } 317 entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName); 318 if (entry && (entry->attributes() & Function) && entry->function() == jsDOMWindowPrototypeFunctionClose) { 319 PropertySlot slot; 320 slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); 321 descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); 322 return true; 323 } 324 descriptor.setUndefined(); 325 return true; 326 } 327 328 entry = JSDOMWindow::s_info.propHashTable(exec)->entry(exec, propertyName); 329 if (entry) { 330 PropertySlot slot; 331 slot.setCustom(this, entry->propertyGetter()); 332 descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); 333 return true; 334 } 335 336 // Check for child frames by name before built-in properties to 337 // match Mozilla. This does not match IE, but some sites end up 338 // naming frames things that conflict with window properties that 339 // are in Moz but not IE. Since we have some of these, we have to do 340 // it the Moz way. 341 if (impl()->frame()->tree()->child(propertyName)) { 342 PropertySlot slot; 343 slot.setCustom(this, childFrameGetter); 344 descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); 345 return true; 346 } 347 348 bool ok; 349 unsigned i = propertyName.toArrayIndex(&ok); 350 if (ok && i < impl()->frame()->tree()->childCount()) { 351 PropertySlot slot; 352 slot.setCustomIndex(this, i, indexGetter); 353 descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); 354 return true; 355 } 356 357 // Allow shortcuts like 'Image1' instead of document.images.Image1 358 Document* document = impl()->frame()->document(); 359 if (document->isHTMLDocument()) { 360 AtomicStringImpl* atomicPropertyName = AtomicString::find(propertyName); 361 if (atomicPropertyName && (static_cast<HTMLDocument*>(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) { 362 PropertySlot slot; 363 slot.setCustom(this, namedItemGetter); 364 descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); 365 return true; 366 } 367 } 368 369 return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); 370} 371 372void JSDOMWindow::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) 373{ 374 if (!impl()->frame()) 375 return; 376 377 // Optimization: access JavaScript global variables directly before involving the DOM. 378 if (JSGlobalObject::hasOwnPropertyForWrite(exec, propertyName)) { 379 if (allowsAccessFrom(exec)) 380 JSGlobalObject::put(exec, propertyName, value, slot); 381 return; 382 } 383 384 if (lookupPut<JSDOMWindow>(exec, propertyName, value, s_info.propHashTable(exec), this)) 385 return; 386 387 if (allowsAccessFrom(exec)) 388 Base::put(exec, propertyName, value, slot); 389} 390 391bool JSDOMWindow::deleteProperty(ExecState* exec, const Identifier& propertyName) 392{ 393 // Only allow deleting properties by frames in the same origin. 394 if (!allowsAccessFrom(exec)) 395 return false; 396 return Base::deleteProperty(exec, propertyName); 397} 398 399void JSDOMWindow::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 400{ 401 // Only allow the window to enumerated by frames in the same origin. 402 if (!allowsAccessFrom(exec)) 403 return; 404 Base::getPropertyNames(exec, propertyNames, mode); 405} 406 407void JSDOMWindow::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 408{ 409 // Only allow the window to enumerated by frames in the same origin. 410 if (!allowsAccessFrom(exec)) 411 return; 412 Base::getOwnPropertyNames(exec, propertyNames, mode); 413} 414 415void JSDOMWindow::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes) 416{ 417 // Only allow defining getters by frames in the same origin. 418 if (!allowsAccessFrom(exec)) 419 return; 420 421 // Don't allow shadowing location using defineGetter. 422 if (propertyName == "location") 423 return; 424 425 Base::defineGetter(exec, propertyName, getterFunction, attributes); 426} 427 428void JSDOMWindow::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes) 429{ 430 // Only allow defining setters by frames in the same origin. 431 if (!allowsAccessFrom(exec)) 432 return; 433 Base::defineSetter(exec, propertyName, setterFunction, attributes); 434} 435 436bool JSDOMWindow::defineOwnProperty(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::PropertyDescriptor& descriptor, bool shouldThrow) 437{ 438 // Only allow defining properties in this way by frames in the same origin, as it allows setters to be introduced. 439 if (!allowsAccessFrom(exec)) 440 return false; 441 return Base::defineOwnProperty(exec, propertyName, descriptor, shouldThrow); 442} 443 444JSValue JSDOMWindow::lookupGetter(ExecState* exec, const Identifier& propertyName) 445{ 446 // Only allow looking-up getters by frames in the same origin. 447 if (!allowsAccessFrom(exec)) 448 return jsUndefined(); 449 return Base::lookupGetter(exec, propertyName); 450} 451 452JSValue JSDOMWindow::lookupSetter(ExecState* exec, const Identifier& propertyName) 453{ 454 // Only allow looking-up setters by frames in the same origin. 455 if (!allowsAccessFrom(exec)) 456 return jsUndefined(); 457 return Base::lookupSetter(exec, propertyName); 458} 459 460// Custom Attributes 461 462JSValue JSDOMWindow::history(ExecState* exec) const 463{ 464 History* history = impl()->history(); 465 if (DOMObject* wrapper = getCachedDOMObjectWrapper(exec, history)) 466 return wrapper; 467 468 JSDOMWindow* window = const_cast<JSDOMWindow*>(this); 469 JSHistory* jsHistory = new (exec) JSHistory(getDOMStructure<JSHistory>(exec, window), window, history); 470 cacheDOMObjectWrapper(exec, history, jsHistory); 471 return jsHistory; 472} 473 474JSValue JSDOMWindow::location(ExecState* exec) const 475{ 476 Location* location = impl()->location(); 477 if (DOMObject* wrapper = getCachedDOMObjectWrapper(exec, location)) 478 return wrapper; 479 480 JSDOMWindow* window = const_cast<JSDOMWindow*>(this); 481 JSLocation* jsLocation = new (exec) JSLocation(getDOMStructure<JSLocation>(exec, window), window, location); 482 cacheDOMObjectWrapper(exec, location, jsLocation); 483 return jsLocation; 484} 485 486void JSDOMWindow::setLocation(ExecState* exec, JSValue value) 487{ 488 Frame* lexicalFrame = toLexicalFrame(exec); 489 if (!lexicalFrame) 490 return; 491 492#if ENABLE(DASHBOARD_SUPPORT) 493 // To avoid breaking old widgets, make "var location =" in a top-level frame create 494 // a property named "location" instead of performing a navigation (<rdar://problem/5688039>). 495 if (Settings* settings = lexicalFrame->settings()) { 496 if (settings->usesDashboardBackwardCompatibilityMode() && !lexicalFrame->tree()->parent()) { 497 if (allowsAccessFrom(exec)) 498 putDirect(Identifier(exec, "location"), value); 499 return; 500 } 501 } 502#endif 503 504 Frame* frame = impl()->frame(); 505 ASSERT(frame); 506 507 KURL url = completeURL(exec, value.toString(exec)); 508 if (url.isNull()) 509 return; 510 511 if (!shouldAllowNavigation(exec, frame)) 512 return; 513 514 if (!protocolIsJavaScript(url) || allowsAccessFrom(exec)) { 515 // We want a new history item if this JS was called via a user gesture 516 frame->redirectScheduler()->scheduleLocationChange(url, lexicalFrame->loader()->outgoingReferrer(), !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, processingUserGesture(exec)); 517 } 518} 519 520JSValue JSDOMWindow::crypto(ExecState*) const 521{ 522 return jsUndefined(); 523} 524 525JSValue JSDOMWindow::event(ExecState* exec) const 526{ 527 Event* event = currentEvent(); 528 if (!event) 529 return jsUndefined(); 530 return toJS(exec, event); 531} 532 533#if ENABLE(EVENTSOURCE) 534JSValue JSDOMWindow::eventSource(ExecState* exec) const 535{ 536 return getDOMConstructor<JSEventSourceConstructor>(exec, this); 537} 538#endif 539 540JSValue JSDOMWindow::image(ExecState* exec) const 541{ 542 return getDOMConstructor<JSImageConstructor>(exec, this); 543} 544 545JSValue JSDOMWindow::option(ExecState* exec) const 546{ 547 return getDOMConstructor<JSOptionConstructor>(exec, this); 548} 549 550#if ENABLE(VIDEO) 551JSValue JSDOMWindow::audio(ExecState* exec) const 552{ 553 if (!MediaPlayer::isAvailable()) 554 return jsUndefined(); 555 return getDOMConstructor<JSAudioConstructor>(exec, this); 556} 557#endif 558 559JSValue JSDOMWindow::webKitPoint(ExecState* exec) const 560{ 561 return getDOMConstructor<JSWebKitPointConstructor>(exec, this); 562} 563 564JSValue JSDOMWindow::webKitCSSMatrix(ExecState* exec) const 565{ 566 return getDOMConstructor<JSWebKitCSSMatrixConstructor>(exec, this); 567} 568 569#if ENABLE(3D_CANVAS) 570JSValue JSDOMWindow::webGLArrayBuffer(ExecState* exec) const 571{ 572 return getDOMConstructor<JSWebGLArrayBufferConstructor>(exec, this); 573} 574 575JSValue JSDOMWindow::webGLByteArray(ExecState* exec) const 576{ 577 return getDOMConstructor<JSWebGLByteArrayConstructor>(exec, this); 578} 579 580JSValue JSDOMWindow::webGLUnsignedByteArray(ExecState* exec) const 581{ 582 return getDOMConstructor<JSWebGLUnsignedByteArrayConstructor>(exec, this); 583} 584 585JSValue JSDOMWindow::webGLIntArray(ExecState* exec) const 586{ 587 return getDOMConstructor<JSWebGLIntArrayConstructor>(exec, this); 588} 589 590JSValue JSDOMWindow::webGLUnsignedIntArray(ExecState* exec) const 591{ 592 return getDOMConstructor<JSWebGLUnsignedIntArrayConstructor>(exec, this); 593} 594 595JSValue JSDOMWindow::webGLShortArray(ExecState* exec) const 596{ 597 return getDOMConstructor<JSWebGLShortArrayConstructor>(exec, this); 598} 599 600JSValue JSDOMWindow::webGLUnsignedShortArray(ExecState* exec) const 601{ 602 return getDOMConstructor<JSWebGLUnsignedShortArrayConstructor>(exec, this); 603} 604 605JSValue JSDOMWindow::webGLFloatArray(ExecState* exec) const 606{ 607 return getDOMConstructor<JSWebGLFloatArrayConstructor>(exec, this); 608} 609#endif 610 611JSValue JSDOMWindow::xmlHttpRequest(ExecState* exec) const 612{ 613 return getDOMConstructor<JSXMLHttpRequestConstructor>(exec, this); 614} 615 616#if ENABLE(XSLT) 617JSValue JSDOMWindow::xsltProcessor(ExecState* exec) const 618{ 619 return getDOMConstructor<JSXSLTProcessorConstructor>(exec, this); 620} 621#endif 622 623#if ENABLE(CHANNEL_MESSAGING) 624JSValue JSDOMWindow::messageChannel(ExecState* exec) const 625{ 626 return getDOMConstructor<JSMessageChannelConstructor>(exec, this); 627} 628#endif 629 630#if ENABLE(WORKERS) 631JSValue JSDOMWindow::worker(ExecState* exec) const 632{ 633 return getDOMConstructor<JSWorkerConstructor>(exec, this); 634} 635#endif 636 637#if ENABLE(SHARED_WORKERS) 638JSValue JSDOMWindow::sharedWorker(ExecState* exec) const 639{ 640 if (SharedWorkerRepository::isAvailable()) 641 return getDOMConstructor<JSSharedWorkerConstructor>(exec, this); 642 return jsUndefined(); 643} 644#endif 645 646#if ENABLE(WEB_SOCKETS) 647JSValue JSDOMWindow::webSocket(ExecState* exec) const 648{ 649 Frame* frame = impl()->frame(); 650 if (!frame) 651 return jsUndefined(); 652 Settings* settings = frame->settings(); 653 if (!settings) 654 return jsUndefined(); 655 return getDOMConstructor<JSWebSocketConstructor>(exec, this); 656} 657#endif 658 659// Custom functions 660 661// Helper for window.open() and window.showModalDialog() 662static Frame* createWindow(ExecState* exec, Frame* lexicalFrame, Frame* dynamicFrame, 663 Frame* openerFrame, const String& url, const String& frameName, 664 const WindowFeatures& windowFeatures, JSValue dialogArgs) 665{ 666 ASSERT(lexicalFrame); 667 ASSERT(dynamicFrame); 668 669 if (Document* lexicalDocument = lexicalFrame->document()) { 670 // Sandboxed iframes cannot open new auxiliary browsing contexts. 671 if (lexicalDocument->securityOrigin()->isSandboxed(SandboxNavigation)) 672 return 0; 673 } 674 675 ResourceRequest request; 676 677 // For whatever reason, Firefox uses the dynamicGlobalObject to determine 678 // the outgoingReferrer. We replicate that behavior here. 679 String referrer = dynamicFrame->loader()->outgoingReferrer(); 680 request.setHTTPReferrer(referrer); 681 FrameLoader::addHTTPOriginIfNeeded(request, dynamicFrame->loader()->outgoingOrigin()); 682 FrameLoadRequest frameRequest(request, frameName); 683 684 // FIXME: It's much better for client API if a new window starts with a URL, here where we 685 // know what URL we are going to open. Unfortunately, this code passes the empty string 686 // for the URL, but there's a reason for that. Before loading we have to set up the opener, 687 // openedByDOM, and dialogArguments values. Also, to decide whether to use the URL we currently 688 // do an allowsAccessFrom call using the window we create, which can't be done before creating it. 689 // We'd have to resolve all those issues to pass the URL instead of "". 690 691 bool created; 692 // We pass in the opener frame here so it can be used for looking up the frame name, in case the active frame 693 // is different from the opener frame, and the name references a frame relative to the opener frame, for example 694 // "_self" or "_parent". 695 Frame* newFrame = lexicalFrame->loader()->createWindow(openerFrame->loader(), frameRequest, windowFeatures, created); 696 if (!newFrame) 697 return 0; 698 699 newFrame->loader()->setOpener(openerFrame); 700 newFrame->page()->setOpenedByDOM(); 701 702 // FIXME: If a window is created from an isolated world, what are the consequences of this? 'dialogArguments' only appears back in the normal world? 703 JSDOMWindow* newWindow = toJSDOMWindow(newFrame, normalWorld(exec->globalData())); 704 705 if (dialogArgs) 706 newWindow->putDirect(Identifier(exec, "dialogArguments"), dialogArgs); 707 708 if (!protocolIsJavaScript(url) || newWindow->allowsAccessFrom(exec)) { 709 KURL completedURL = url.isEmpty() ? KURL(ParsedURLString, "") : completeURL(exec, url); 710 bool userGesture = processingUserGesture(exec); 711 712 if (created) 713 newFrame->loader()->changeLocation(completedURL, referrer, false, false, userGesture); 714 else if (!url.isEmpty()) 715 newFrame->redirectScheduler()->scheduleLocationChange(completedURL.string(), referrer, !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, userGesture); 716 } 717 718 return newFrame; 719} 720 721static bool domWindowAllowPopUp(Frame* activeFrame, ExecState* exec) 722{ 723 ASSERT(activeFrame); 724 if (activeFrame->script()->processingUserGesture(currentWorld(exec))) 725 return true; 726 return DOMWindow::allowPopUp(activeFrame); 727} 728 729JSValue JSDOMWindow::open(ExecState* exec, const ArgList& args) 730{ 731 String urlString = valueToStringWithUndefinedOrNullCheck(exec, args.at(0)); 732 AtomicString frameName = args.at(1).isUndefinedOrNull() ? "_blank" : AtomicString(args.at(1).toString(exec)); 733 WindowFeatures windowFeatures(valueToStringWithUndefinedOrNullCheck(exec, args.at(2))); 734 735 Frame* frame = impl()->frame(); 736 if (!frame) 737 return jsUndefined(); 738 Frame* lexicalFrame = toLexicalFrame(exec); 739 if (!lexicalFrame) 740 return jsUndefined(); 741 Frame* dynamicFrame = toDynamicFrame(exec); 742 if (!dynamicFrame) 743 return jsUndefined(); 744 745 Page* page = frame->page(); 746 747 // Because FrameTree::find() returns true for empty strings, we must check for empty framenames. 748 // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker. 749 if (!domWindowAllowPopUp(dynamicFrame, exec) && (frameName.isEmpty() || !frame->tree()->find(frameName))) 750 return jsUndefined(); 751 752 // Get the target frame for the special cases of _top and _parent. In those 753 // cases, we can schedule a location change right now and return early. 754 bool topOrParent = false; 755 if (frameName == "_top") { 756 frame = frame->tree()->top(); 757 topOrParent = true; 758 } else if (frameName == "_parent") { 759 if (Frame* parent = frame->tree()->parent()) 760 frame = parent; 761 topOrParent = true; 762 } 763 if (topOrParent) { 764 String completedURL; 765 if (!urlString.isEmpty()) 766 completedURL = completeURL(exec, urlString).string(); 767 768 if (!shouldAllowNavigation(exec, frame)) 769 return jsUndefined(); 770 771 const JSDOMWindow* targetedWindow = toJSDOMWindow(frame, currentWorld(exec)); 772 if (!completedURL.isEmpty() && (!protocolIsJavaScript(completedURL) || (targetedWindow && targetedWindow->allowsAccessFrom(exec)))) { 773 bool userGesture = processingUserGesture(exec); 774 775 // For whatever reason, Firefox uses the dynamicGlobalObject to 776 // determine the outgoingReferrer. We replicate that behavior 777 // here. 778 String referrer = dynamicFrame->loader()->outgoingReferrer(); 779 780 frame->redirectScheduler()->scheduleLocationChange(completedURL, referrer, !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, userGesture); 781 } 782 return toJS(exec, frame->domWindow()); 783 } 784 785 // In the case of a named frame or a new window, we'll use the createWindow() helper 786 FloatRect windowRect(windowFeatures.xSet ? windowFeatures.x : 0, windowFeatures.ySet ? windowFeatures.y : 0, 787 windowFeatures.widthSet ? windowFeatures.width : 0, windowFeatures.heightSet ? windowFeatures.height : 0); 788 DOMWindow::adjustWindowRect(screenAvailableRect(page ? page->mainFrame()->view() : 0), windowRect, windowRect); 789 790 windowFeatures.x = windowRect.x(); 791 windowFeatures.y = windowRect.y(); 792 windowFeatures.height = windowRect.height(); 793 windowFeatures.width = windowRect.width(); 794 795 frame = createWindow(exec, lexicalFrame, dynamicFrame, frame, urlString, frameName, windowFeatures, JSValue()); 796 797 if (!frame) 798 return jsUndefined(); 799 800 return toJS(exec, frame->domWindow()); 801} 802 803JSValue JSDOMWindow::showModalDialog(ExecState* exec, const ArgList& args) 804{ 805 String url = valueToStringWithUndefinedOrNullCheck(exec, args.at(0)); 806 JSValue dialogArgs = args.at(1); 807 String featureArgs = valueToStringWithUndefinedOrNullCheck(exec, args.at(2)); 808 809 Frame* frame = impl()->frame(); 810 if (!frame) 811 return jsUndefined(); 812 Frame* lexicalFrame = toLexicalFrame(exec); 813 if (!lexicalFrame) 814 return jsUndefined(); 815 Frame* dynamicFrame = toDynamicFrame(exec); 816 if (!dynamicFrame) 817 return jsUndefined(); 818 819 if (!DOMWindow::canShowModalDialogNow(frame) || !domWindowAllowPopUp(dynamicFrame, exec)) 820 return jsUndefined(); 821 822 HashMap<String, String> features; 823 DOMWindow::parseModalDialogFeatures(featureArgs, features); 824 825 const bool trusted = false; 826 827 // The following features from Microsoft's documentation are not implemented: 828 // - default font settings 829 // - width, height, left, and top specified in units other than "px" 830 // - edge (sunken or raised, default is raised) 831 // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print 832 // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?) 833 // - unadorned: trusted && boolFeature(features, "unadorned"); 834 835 FloatRect screenRect = screenAvailableRect(frame->view()); 836 837 WindowFeatures wargs; 838 wargs.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE 839 wargs.widthSet = true; 840 wargs.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE 841 wargs.heightSet = true; 842 843 wargs.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1); 844 wargs.xSet = wargs.x > 0; 845 wargs.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1); 846 wargs.ySet = wargs.y > 0; 847 848 if (WindowFeatures::boolFeature(features, "center", true)) { 849 if (!wargs.xSet) { 850 wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2; 851 wargs.xSet = true; 852 } 853 if (!wargs.ySet) { 854 wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2; 855 wargs.ySet = true; 856 } 857 } 858 859 wargs.dialog = true; 860 wargs.resizable = WindowFeatures::boolFeature(features, "resizable"); 861 wargs.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true); 862 wargs.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted); 863 wargs.menuBarVisible = false; 864 wargs.toolBarVisible = false; 865 wargs.locationBarVisible = false; 866 wargs.fullscreen = false; 867 868 Frame* dialogFrame = createWindow(exec, lexicalFrame, dynamicFrame, frame, url, "", wargs, dialogArgs); 869 if (!dialogFrame) 870 return jsUndefined(); 871 872 JSDOMWindow* dialogWindow = toJSDOMWindow(dialogFrame, currentWorld(exec)); 873 dialogFrame->page()->chrome()->runModal(); 874 875 Identifier returnValue(exec, "returnValue"); 876 if (dialogWindow->allowsAccessFromNoErrorMessage(exec)) { 877 PropertySlot slot; 878 // This is safe, we have already performed the origin security check and we are 879 // not interested in any of the DOM properties of the window. 880 if (dialogWindow->JSGlobalObject::getOwnPropertySlot(exec, returnValue, slot)) 881 return slot.getValue(exec, returnValue); 882 } 883 return jsUndefined(); 884} 885 886JSValue JSDOMWindow::postMessage(ExecState* exec, const ArgList& args) 887{ 888 DOMWindow* window = impl(); 889 890 DOMWindow* source = asJSDOMWindow(exec->lexicalGlobalObject())->impl(); 891 PassRefPtr<SerializedScriptValue> message = SerializedScriptValue::create(exec, args.at(0)); 892 893 if (exec->hadException()) 894 return jsUndefined(); 895 896 MessagePortArray messagePorts; 897 if (args.size() > 2) 898 fillMessagePortArray(exec, args.at(1), messagePorts); 899 if (exec->hadException()) 900 return jsUndefined(); 901 902 String targetOrigin = valueToStringWithUndefinedOrNullCheck(exec, args.at((args.size() == 2) ? 1 : 2)); 903 if (exec->hadException()) 904 return jsUndefined(); 905 906 ExceptionCode ec = 0; 907 window->postMessage(message, &messagePorts, targetOrigin, source, ec); 908 setDOMException(exec, ec); 909 910 return jsUndefined(); 911} 912 913JSValue JSDOMWindow::setTimeout(ExecState* exec, const ArgList& args) 914{ 915 ScheduledAction* action = ScheduledAction::create(exec, args, currentWorld(exec)); 916 if (exec->hadException()) 917 return jsUndefined(); 918 int delay = args.at(1).toInt32(exec); 919 920 ExceptionCode ec = 0; 921 int result = impl()->setTimeout(action, delay, ec); 922 setDOMException(exec, ec); 923 924 return jsNumber(exec, result); 925} 926 927JSValue JSDOMWindow::setInterval(ExecState* exec, const ArgList& args) 928{ 929 ScheduledAction* action = ScheduledAction::create(exec, args, currentWorld(exec)); 930 if (exec->hadException()) 931 return jsUndefined(); 932 int delay = args.at(1).toInt32(exec); 933 934 ExceptionCode ec = 0; 935 int result = impl()->setInterval(action, delay, ec); 936 setDOMException(exec, ec); 937 938 return jsNumber(exec, result); 939} 940 941JSValue JSDOMWindow::atob(ExecState* exec, const ArgList& args) 942{ 943 if (args.size() < 1) 944 return throwError(exec, SyntaxError, "Not enough arguments"); 945 946 JSValue v = args.at(0); 947 if (v.isNull()) 948 return jsEmptyString(exec); 949 950 UString s = v.toString(exec); 951 if (!s.is8Bit()) { 952 setDOMException(exec, INVALID_CHARACTER_ERR); 953 return jsUndefined(); 954 } 955 956 Vector<char> in(s.size()); 957 for (int i = 0; i < s.size(); ++i) 958 in[i] = static_cast<char>(s.data()[i]); 959 Vector<char> out; 960 961 if (!base64Decode(in, out)) 962 return throwError(exec, GeneralError, "Cannot decode base64"); 963 964 return jsString(exec, String(out.data(), out.size())); 965} 966 967JSValue JSDOMWindow::btoa(ExecState* exec, const ArgList& args) 968{ 969 if (args.size() < 1) 970 return throwError(exec, SyntaxError, "Not enough arguments"); 971 972 JSValue v = args.at(0); 973 if (v.isNull()) 974 return jsEmptyString(exec); 975 976 UString s = v.toString(exec); 977 if (!s.is8Bit()) { 978 setDOMException(exec, INVALID_CHARACTER_ERR); 979 return jsUndefined(); 980 } 981 982 Vector<char> in(s.size()); 983 for (int i = 0; i < s.size(); ++i) 984 in[i] = static_cast<char>(s.data()[i]); 985 Vector<char> out; 986 987 base64Encode(in, out); 988 989 return jsString(exec, String(out.data(), out.size())); 990} 991 992JSValue JSDOMWindow::addEventListener(ExecState* exec, const ArgList& args) 993{ 994 Frame* frame = impl()->frame(); 995 if (!frame) 996 return jsUndefined(); 997 998 JSValue listener = args.at(1); 999 if (!listener.isObject()) 1000 return jsUndefined(); 1001 1002 impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec)); 1003 return jsUndefined(); 1004} 1005 1006JSValue JSDOMWindow::removeEventListener(ExecState* exec, const ArgList& args) 1007{ 1008 Frame* frame = impl()->frame(); 1009 if (!frame) 1010 return jsUndefined(); 1011 1012 JSValue listener = args.at(1); 1013 if (!listener.isObject()) 1014 return jsUndefined(); 1015 1016 impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec)); 1017 return jsUndefined(); 1018} 1019 1020DOMWindow* toDOMWindow(JSValue value) 1021{ 1022 if (!value.isObject()) 1023 return 0; 1024 JSObject* object = asObject(value); 1025 if (object->inherits(&JSDOMWindow::s_info)) 1026 return static_cast<JSDOMWindow*>(object)->impl(); 1027 if (object->inherits(&JSDOMWindowShell::s_info)) 1028 return static_cast<JSDOMWindowShell*>(object)->impl(); 1029 return 0; 1030} 1031 1032} // namespace WebCore 1033