1 /* 2 * Copyright (C) 2004, 2006, 2007, 2008 Apple 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 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #import "config.h" 27 #import "WebScriptObjectPrivate.h" 28 29 #import "BridgeJSC.h" 30 #import "Console.h" 31 #import "DOMInternal.h" 32 #import "DOMWindow.h" 33 #import "Frame.h" 34 #import "JSDOMWindow.h" 35 #import "JSDOMWindowCustom.h" 36 #import "JSHTMLElement.h" 37 #import "JSMainThreadExecState.h" 38 #import "JSPluginElementFunctions.h" 39 #import "ObjCRuntimeObject.h" 40 #import "PlatformString.h" 41 #import "StringSourceProvider.h" 42 #import "WebCoreObjCExtras.h" 43 #import "objc_instance.h" 44 #import "runtime_object.h" 45 #import "runtime_root.h" 46 #import <JavaScriptCore/APICast.h> 47 #import <interpreter/CallFrame.h> 48 #import <runtime/InitializeThreading.h> 49 #import <runtime/JSGlobalObject.h> 50 #import <runtime/JSLock.h> 51 #import <runtime/Completion.h> 52 #import <runtime/Completion.h> 53 #import <wtf/Threading.h> 54 55 #ifdef BUILDING_ON_TIGER 56 typedef unsigned NSUInteger; 57 #endif 58 59 using namespace JSC; 60 using namespace JSC::Bindings; 61 using namespace WebCore; 62 63 namespace WebCore { 64 65 static NSMapTable* JSWrapperCache; 66 67 NSObject* getJSWrapper(JSObject* impl) 68 { 69 if (!JSWrapperCache) 70 return nil; 71 return static_cast<NSObject*>(NSMapGet(JSWrapperCache, impl)); 72 } 73 74 void addJSWrapper(NSObject* wrapper, JSObject* impl) 75 { 76 if (!JSWrapperCache) 77 JSWrapperCache = createWrapperCache(); 78 NSMapInsert(JSWrapperCache, impl, wrapper); 79 } 80 81 void removeJSWrapper(JSObject* impl) 82 { 83 if (!JSWrapperCache) 84 return; 85 NSMapRemove(JSWrapperCache, impl); 86 } 87 88 id createJSWrapper(JSC::JSObject* object, PassRefPtr<JSC::Bindings::RootObject> origin, PassRefPtr<JSC::Bindings::RootObject> root) 89 { 90 if (id wrapper = getJSWrapper(object)) 91 return [[wrapper retain] autorelease]; 92 return [[[WebScriptObject alloc] _initWithJSObject:object originRootObject:origin rootObject:root] autorelease]; 93 } 94 95 static void addExceptionToConsole(ExecState* exec) 96 { 97 JSDOMWindow* window = asJSDOMWindow(exec->dynamicGlobalObject()); 98 if (!window || !exec->hadException()) 99 return; 100 reportCurrentException(exec); 101 } 102 103 } // namespace WebCore 104 105 @implementation WebScriptObjectPrivate 106 107 @end 108 109 @implementation WebScriptObject 110 111 + (void)initialize 112 { 113 JSC::initializeThreading(); 114 WTF::initializeMainThreadToProcessMainThread(); 115 #ifndef BUILDING_ON_TIGER 116 WebCoreObjCFinalizeOnMainThread(self); 117 #endif 118 } 119 120 + (id)scriptObjectForJSObject:(JSObjectRef)jsObject originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject 121 { 122 if (id domWrapper = createDOMWrapper(toJS(jsObject), originRootObject, rootObject)) 123 return domWrapper; 124 125 return WebCore::createJSWrapper(toJS(jsObject), originRootObject, rootObject); 126 } 127 128 static void _didExecute(WebScriptObject *obj) 129 { 130 ASSERT(JSLock::lockCount() > 0); 131 132 RootObject* root = [obj _rootObject]; 133 if (!root) 134 return; 135 136 ExecState* exec = root->globalObject()->globalExec(); 137 KJSDidExecuteFunctionPtr func = Instance::didExecuteFunction(); 138 if (func) 139 func(exec, root->globalObject()); 140 } 141 142 - (void)_setImp:(JSObject*)imp originRootObject:(PassRefPtr<RootObject>)originRootObject rootObject:(PassRefPtr<RootObject>)rootObject 143 { 144 // This function should only be called once, as a (possibly lazy) initializer. 145 ASSERT(!_private->imp); 146 ASSERT(!_private->rootObject); 147 ASSERT(!_private->originRootObject); 148 ASSERT(imp); 149 150 _private->imp = imp; 151 _private->rootObject = rootObject.releaseRef(); 152 _private->originRootObject = originRootObject.releaseRef(); 153 154 WebCore::addJSWrapper(self, imp); 155 156 if (_private->rootObject) 157 _private->rootObject->gcProtect(imp); 158 } 159 160 - (void)_setOriginRootObject:(PassRefPtr<RootObject>)originRootObject andRootObject:(PassRefPtr<RootObject>)rootObject 161 { 162 ASSERT(_private->imp); 163 164 if (rootObject) 165 rootObject->gcProtect(_private->imp); 166 167 if (_private->rootObject && _private->rootObject->isValid()) 168 _private->rootObject->gcUnprotect(_private->imp); 169 170 if (_private->rootObject) 171 _private->rootObject->deref(); 172 173 if (_private->originRootObject) 174 _private->originRootObject->deref(); 175 176 _private->rootObject = rootObject.releaseRef(); 177 _private->originRootObject = originRootObject.releaseRef(); 178 } 179 180 - (id)_initWithJSObject:(JSC::JSObject*)imp originRootObject:(PassRefPtr<JSC::Bindings::RootObject>)originRootObject rootObject:(PassRefPtr<JSC::Bindings::RootObject>)rootObject 181 { 182 ASSERT(imp); 183 184 self = [super init]; 185 _private = [[WebScriptObjectPrivate alloc] init]; 186 [self _setImp:imp originRootObject:originRootObject rootObject:rootObject]; 187 188 return self; 189 } 190 191 - (JSObject*)_imp 192 { 193 // Associate the WebScriptObject with the JS wrapper for the ObjC DOM wrapper. 194 // This is done on lazily, on demand. 195 if (!_private->imp && _private->isCreatedByDOMWrapper) 196 [self _initializeScriptDOMNodeImp]; 197 return [self _rootObject] ? _private->imp : 0; 198 } 199 200 - (BOOL)_hasImp 201 { 202 return _private->imp != nil; 203 } 204 205 // Node that DOMNode overrides this method. So you should almost always 206 // use this method call instead of _private->rootObject directly. 207 - (RootObject*)_rootObject 208 { 209 return _private->rootObject && _private->rootObject->isValid() ? _private->rootObject : 0; 210 } 211 212 - (RootObject *)_originRootObject 213 { 214 return _private->originRootObject && _private->originRootObject->isValid() ? _private->originRootObject : 0; 215 } 216 217 - (BOOL)_isSafeScript 218 { 219 RootObject *root = [self _rootObject]; 220 if (!root) 221 return false; 222 223 if (!_private->originRootObject) 224 return true; 225 226 if (!_private->originRootObject->isValid()) 227 return false; 228 229 return root->globalObject()->allowsAccessFrom(_private->originRootObject->globalObject()); 230 } 231 232 - (void)dealloc 233 { 234 if (WebCoreObjCScheduleDeallocateOnMainThread([WebScriptObject class], self)) 235 return; 236 237 if (_private->imp) 238 WebCore::removeJSWrapper(_private->imp); 239 240 if (_private->rootObject && _private->rootObject->isValid()) 241 _private->rootObject->gcUnprotect(_private->imp); 242 243 if (_private->rootObject) 244 _private->rootObject->deref(); 245 246 if (_private->originRootObject) 247 _private->originRootObject->deref(); 248 249 [_private release]; 250 251 [super dealloc]; 252 } 253 254 - (void)finalize 255 { 256 if (_private->rootObject && _private->rootObject->isValid()) 257 _private->rootObject->gcUnprotect(_private->imp); 258 259 if (_private->rootObject) 260 _private->rootObject->deref(); 261 262 if (_private->originRootObject) 263 _private->originRootObject->deref(); 264 265 [super finalize]; 266 } 267 268 + (BOOL)throwException:(NSString *)exceptionMessage 269 { 270 ObjcInstance::setGlobalException(exceptionMessage); 271 return YES; 272 } 273 274 static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* rootObject, MarkedArgumentBuffer& aList) 275 { 276 int i, numObjects = array ? [array count] : 0; 277 278 for (i = 0; i < numObjects; i++) { 279 id anObject = [array objectAtIndex:i]; 280 aList.append(convertObjcValueToValue(exec, &anObject, ObjcObjectType, rootObject)); 281 } 282 } 283 284 - (id)callWebScriptMethod:(NSString *)name withArguments:(NSArray *)args 285 { 286 if (![self _isSafeScript]) 287 return nil; 288 289 JSLock lock(SilenceAssertionsOnly); 290 291 // Look up the function object. 292 ExecState* exec = [self _rootObject]->globalObject()->globalExec(); 293 ASSERT(!exec->hadException()); 294 295 JSValue function = [self _imp]->get(exec, Identifier(exec, stringToUString(String(name)))); 296 CallData callData; 297 CallType callType = getCallData(function, callData); 298 if (callType == CallTypeNone) 299 return nil; 300 301 MarkedArgumentBuffer argList; 302 getListFromNSArray(exec, args, [self _rootObject], argList); 303 304 if (![self _isSafeScript]) 305 return nil; 306 307 [self _rootObject]->globalObject()->globalData().timeoutChecker.start(); 308 JSValue result = JSMainThreadExecState::call(exec, function, callType, callData, [self _imp], argList); 309 [self _rootObject]->globalObject()->globalData().timeoutChecker.stop(); 310 311 if (exec->hadException()) { 312 addExceptionToConsole(exec); 313 result = jsUndefined(); 314 exec->clearException(); 315 } 316 317 // Convert and return the result of the function call. 318 id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]]; 319 320 _didExecute(self); 321 322 return resultObj; 323 } 324 325 - (id)evaluateWebScript:(NSString *)script 326 { 327 if (![self _isSafeScript]) 328 return nil; 329 330 ExecState* exec = [self _rootObject]->globalObject()->globalExec(); 331 ASSERT(!exec->hadException()); 332 333 JSValue result; 334 JSLock lock(SilenceAssertionsOnly); 335 336 [self _rootObject]->globalObject()->globalData().timeoutChecker.start(); 337 Completion completion = JSMainThreadExecState::evaluate([self _rootObject]->globalObject()->globalExec(), [self _rootObject]->globalObject()->globalScopeChain(), makeSource(String(script)), JSC::JSValue()); 338 [self _rootObject]->globalObject()->globalData().timeoutChecker.stop(); 339 ComplType type = completion.complType(); 340 341 if (type == Normal) { 342 result = completion.value(); 343 if (!result) 344 result = jsUndefined(); 345 } else 346 result = jsUndefined(); 347 348 if (exec->hadException()) { 349 addExceptionToConsole(exec); 350 result = jsUndefined(); 351 exec->clearException(); 352 } 353 354 id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]]; 355 356 _didExecute(self); 357 358 return resultObj; 359 } 360 361 - (void)setValue:(id)value forKey:(NSString *)key 362 { 363 if (![self _isSafeScript]) 364 return; 365 366 ExecState* exec = [self _rootObject]->globalObject()->globalExec(); 367 ASSERT(!exec->hadException()); 368 369 JSLock lock(SilenceAssertionsOnly); 370 371 PutPropertySlot slot; 372 [self _imp]->put(exec, Identifier(exec, stringToUString(String(key))), convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]), slot); 373 374 if (exec->hadException()) { 375 addExceptionToConsole(exec); 376 exec->clearException(); 377 } 378 379 _didExecute(self); 380 } 381 382 - (id)valueForKey:(NSString *)key 383 { 384 if (![self _isSafeScript]) 385 return nil; 386 387 ExecState* exec = [self _rootObject]->globalObject()->globalExec(); 388 ASSERT(!exec->hadException()); 389 390 id resultObj; 391 { 392 // Need to scope this lock to ensure that we release the lock before calling 393 // [super valueForKey:key] which might throw an exception and bypass the JSLock destructor, 394 // leaving the lock permanently held 395 JSLock lock(SilenceAssertionsOnly); 396 397 JSValue result = [self _imp]->get(exec, Identifier(exec, stringToUString(String(key)))); 398 399 if (exec->hadException()) { 400 addExceptionToConsole(exec); 401 result = jsUndefined(); 402 exec->clearException(); 403 } 404 405 resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]]; 406 } 407 408 if ([resultObj isKindOfClass:[WebUndefined class]]) 409 resultObj = [super valueForKey:key]; // defaults to throwing an exception 410 411 JSLock lock(SilenceAssertionsOnly); 412 _didExecute(self); 413 414 return resultObj; 415 } 416 417 - (void)removeWebScriptKey:(NSString *)key 418 { 419 if (![self _isSafeScript]) 420 return; 421 422 ExecState* exec = [self _rootObject]->globalObject()->globalExec(); 423 ASSERT(!exec->hadException()); 424 425 JSLock lock(SilenceAssertionsOnly); 426 [self _imp]->deleteProperty(exec, Identifier(exec, stringToUString(String(key)))); 427 428 if (exec->hadException()) { 429 addExceptionToConsole(exec); 430 exec->clearException(); 431 } 432 433 _didExecute(self); 434 } 435 436 - (BOOL)hasWebScriptKey:(NSString *)key 437 { 438 if (![self _isSafeScript]) 439 return NO; 440 441 ExecState* exec = [self _rootObject]->globalObject()->globalExec(); 442 ASSERT(!exec->hadException()); 443 444 JSLock lock(SilenceAssertionsOnly); 445 BOOL result = [self _imp]->hasProperty(exec, Identifier(exec, stringToUString(String(key)))); 446 447 if (exec->hadException()) { 448 addExceptionToConsole(exec); 449 exec->clearException(); 450 } 451 452 _didExecute(self); 453 454 return result; 455 } 456 457 - (NSString *)stringRepresentation 458 { 459 if (![self _isSafeScript]) { 460 // This is a workaround for a gcc 3.3 internal compiler error. 461 return @"Undefined"; 462 } 463 464 JSLock lock(SilenceAssertionsOnly); 465 ExecState* exec = [self _rootObject]->globalObject()->globalExec(); 466 467 id result = convertValueToObjcValue(exec, [self _imp], ObjcObjectType).objectValue; 468 469 NSString *description = [result description]; 470 471 _didExecute(self); 472 473 return description; 474 } 475 476 - (id)webScriptValueAtIndex:(unsigned)index 477 { 478 if (![self _isSafeScript]) 479 return nil; 480 481 ExecState* exec = [self _rootObject]->globalObject()->globalExec(); 482 ASSERT(!exec->hadException()); 483 484 JSLock lock(SilenceAssertionsOnly); 485 JSValue result = [self _imp]->get(exec, index); 486 487 if (exec->hadException()) { 488 addExceptionToConsole(exec); 489 result = jsUndefined(); 490 exec->clearException(); 491 } 492 493 id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]]; 494 495 _didExecute(self); 496 497 return resultObj; 498 } 499 500 - (void)setWebScriptValueAtIndex:(unsigned)index value:(id)value 501 { 502 if (![self _isSafeScript]) 503 return; 504 505 ExecState* exec = [self _rootObject]->globalObject()->globalExec(); 506 ASSERT(!exec->hadException()); 507 508 JSLock lock(SilenceAssertionsOnly); 509 [self _imp]->put(exec, index, convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject])); 510 511 if (exec->hadException()) { 512 addExceptionToConsole(exec); 513 exec->clearException(); 514 } 515 516 _didExecute(self); 517 } 518 519 - (void)setException:(NSString *)description 520 { 521 if (![self _rootObject]) 522 return; 523 ObjcInstance::setGlobalException(description, [self _rootObject]->globalObject()); 524 } 525 526 - (JSObjectRef)JSObject 527 { 528 if (![self _isSafeScript]) 529 return NULL; 530 531 return toRef([self _imp]); 532 } 533 534 + (id)_convertValueToObjcValue:(JSValue)value originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject 535 { 536 if (value.isObject()) { 537 JSObject* object = asObject(value); 538 JSLock lock(SilenceAssertionsOnly); 539 540 if (object->inherits(&JSHTMLElement::s_info)) { 541 // Plugin elements cache the instance internally. 542 HTMLElement* el = static_cast<JSHTMLElement*>(object)->impl(); 543 ObjcInstance* instance = static_cast<ObjcInstance*>(pluginInstance(el)); 544 if (instance) 545 return instance->getObject(); 546 } else if (object->inherits(&ObjCRuntimeObject::s_info)) { 547 ObjCRuntimeObject* runtimeObject = static_cast<ObjCRuntimeObject*>(object); 548 ObjcInstance* instance = runtimeObject->getInternalObjCInstance(); 549 if (instance) 550 return instance->getObject(); 551 return nil; 552 } 553 554 return [WebScriptObject scriptObjectForJSObject:toRef(object) originRootObject:originRootObject rootObject:rootObject]; 555 } 556 557 if (value.isString()) { 558 ExecState* exec = rootObject->globalObject()->globalExec(); 559 const UString& u = asString(value)->value(exec); 560 return [NSString stringWithCharacters:u.characters() length:u.length()]; 561 } 562 563 if (value.isNumber()) 564 return [NSNumber numberWithDouble:value.uncheckedGetNumber()]; 565 566 if (value.isBoolean()) 567 return [NSNumber numberWithBool:value.getBoolean()]; 568 569 if (value.isUndefined()) 570 return [WebUndefined undefined]; 571 572 // jsNull is not returned as NSNull because existing applications do not expect 573 // that return value. Return as nil for compatibility. <rdar://problem/4651318> <rdar://problem/4701626> 574 // Other types (e.g., UnspecifiedType) also return as nil. 575 return nil; 576 } 577 578 @end 579 580 @interface WebScriptObject (WebKitCocoaBindings) 581 582 - (id)objectAtIndex:(unsigned)index; 583 584 @end 585 586 @implementation WebScriptObject (WebKitCocoaBindings) 587 588 #if 0 589 590 // FIXME: We'd like to add this, but we can't do that until this issue is resolved: 591 // http://bugs.webkit.org/show_bug.cgi?id=13129: presence of 'count' method on 592 // WebScriptObject breaks Democracy player. 593 594 - (unsigned)count 595 { 596 id length = [self valueForKey:@"length"]; 597 if (![length respondsToSelector:@selector(intValue)]) 598 return 0; 599 return [length intValue]; 600 } 601 602 #endif 603 604 - (id)objectAtIndex:(unsigned)index 605 { 606 return [self webScriptValueAtIndex:index]; 607 } 608 609 @end 610 611 @implementation WebUndefined 612 613 + (id)allocWithZone:(NSZone *)unusedZone 614 { 615 UNUSED_PARAM(unusedZone); 616 617 static WebUndefined *sharedUndefined = 0; 618 if (!sharedUndefined) 619 sharedUndefined = [super allocWithZone:NULL]; 620 return sharedUndefined; 621 } 622 623 - (NSString *)description 624 { 625 return @"undefined"; 626 } 627 628 - (id)initWithCoder:(NSCoder *)unusedCoder 629 { 630 UNUSED_PARAM(unusedCoder); 631 632 return self; 633 } 634 635 - (void)encodeWithCoder:(NSCoder *)unusedCoder 636 { 637 UNUSED_PARAM(unusedCoder); 638 } 639 640 - (id)copyWithZone:(NSZone *)unusedZone 641 { 642 UNUSED_PARAM(unusedZone); 643 644 return self; 645 } 646 647 - (id)retain 648 { 649 return self; 650 } 651 652 - (oneway void)release 653 { 654 } 655 656 - (NSUInteger)retainCount 657 { 658 return NSUIntegerMax; 659 } 660 661 - (id)autorelease 662 { 663 return self; 664 } 665 666 - (void)dealloc 667 { 668 ASSERT(false); 669 return; 670 [super dealloc]; // make -Wdealloc-check happy 671 } 672 673 + (WebUndefined *)undefined 674 { 675 return [WebUndefined allocWithZone:NULL]; 676 } 677 678 @end 679