OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 "use strict"; | 5 "use strict"; |
6 | 6 |
7 // Overview: | 7 // Overview: |
8 // | 8 // |
9 // This file contains all of the routing and accounting for Object.observe. | 9 // This file contains all of the routing and accounting for Object.observe. |
10 // User code will interact with these mechanisms via the Object.observe APIs | 10 // User code will interact with these mechanisms via the Object.observe APIs |
(...skipping 337 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 callbackInfo = new InternalArray; | 348 callbackInfo = new InternalArray; |
349 callbackInfo.priority = priority; | 349 callbackInfo.priority = priority; |
350 GetCallbackInfoMap().set(callback, callbackInfo); | 350 GetCallbackInfoMap().set(callback, callbackInfo); |
351 } | 351 } |
352 return callbackInfo; | 352 return callbackInfo; |
353 } | 353 } |
354 | 354 |
355 function ObjectObserve(object, callback, acceptList) { | 355 function ObjectObserve(object, callback, acceptList) { |
356 if (!IS_SPEC_OBJECT(object)) | 356 if (!IS_SPEC_OBJECT(object)) |
357 throw MakeTypeError("observe_non_object", ["observe"]); | 357 throw MakeTypeError("observe_non_object", ["observe"]); |
| 358 if (%IsJSGlobalProxy(object)) |
| 359 throw MakeTypeError("observe_global_proxy", ["observe"]); |
358 if (!IS_SPEC_FUNCTION(callback)) | 360 if (!IS_SPEC_FUNCTION(callback)) |
359 throw MakeTypeError("observe_non_function", ["observe"]); | 361 throw MakeTypeError("observe_non_function", ["observe"]); |
360 if (ObjectIsFrozen(callback)) | 362 if (ObjectIsFrozen(callback)) |
361 throw MakeTypeError("observe_callback_frozen"); | 363 throw MakeTypeError("observe_callback_frozen"); |
362 if (!AcceptArgIsValid(acceptList)) | 364 if (!AcceptArgIsValid(acceptList)) |
363 throw MakeTypeError("observe_accept_invalid"); | 365 throw MakeTypeError("observe_accept_invalid"); |
364 | 366 |
365 var objectInfo = ObjectInfoGetOrCreate(object); | 367 var objectInfo = ObjectInfoGetOrCreate(object); |
366 ObjectInfoAddObserver(objectInfo, callback, acceptList); | 368 ObjectInfoAddObserver(objectInfo, callback, acceptList); |
367 return object; | 369 return object; |
368 } | 370 } |
369 | 371 |
370 function ObjectUnobserve(object, callback) { | 372 function ObjectUnobserve(object, callback) { |
371 if (!IS_SPEC_OBJECT(object)) | 373 if (!IS_SPEC_OBJECT(object)) |
372 throw MakeTypeError("observe_non_object", ["unobserve"]); | 374 throw MakeTypeError("observe_non_object", ["unobserve"]); |
| 375 if (%IsJSGlobalProxy(object)) |
| 376 throw MakeTypeError("observe_global_proxy", ["unobserve"]); |
373 if (!IS_SPEC_FUNCTION(callback)) | 377 if (!IS_SPEC_FUNCTION(callback)) |
374 throw MakeTypeError("observe_non_function", ["unobserve"]); | 378 throw MakeTypeError("observe_non_function", ["unobserve"]); |
375 | 379 |
376 var objectInfo = ObjectInfoGet(object); | 380 var objectInfo = ObjectInfoGet(object); |
377 if (IS_UNDEFINED(objectInfo)) | 381 if (IS_UNDEFINED(objectInfo)) |
378 return object; | 382 return object; |
379 | 383 |
380 ObjectInfoRemoveObserver(objectInfo, callback); | 384 ObjectInfoRemoveObserver(objectInfo, callback); |
381 return object; | 385 return object; |
382 } | 386 } |
383 | 387 |
384 function ArrayObserve(object, callback) { | 388 function ArrayObserve(object, callback) { |
385 return ObjectObserve(object, callback, ['add', | 389 return ObjectObserve(object, callback, ['add', |
386 'update', | 390 'update', |
387 'delete', | 391 'delete', |
388 'splice']); | 392 'splice']); |
389 } | 393 } |
390 | 394 |
391 function ArrayUnobserve(object, callback) { | 395 function ArrayUnobserve(object, callback) { |
392 return ObjectUnobserve(object, callback); | 396 return ObjectUnobserve(object, callback); |
393 } | 397 } |
394 | 398 |
395 function ObserverEnqueueIfActive(observer, objectInfo, changeRecord, | 399 function ObserverEnqueueIfActive(observer, objectInfo, changeRecord) { |
396 needsAccessCheck) { | |
397 if (!ObserverIsActive(observer, objectInfo) || | 400 if (!ObserverIsActive(observer, objectInfo) || |
398 !TypeMapHasType(ObserverGetAcceptTypes(observer), changeRecord.type)) { | 401 !TypeMapHasType(ObserverGetAcceptTypes(observer), changeRecord.type)) { |
399 return; | 402 return; |
400 } | 403 } |
401 | 404 |
402 var callback = ObserverGetCallback(observer); | 405 var callback = ObserverGetCallback(observer); |
403 if (needsAccessCheck && | 406 if (!%ObserverObjectAndRecordHaveSameOrigin(callback, changeRecord.object, |
404 // Drop all splice records on the floor for access-checked objects | 407 changeRecord)) { |
405 (changeRecord.type == 'splice' || | |
406 !%IsAccessAllowedForObserver( | |
407 callback, changeRecord.object, changeRecord.name))) { | |
408 return; | 408 return; |
409 } | 409 } |
410 | 410 |
411 var callbackInfo = CallbackInfoNormalize(callback); | 411 var callbackInfo = CallbackInfoNormalize(callback); |
412 if (IS_NULL(GetPendingObservers())) { | 412 if (IS_NULL(GetPendingObservers())) { |
413 SetPendingObservers(nullProtoObject()) | 413 SetPendingObservers(nullProtoObject()) |
414 EnqueueMicrotask(ObserveMicrotaskRunner); | 414 EnqueueMicrotask(ObserveMicrotaskRunner); |
415 } | 415 } |
416 GetPendingObservers()[callbackInfo.priority] = callback; | 416 GetPendingObservers()[callbackInfo.priority] = callback; |
417 callbackInfo.push(changeRecord); | 417 callbackInfo.push(changeRecord); |
418 } | 418 } |
419 | 419 |
420 function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) { | 420 function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) { |
421 if (!ObjectInfoHasActiveObservers(objectInfo)) | 421 if (!ObjectInfoHasActiveObservers(objectInfo)) |
422 return; | 422 return; |
423 | 423 |
424 var hasType = !IS_UNDEFINED(type); | 424 var hasType = !IS_UNDEFINED(type); |
425 var newRecord = hasType ? | 425 var newRecord = hasType ? |
426 { object: ObjectInfoGetObject(objectInfo), type: type } : | 426 { object: ObjectInfoGetObject(objectInfo), type: type } : |
427 { object: ObjectInfoGetObject(objectInfo) }; | 427 { object: ObjectInfoGetObject(objectInfo) }; |
428 | 428 |
429 for (var prop in changeRecord) { | 429 for (var prop in changeRecord) { |
430 if (prop === 'object' || (hasType && prop === 'type')) continue; | 430 if (prop === 'object' || (hasType && prop === 'type')) continue; |
431 %DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop], | 431 %DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop], |
432 READ_ONLY + DONT_DELETE); | 432 READ_ONLY + DONT_DELETE); |
433 } | 433 } |
434 ObjectFreeze(newRecord); | 434 ObjectFreeze(newRecord); |
435 | 435 |
436 ObjectInfoEnqueueInternalChangeRecord(objectInfo, newRecord, | 436 ObjectInfoEnqueueInternalChangeRecord(objectInfo, newRecord); |
437 true /* skip access check */); | |
438 } | 437 } |
439 | 438 |
440 function ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord, | 439 function ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord) { |
441 skipAccessCheck) { | |
442 // TODO(rossberg): adjust once there is a story for symbols vs proxies. | 440 // TODO(rossberg): adjust once there is a story for symbols vs proxies. |
443 if (IS_SYMBOL(changeRecord.name)) return; | 441 if (IS_SYMBOL(changeRecord.name)) return; |
444 | 442 |
445 var needsAccessCheck = !skipAccessCheck && | |
446 %IsAccessCheckNeeded(changeRecord.object); | |
447 | |
448 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) { | 443 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) { |
449 var observer = objectInfo.changeObservers; | 444 var observer = objectInfo.changeObservers; |
450 ObserverEnqueueIfActive(observer, objectInfo, changeRecord, | 445 ObserverEnqueueIfActive(observer, objectInfo, changeRecord); |
451 needsAccessCheck); | |
452 return; | 446 return; |
453 } | 447 } |
454 | 448 |
455 for (var priority in objectInfo.changeObservers) { | 449 for (var priority in objectInfo.changeObservers) { |
456 var observer = objectInfo.changeObservers[priority]; | 450 var observer = objectInfo.changeObservers[priority]; |
457 if (IS_NULL(observer)) | 451 if (IS_NULL(observer)) |
458 continue; | 452 continue; |
459 ObserverEnqueueIfActive(observer, objectInfo, changeRecord, | 453 ObserverEnqueueIfActive(observer, objectInfo, changeRecord); |
460 needsAccessCheck); | |
461 } | 454 } |
462 } | 455 } |
463 | 456 |
464 function BeginPerformSplice(array) { | 457 function BeginPerformSplice(array) { |
465 var objectInfo = ObjectInfoGet(array); | 458 var objectInfo = ObjectInfoGet(array); |
466 if (!IS_UNDEFINED(objectInfo)) | 459 if (!IS_UNDEFINED(objectInfo)) |
467 ObjectInfoAddPerformingType(objectInfo, 'splice'); | 460 ObjectInfoAddPerformingType(objectInfo, 'splice'); |
468 } | 461 } |
469 | 462 |
470 function EndPerformSplice(array) { | 463 function EndPerformSplice(array) { |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
551 ObjectInfoRemovePerformingType(objectInfo, changeType); | 544 ObjectInfoRemovePerformingType(objectInfo, changeType); |
552 } | 545 } |
553 | 546 |
554 if (IS_SPEC_OBJECT(changeRecord)) | 547 if (IS_SPEC_OBJECT(changeRecord)) |
555 ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, changeType); | 548 ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, changeType); |
556 } | 549 } |
557 | 550 |
558 function ObjectGetNotifier(object) { | 551 function ObjectGetNotifier(object) { |
559 if (!IS_SPEC_OBJECT(object)) | 552 if (!IS_SPEC_OBJECT(object)) |
560 throw MakeTypeError("observe_non_object", ["getNotifier"]); | 553 throw MakeTypeError("observe_non_object", ["getNotifier"]); |
| 554 if (%IsJSGlobalProxy(object)) |
| 555 throw MakeTypeError("observe_global_proxy", ["getNotifier"]); |
561 | 556 |
562 if (ObjectIsFrozen(object)) return null; | 557 if (ObjectIsFrozen(object)) return null; |
563 | 558 |
| 559 if (!%ObjectWasCreatedInCurrentOrigin(object)) return null; |
| 560 |
564 var objectInfo = ObjectInfoGetOrCreate(object); | 561 var objectInfo = ObjectInfoGetOrCreate(object); |
565 return ObjectInfoGetNotifier(objectInfo); | 562 return ObjectInfoGetNotifier(objectInfo); |
566 } | 563 } |
567 | 564 |
568 function CallbackDeliverPending(callback) { | 565 function CallbackDeliverPending(callback) { |
569 var callbackInfo = GetCallbackInfoMap().get(callback); | 566 var callbackInfo = GetCallbackInfoMap().get(callback); |
570 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo)) | 567 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo)) |
571 return false; | 568 return false; |
572 | 569 |
573 // Clear the pending change records from callback and return it to its | 570 // Clear the pending change records from callback and return it to its |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
615 InstallFunctions($Array, DONT_ENUM, $Array( | 612 InstallFunctions($Array, DONT_ENUM, $Array( |
616 "observe", ArrayObserve, | 613 "observe", ArrayObserve, |
617 "unobserve", ArrayUnobserve | 614 "unobserve", ArrayUnobserve |
618 )); | 615 )); |
619 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( | 616 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( |
620 "notify", ObjectNotifierNotify, | 617 "notify", ObjectNotifierNotify, |
621 "performChange", ObjectNotifierPerformChange | 618 "performChange", ObjectNotifierPerformChange |
622 )); | 619 )); |
623 } | 620 } |
624 | 621 |
625 // Disable Object.observe API for M35. | 622 SetupObjectObserve(); |
626 // SetupObjectObserve(); | |
OLD | NEW |