2012-10-25 14:56:44 +00:00
|
|
|
// Copyright 2012 the V8 project authors. All rights reserved.
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions are
|
|
|
|
// met:
|
|
|
|
//
|
|
|
|
// * Redistributions of source code must retain the above copyright
|
|
|
|
// notice, this list of conditions and the following disclaimer.
|
|
|
|
// * Redistributions in binary form must reproduce the above
|
|
|
|
// copyright notice, this list of conditions and the following
|
|
|
|
// disclaimer in the documentation and/or other materials provided
|
|
|
|
// with the distribution.
|
|
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
|
|
// contributors may be used to endorse or promote products derived
|
|
|
|
// from this software without specific prior written permission.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
2012-11-06 16:47:15 +00:00
|
|
|
var observationState = %GetObservationState();
|
|
|
|
if (IS_UNDEFINED(observationState.observerInfoMap)) {
|
2013-02-04 20:24:11 +00:00
|
|
|
observationState.observerInfoMap = %ObservationWeakMapCreate();
|
|
|
|
observationState.objectInfoMap = %ObservationWeakMapCreate();
|
|
|
|
observationState.notifierTargetMap = %ObservationWeakMapCreate();
|
2012-11-13 15:53:28 +00:00
|
|
|
observationState.pendingObservers = new InternalArray;
|
2012-11-08 13:44:59 +00:00
|
|
|
observationState.observerPriority = 0;
|
2012-10-25 14:56:44 +00:00
|
|
|
}
|
|
|
|
|
2013-02-04 20:24:11 +00:00
|
|
|
function ObservationWeakMap(map) {
|
|
|
|
this.map_ = map;
|
2012-10-25 14:56:44 +00:00
|
|
|
}
|
|
|
|
|
2013-02-04 20:24:11 +00:00
|
|
|
ObservationWeakMap.prototype = {
|
2012-11-06 16:47:15 +00:00
|
|
|
get: function(key) {
|
2013-02-04 20:24:11 +00:00
|
|
|
key = %UnwrapGlobalProxy(key);
|
|
|
|
if (!IS_SPEC_OBJECT(key)) return void 0;
|
|
|
|
return %WeakMapGet(this.map_, key);
|
2012-11-06 16:47:15 +00:00
|
|
|
},
|
|
|
|
set: function(key, value) {
|
2013-02-04 20:24:11 +00:00
|
|
|
key = %UnwrapGlobalProxy(key);
|
|
|
|
if (!IS_SPEC_OBJECT(key)) return void 0;
|
|
|
|
%WeakMapSet(this.map_, key, value);
|
2012-11-06 16:47:15 +00:00
|
|
|
},
|
|
|
|
has: function(key) {
|
2012-12-05 12:03:57 +00:00
|
|
|
return !IS_UNDEFINED(this.get(key));
|
2012-11-06 16:47:15 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-02-04 20:24:11 +00:00
|
|
|
var observerInfoMap =
|
|
|
|
new ObservationWeakMap(observationState.observerInfoMap);
|
|
|
|
var objectInfoMap = new ObservationWeakMap(observationState.objectInfoMap);
|
|
|
|
var notifierTargetMap =
|
|
|
|
new ObservationWeakMap(observationState.notifierTargetMap);
|
2012-11-09 16:14:42 +00:00
|
|
|
|
|
|
|
function CreateObjectInfo(object) {
|
|
|
|
var info = {
|
|
|
|
changeObservers: new InternalArray,
|
|
|
|
notifier: null,
|
2013-05-15 22:09:40 +00:00
|
|
|
inactiveObservers: new InternalArray,
|
|
|
|
performing: { __proto__: null },
|
|
|
|
performingCount: 0,
|
2012-11-09 16:14:42 +00:00
|
|
|
};
|
|
|
|
objectInfoMap.set(object, info);
|
|
|
|
return info;
|
|
|
|
}
|
2012-10-25 14:56:44 +00:00
|
|
|
|
2013-05-15 22:09:40 +00:00
|
|
|
var defaultAcceptTypes = {
|
|
|
|
__proto__: null,
|
|
|
|
'new': true,
|
|
|
|
'updated': true,
|
|
|
|
'deleted': true,
|
|
|
|
'prototype': true,
|
|
|
|
'reconfigured': true
|
|
|
|
};
|
|
|
|
|
|
|
|
function CreateObserver(callback, accept) {
|
|
|
|
var observer = {
|
|
|
|
__proto__: null,
|
|
|
|
callback: callback,
|
|
|
|
accept: defaultAcceptTypes
|
|
|
|
};
|
|
|
|
|
|
|
|
if (IS_UNDEFINED(accept))
|
|
|
|
return observer;
|
|
|
|
|
|
|
|
var acceptMap = { __proto__: null };
|
|
|
|
for (var i = 0; i < accept.length; i++)
|
|
|
|
acceptMap[accept[i]] = true;
|
|
|
|
|
|
|
|
observer.accept = acceptMap;
|
|
|
|
return observer;
|
|
|
|
}
|
|
|
|
|
|
|
|
function ObserverIsActive(observer, objectInfo) {
|
|
|
|
if (objectInfo.performingCount === 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
var performing = objectInfo.performing;
|
|
|
|
for (var type in performing) {
|
|
|
|
if (performing[type] > 0 && observer.accept[type])
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function ObserverIsInactive(observer, objectInfo) {
|
|
|
|
return !ObserverIsActive(observer, objectInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
function RemoveNullElements(from) {
|
|
|
|
var i = 0;
|
|
|
|
var j = 0;
|
|
|
|
for (; i < from.length; i++) {
|
|
|
|
if (from[i] === null)
|
|
|
|
continue;
|
|
|
|
if (j < i)
|
|
|
|
from[j] = from[i];
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i !== j)
|
|
|
|
from.length = from.length - (i - j);
|
|
|
|
}
|
|
|
|
|
|
|
|
function RepartitionObservers(conditionFn, from, to, objectInfo) {
|
|
|
|
var anyRemoved = false;
|
|
|
|
for (var i = 0; i < from.length; i++) {
|
|
|
|
var observer = from[i];
|
|
|
|
if (conditionFn(observer, objectInfo)) {
|
|
|
|
anyRemoved = true;
|
|
|
|
from[i] = null;
|
|
|
|
to.push(observer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (anyRemoved)
|
|
|
|
RemoveNullElements(from);
|
|
|
|
}
|
|
|
|
|
|
|
|
function BeginPerformChange(objectInfo, type) {
|
|
|
|
objectInfo.performing[type] = (objectInfo.performing[type] || 0) + 1;
|
|
|
|
objectInfo.performingCount++;
|
|
|
|
RepartitionObservers(ObserverIsInactive,
|
|
|
|
objectInfo.changeObservers,
|
|
|
|
objectInfo.inactiveObservers,
|
|
|
|
objectInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
function EndPerformChange(objectInfo, type) {
|
|
|
|
objectInfo.performing[type]--;
|
|
|
|
objectInfo.performingCount--;
|
|
|
|
RepartitionObservers(ObserverIsActive,
|
|
|
|
objectInfo.inactiveObservers,
|
|
|
|
objectInfo.changeObservers,
|
|
|
|
objectInfo);
|
|
|
|
}
|
|
|
|
|
2013-05-16 11:19:37 +00:00
|
|
|
function EnsureObserverRemoved(objectInfo, callback) {
|
2013-05-15 22:09:40 +00:00
|
|
|
function remove(observerList) {
|
|
|
|
for (var i = 0; i < observerList.length; i++) {
|
|
|
|
if (observerList[i].callback === callback) {
|
|
|
|
observerList.splice(i, 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!remove(objectInfo.changeObservers))
|
|
|
|
remove(objectInfo.inactiveObservers);
|
|
|
|
}
|
|
|
|
|
|
|
|
function AcceptArgIsValid(arg) {
|
|
|
|
if (IS_UNDEFINED(arg))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!IS_SPEC_OBJECT(arg) ||
|
|
|
|
!IS_NUMBER(arg.length) ||
|
|
|
|
arg.length < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
var length = arg.length;
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
if (!IS_STRING(arg[i]))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function ObjectObserve(object, callback, accept) {
|
2012-10-25 14:56:44 +00:00
|
|
|
if (!IS_SPEC_OBJECT(object))
|
|
|
|
throw MakeTypeError("observe_non_object", ["observe"]);
|
|
|
|
if (!IS_SPEC_FUNCTION(callback))
|
|
|
|
throw MakeTypeError("observe_non_function", ["observe"]);
|
2012-11-16 09:35:27 +00:00
|
|
|
if (ObjectIsFrozen(callback))
|
2012-10-25 14:56:44 +00:00
|
|
|
throw MakeTypeError("observe_callback_frozen");
|
2013-05-15 22:09:40 +00:00
|
|
|
if (!AcceptArgIsValid(accept))
|
|
|
|
throw MakeTypeError("observe_accept_invalid");
|
2012-10-25 14:56:44 +00:00
|
|
|
|
|
|
|
if (!observerInfoMap.has(callback)) {
|
|
|
|
observerInfoMap.set(callback, {
|
2012-11-08 13:44:59 +00:00
|
|
|
pendingChangeRecords: null,
|
|
|
|
priority: observationState.observerPriority++,
|
2012-10-25 14:56:44 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
var objectInfo = objectInfoMap.get(object);
|
2012-12-19 09:51:46 +00:00
|
|
|
if (IS_UNDEFINED(objectInfo)) objectInfo = CreateObjectInfo(object);
|
2012-12-13 09:31:44 +00:00
|
|
|
%SetIsObserved(object, true);
|
2012-10-25 14:56:44 +00:00
|
|
|
|
2013-05-16 11:19:37 +00:00
|
|
|
EnsureObserverRemoved(objectInfo, callback);
|
2013-05-15 22:09:40 +00:00
|
|
|
|
|
|
|
var observer = CreateObserver(callback, accept);
|
|
|
|
if (ObserverIsActive(observer, objectInfo))
|
|
|
|
objectInfo.changeObservers.push(observer);
|
|
|
|
else
|
|
|
|
objectInfo.inactiveObservers.push(observer);
|
2012-10-25 14:56:44 +00:00
|
|
|
|
2012-11-20 10:38:14 +00:00
|
|
|
return object;
|
2012-10-25 14:56:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function ObjectUnobserve(object, callback) {
|
|
|
|
if (!IS_SPEC_OBJECT(object))
|
|
|
|
throw MakeTypeError("observe_non_object", ["unobserve"]);
|
2012-11-13 15:50:59 +00:00
|
|
|
if (!IS_SPEC_FUNCTION(callback))
|
|
|
|
throw MakeTypeError("observe_non_function", ["unobserve"]);
|
2012-10-25 14:56:44 +00:00
|
|
|
|
|
|
|
var objectInfo = objectInfoMap.get(object);
|
|
|
|
if (IS_UNDEFINED(objectInfo))
|
2012-11-20 10:38:14 +00:00
|
|
|
return object;
|
2012-10-25 14:56:44 +00:00
|
|
|
|
2013-05-16 11:19:37 +00:00
|
|
|
EnsureObserverRemoved(objectInfo, callback);
|
2013-05-15 22:09:40 +00:00
|
|
|
|
|
|
|
if (objectInfo.changeObservers.length === 0 &&
|
|
|
|
objectInfo.inactiveObservers.length === 0) {
|
|
|
|
%SetIsObserved(object, false);
|
2012-12-13 09:31:44 +00:00
|
|
|
}
|
2012-10-25 14:56:44 +00:00
|
|
|
|
2012-11-20 10:38:14 +00:00
|
|
|
return object;
|
2012-10-25 14:56:44 +00:00
|
|
|
}
|
|
|
|
|
2013-05-16 11:19:37 +00:00
|
|
|
function ArrayObserve(object, callback) {
|
|
|
|
return ObjectObserve(object, callback, ['new',
|
|
|
|
'updated',
|
|
|
|
'deleted',
|
|
|
|
'splice']);
|
|
|
|
}
|
|
|
|
|
|
|
|
function ArrayUnobserve(object, callback) {
|
|
|
|
return ObjectUnobserve(object, callback);
|
|
|
|
}
|
|
|
|
|
2012-10-25 14:56:44 +00:00
|
|
|
function EnqueueChangeRecord(changeRecord, observers) {
|
2013-03-22 17:27:44 +00:00
|
|
|
// TODO(rossberg): adjust once there is a story for symbols vs proxies.
|
|
|
|
if (IS_SYMBOL(changeRecord.name)) return;
|
|
|
|
|
2012-10-25 14:56:44 +00:00
|
|
|
for (var i = 0; i < observers.length; i++) {
|
|
|
|
var observer = observers[i];
|
2013-05-15 22:09:40 +00:00
|
|
|
if (IS_UNDEFINED(observer.accept[changeRecord.type]))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
var callback = observer.callback;
|
|
|
|
var observerInfo = observerInfoMap.get(callback);
|
|
|
|
observationState.pendingObservers[observerInfo.priority] = callback;
|
2012-11-08 13:44:59 +00:00
|
|
|
%SetObserverDeliveryPending();
|
2012-10-25 14:56:44 +00:00
|
|
|
if (IS_NULL(observerInfo.pendingChangeRecords)) {
|
|
|
|
observerInfo.pendingChangeRecords = new InternalArray(changeRecord);
|
|
|
|
} else {
|
|
|
|
observerInfo.pendingChangeRecords.push(changeRecord);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-16 11:19:37 +00:00
|
|
|
function BeginPerformSplice(array) {
|
|
|
|
var objectInfo = objectInfoMap.get(array);
|
|
|
|
if (!IS_UNDEFINED(objectInfo))
|
|
|
|
BeginPerformChange(objectInfo, 'splice');
|
|
|
|
}
|
|
|
|
|
|
|
|
function EndPerformSplice(array) {
|
|
|
|
var objectInfo = objectInfoMap.get(array);
|
|
|
|
if (!IS_UNDEFINED(objectInfo))
|
|
|
|
EndPerformChange(objectInfo, 'splice');
|
|
|
|
}
|
|
|
|
|
2013-06-06 19:15:40 +00:00
|
|
|
function EnqueueSpliceRecord(array, index, removed, addedCount) {
|
2013-05-16 11:19:37 +00:00
|
|
|
var objectInfo = objectInfoMap.get(array);
|
|
|
|
if (IS_UNDEFINED(objectInfo) || objectInfo.changeObservers.length === 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var changeRecord = {
|
|
|
|
type: 'splice',
|
|
|
|
object: array,
|
|
|
|
index: index,
|
|
|
|
removed: removed,
|
|
|
|
addedCount: addedCount
|
|
|
|
};
|
|
|
|
|
2013-05-28 11:34:31 +00:00
|
|
|
ObjectFreeze(changeRecord);
|
|
|
|
ObjectFreeze(changeRecord.removed);
|
2013-05-16 11:19:37 +00:00
|
|
|
EnqueueChangeRecord(changeRecord, objectInfo.changeObservers);
|
|
|
|
}
|
|
|
|
|
2012-11-06 12:32:36 +00:00
|
|
|
function NotifyChange(type, object, name, oldValue) {
|
|
|
|
var objectInfo = objectInfoMap.get(object);
|
2013-05-15 22:09:40 +00:00
|
|
|
if (objectInfo.changeObservers.length === 0)
|
|
|
|
return;
|
|
|
|
|
2012-11-06 12:32:36 +00:00
|
|
|
var changeRecord = (arguments.length < 4) ?
|
|
|
|
{ type: type, object: object, name: name } :
|
|
|
|
{ type: type, object: object, name: name, oldValue: oldValue };
|
2013-05-28 11:34:31 +00:00
|
|
|
ObjectFreeze(changeRecord);
|
2012-11-06 12:32:36 +00:00
|
|
|
EnqueueChangeRecord(changeRecord, objectInfo.changeObservers);
|
|
|
|
}
|
|
|
|
|
2012-11-09 16:14:42 +00:00
|
|
|
var notifierPrototype = {};
|
|
|
|
|
|
|
|
function ObjectNotifierNotify(changeRecord) {
|
|
|
|
if (!IS_SPEC_OBJECT(this))
|
|
|
|
throw MakeTypeError("called_on_non_object", ["notify"]);
|
2012-12-19 10:28:36 +00:00
|
|
|
|
|
|
|
var target = notifierTargetMap.get(this);
|
2012-11-09 16:14:42 +00:00
|
|
|
if (IS_UNDEFINED(target))
|
|
|
|
throw MakeTypeError("observe_notify_non_notifier");
|
2012-10-25 14:56:44 +00:00
|
|
|
if (!IS_STRING(changeRecord.type))
|
|
|
|
throw MakeTypeError("observe_type_non_string");
|
|
|
|
|
2012-11-09 16:14:42 +00:00
|
|
|
var objectInfo = objectInfoMap.get(target);
|
2012-12-19 09:51:46 +00:00
|
|
|
if (IS_UNDEFINED(objectInfo) || objectInfo.changeObservers.length === 0)
|
2012-10-25 14:56:44 +00:00
|
|
|
return;
|
|
|
|
|
2012-12-19 09:51:46 +00:00
|
|
|
var newRecord = { object: target };
|
2012-10-25 14:56:44 +00:00
|
|
|
for (var prop in changeRecord) {
|
2012-12-19 09:51:46 +00:00
|
|
|
if (prop === 'object') continue;
|
2012-11-16 09:35:27 +00:00
|
|
|
%DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop],
|
|
|
|
READ_ONLY + DONT_DELETE);
|
2012-10-25 14:56:44 +00:00
|
|
|
}
|
2013-05-28 11:34:31 +00:00
|
|
|
ObjectFreeze(newRecord);
|
2012-10-25 14:56:44 +00:00
|
|
|
|
|
|
|
EnqueueChangeRecord(newRecord, objectInfo.changeObservers);
|
|
|
|
}
|
|
|
|
|
2013-05-15 22:09:40 +00:00
|
|
|
function ObjectNotifierPerformChange(changeType, changeFn, receiver) {
|
|
|
|
if (!IS_SPEC_OBJECT(this))
|
|
|
|
throw MakeTypeError("called_on_non_object", ["performChange"]);
|
|
|
|
|
|
|
|
var target = notifierTargetMap.get(this);
|
|
|
|
if (IS_UNDEFINED(target))
|
|
|
|
throw MakeTypeError("observe_notify_non_notifier");
|
|
|
|
if (!IS_STRING(changeType))
|
|
|
|
throw MakeTypeError("observe_perform_non_string");
|
|
|
|
if (!IS_SPEC_FUNCTION(changeFn))
|
|
|
|
throw MakeTypeError("observe_perform_non_function");
|
|
|
|
|
|
|
|
if (IS_NULL_OR_UNDEFINED(receiver)) {
|
|
|
|
receiver = %GetDefaultReceiver(changeFn) || receiver;
|
|
|
|
} else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(changeFn)) {
|
|
|
|
receiver = ToObject(receiver);
|
|
|
|
}
|
|
|
|
|
|
|
|
var objectInfo = objectInfoMap.get(target);
|
|
|
|
if (IS_UNDEFINED(objectInfo))
|
|
|
|
return;
|
|
|
|
|
|
|
|
BeginPerformChange(objectInfo, changeType);
|
|
|
|
try {
|
|
|
|
%_CallFunction(receiver, changeFn);
|
|
|
|
} finally {
|
|
|
|
EndPerformChange(objectInfo, changeType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-09 16:14:42 +00:00
|
|
|
function ObjectGetNotifier(object) {
|
|
|
|
if (!IS_SPEC_OBJECT(object))
|
|
|
|
throw MakeTypeError("observe_non_object", ["getNotifier"]);
|
|
|
|
|
2012-12-19 09:51:46 +00:00
|
|
|
if (ObjectIsFrozen(object)) return null;
|
2012-11-09 16:14:42 +00:00
|
|
|
|
|
|
|
var objectInfo = objectInfoMap.get(object);
|
2012-12-19 09:51:46 +00:00
|
|
|
if (IS_UNDEFINED(objectInfo)) objectInfo = CreateObjectInfo(object);
|
2012-11-09 16:14:42 +00:00
|
|
|
|
|
|
|
if (IS_NULL(objectInfo.notifier)) {
|
2012-12-19 09:51:46 +00:00
|
|
|
objectInfo.notifier = { __proto__: notifierPrototype };
|
2012-11-09 16:14:42 +00:00
|
|
|
notifierTargetMap.set(objectInfo.notifier, object);
|
|
|
|
}
|
|
|
|
|
|
|
|
return objectInfo.notifier;
|
|
|
|
}
|
|
|
|
|
2012-11-08 13:44:59 +00:00
|
|
|
function DeliverChangeRecordsForObserver(observer) {
|
|
|
|
var observerInfo = observerInfoMap.get(observer);
|
2012-10-25 14:56:44 +00:00
|
|
|
if (IS_UNDEFINED(observerInfo))
|
2012-12-19 09:51:46 +00:00
|
|
|
return false;
|
2012-10-25 14:56:44 +00:00
|
|
|
|
|
|
|
var pendingChangeRecords = observerInfo.pendingChangeRecords;
|
|
|
|
if (IS_NULL(pendingChangeRecords))
|
2012-12-19 09:51:46 +00:00
|
|
|
return false;
|
2012-10-25 14:56:44 +00:00
|
|
|
|
|
|
|
observerInfo.pendingChangeRecords = null;
|
2012-11-13 15:53:28 +00:00
|
|
|
delete observationState.pendingObservers[observerInfo.priority];
|
2012-10-25 14:56:44 +00:00
|
|
|
var delivered = [];
|
|
|
|
%MoveArrayContents(pendingChangeRecords, delivered);
|
|
|
|
try {
|
2012-11-08 13:44:59 +00:00
|
|
|
%Call(void 0, delivered, observer);
|
2012-10-25 14:56:44 +00:00
|
|
|
} catch (ex) {}
|
2012-12-19 09:51:46 +00:00
|
|
|
return true;
|
2012-10-25 14:56:44 +00:00
|
|
|
}
|
|
|
|
|
2012-11-08 13:44:59 +00:00
|
|
|
function ObjectDeliverChangeRecords(callback) {
|
|
|
|
if (!IS_SPEC_FUNCTION(callback))
|
|
|
|
throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]);
|
|
|
|
|
2012-12-19 09:51:46 +00:00
|
|
|
while (DeliverChangeRecordsForObserver(callback)) {}
|
2012-11-08 13:44:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function DeliverChangeRecords() {
|
2012-11-13 15:53:28 +00:00
|
|
|
while (observationState.pendingObservers.length) {
|
|
|
|
var pendingObservers = observationState.pendingObservers;
|
|
|
|
observationState.pendingObservers = new InternalArray;
|
|
|
|
for (var i in pendingObservers) {
|
|
|
|
DeliverChangeRecordsForObserver(pendingObservers[i]);
|
2012-11-08 13:44:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-25 14:56:44 +00:00
|
|
|
function SetupObjectObserve() {
|
|
|
|
%CheckIsBootstrapping();
|
|
|
|
InstallFunctions($Object, DONT_ENUM, $Array(
|
|
|
|
"deliverChangeRecords", ObjectDeliverChangeRecords,
|
2012-11-09 16:14:42 +00:00
|
|
|
"getNotifier", ObjectGetNotifier,
|
2012-10-25 14:56:44 +00:00
|
|
|
"observe", ObjectObserve,
|
|
|
|
"unobserve", ObjectUnobserve
|
|
|
|
));
|
2013-05-16 11:19:37 +00:00
|
|
|
InstallFunctions($Array, DONT_ENUM, $Array(
|
|
|
|
"observe", ArrayObserve,
|
|
|
|
"unobserve", ArrayUnobserve
|
|
|
|
));
|
2012-11-09 16:14:42 +00:00
|
|
|
InstallFunctions(notifierPrototype, DONT_ENUM, $Array(
|
2013-05-15 22:09:40 +00:00
|
|
|
"notify", ObjectNotifierNotify,
|
|
|
|
"performChange", ObjectNotifierPerformChange
|
2012-11-09 16:14:42 +00:00
|
|
|
));
|
2012-10-25 14:56:44 +00:00
|
|
|
}
|
|
|
|
|
2012-11-06 12:32:36 +00:00
|
|
|
SetupObjectObserve();
|