diff --git a/apps/tests/testRunner.ts b/apps/tests/testRunner.ts index bb02546d6..1e84be3b6 100644 --- a/apps/tests/testRunner.ts +++ b/apps/tests/testRunner.ts @@ -7,7 +7,7 @@ import uiTestModule = require("./ui-test"); frameModule.Frame.defaultAnimatedNavigation = false; -function isRunningOnEmulator(): boolean { +export function isRunningOnEmulator(): boolean { // This checks are not good enough to be added to modules but keeps unittests green. if (platform.device.os === platform.platformNames.android) { diff --git a/apps/tests/ui/list-view/list-view-tests.ts b/apps/tests/ui/list-view/list-view-tests.ts index de6a53567..3b6db9736 100644 --- a/apps/tests/ui/list-view/list-view-tests.ts +++ b/apps/tests/ui/list-view/list-view-tests.ts @@ -6,6 +6,7 @@ import observable = require("data/observable"); import types = require("utils/types"); import platform = require("platform"); import utils = require("utils/utils"); +import testRunner = require("../../testRunner"); //  // # ListView @@ -633,6 +634,9 @@ export function test_ConverterIsCalledJustOnce_onAddingItemsToListView() { } export function test_no_memory_leak_when_items_is_regular_array() { + if (testRunner.isRunningOnEmulator()) { + return; + } var createFunc = function (): listViewModule.ListView { var listView = new listViewModule.ListView(); listView.items = FEW_ITEMS; @@ -645,6 +649,9 @@ export function test_no_memory_leak_when_items_is_regular_array() { } export function test_no_memory_leak_when_items_is_observable_array() { + if (testRunner.isRunningOnEmulator()) { + return; + } // Keep the reference to the observable array to test the weakEventListener var colors = new observableArray.ObservableArray(["red", "green", "blue"]); diff --git a/apps/tests/ui/observable-tests.ts b/apps/tests/ui/observable-tests.ts index 23f76f057..b8bb5598e 100644 --- a/apps/tests/ui/observable-tests.ts +++ b/apps/tests/ui/observable-tests.ts @@ -378,3 +378,30 @@ export var test_Observable_WhenCreatedWithJSON_PropertyChangedWithBracketsNotati TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly."); } + +export function test_AddingTwoEventHandlersAndRemovingWithinHandlerShouldRaiseAllEvents() { + var observableInstance = new observable.Observable(); + var firstHandlerCalled = false; + var secondHandlerCalled= false; + + var firstHandler = function (args) { + observableInstance.off(observable.Observable.propertyChangeEvent, firstHandler, firstObserver); + firstHandlerCalled = true; + } + + var secondHandler = function (args) { + observableInstance.off(observable.Observable.propertyChangeEvent, secondHandler, secondObserver); + secondHandlerCalled = true; + } + + var firstObserver = new observable.Observable(); + var secondObserver = new observable.Observable(); + + observableInstance.on(observable.Observable.propertyChangeEvent, firstHandler, firstObserver); + observableInstance.on(observable.Observable.propertyChangeEvent, secondHandler, secondObserver); + + observableInstance.set("someProperty", "some value"); + + TKUnit.assertEqual(firstHandlerCalled, true); + TKUnit.assertEqual(secondHandlerCalled, true); +} diff --git a/apps/tests/ui/repeater/repeaterItems-bindingToGestures.xml b/apps/tests/ui/repeater/repeaterItems-bindingToGestures.xml index addde5ae1..667c672a1 100644 --- a/apps/tests/ui/repeater/repeaterItems-bindingToGestures.xml +++ b/apps/tests/ui/repeater/repeaterItems-bindingToGestures.xml @@ -2,7 +2,7 @@ - diff --git a/apps/tests/ui/text-field/text-field-tests.ts b/apps/tests/ui/text-field/text-field-tests.ts index a6d78c6db..637535aef 100644 --- a/apps/tests/ui/text-field/text-field-tests.ts +++ b/apps/tests/ui/text-field/text-field-tests.ts @@ -1,4 +1,5 @@ import TKUnit = require("../../TKUnit"); +import testRunner = require("../../testRunner"); import helper = require("../helper"); import viewModule = require("ui/core/view"); import pagesModule = require("ui/page"); @@ -427,6 +428,9 @@ export var testNativeTextAlignmentFromLocal = function () { } export var testMemoryLeak = function () { + if (testRunner.isRunningOnEmulator()) { + return; + } helper.buildUIWithWeakRefAndInteract(_createTextFieldFunc, function (textField) { textFieldTestsNative.typeTextNatively(textField, "Hello, world!"); }); diff --git a/apps/tests/ui/text-view/text-view-tests.ts b/apps/tests/ui/text-view/text-view-tests.ts index 1666e99c7..631088a61 100644 --- a/apps/tests/ui/text-view/text-view-tests.ts +++ b/apps/tests/ui/text-view/text-view-tests.ts @@ -1,4 +1,5 @@ import TKUnit = require("../../TKUnit"); +import testRunner = require("../../testRunner"); import helper = require("../helper"); import viewModule = require("ui/core/view"); import pagesModule = require("ui/page"); @@ -468,6 +469,9 @@ export var testNativeTextAlignmentFromLocal = function () { } export var testMemoryLeak = function () { + if (testRunner.isRunningOnEmulator()) { + return; + } helper.buildUIWithWeakRefAndInteract(_createTextViewFunc, function (textView) { textViewTestsNative.typeTextNatively(textView, "Hello, world!"); }); diff --git a/apps/tests/xml-declaration/xml-declaration-tests.ts b/apps/tests/xml-declaration/xml-declaration-tests.ts index 4083ea09d..5ab1be618 100644 --- a/apps/tests/xml-declaration/xml-declaration-tests.ts +++ b/apps/tests/xml-declaration/xml-declaration-tests.ts @@ -603,7 +603,7 @@ export function test_parse_ShouldParseNestedListViewInListViewTemplate() { } export function test_parse_ShouldEvaluateEventBindingExpressionInListViewTemplate() { - var p = builder.parse(''); + var p = builder.parse(''); function testAction(views: Array) { var ctrl: segmentedBar.SegmentedBar; diff --git a/data/observable/observable.ts b/data/observable/observable.ts index 3fd51a3c3..565fc58e7 100644 --- a/data/observable/observable.ts +++ b/data/observable/observable.ts @@ -136,7 +136,7 @@ export class Observable implements definition.Observable { var i; var entry: ListenerEntry; var observersLength = observers.length; - for (i = 0; i < observersLength; i++) { + for (i = observersLength - 1; i >= 0 ; i--) { entry = observers[i]; if (entry.thisArg) { entry.callback.apply(entry.thisArg, [data]); diff --git a/ui/core/bindable.ts b/ui/core/bindable.ts index 9cc0f646a..0c4bc3c30 100644 --- a/ui/core/bindable.ts +++ b/ui/core/bindable.ts @@ -140,6 +140,15 @@ export class Binding { source: WeakRef; target: WeakRef; + public loadedHandlerVisualTreeBinding(args) { + var targetInstance = args.object; + targetInstance.off(viewModule.View.loadedEvent, this.loadedHandlerVisualTreeBinding, this); + this.unbind(); + if (!types.isNullOrUndefined(targetInstance.bindingContext)) { + this.bind(targetInstance.bindingContext); + } + }; + private propertyChangeListeners = {}; private sourceOptions: { instance: WeakRef; property: any }; @@ -213,6 +222,11 @@ export class Binding { if (parentView) { currentObject = parentView.bindingContext; } + else { + var targetInstance = this.target.get(); + targetInstance.off(viewModule.View.loadedEvent, this.loadedHandlerVisualTreeBinding, this); + targetInstance.on(viewModule.View.loadedEvent, this.loadedHandlerVisualTreeBinding, this); + } currentObjectChanged = true; } result.push({ instance: currentObject, property: objProp }); @@ -507,7 +521,7 @@ export class Binding { indexAsInt--; } } - else { + else if (types.isString(index)) { while (result && result.typeName !== index) { result = result.parent; } diff --git a/ui/list-view/list-view.android.ts b/ui/list-view/list-view.android.ts index fd41b3a1b..131754dc2 100644 --- a/ui/list-view/list-view.android.ts +++ b/ui/list-view/list-view.android.ts @@ -205,6 +205,7 @@ class ListViewAdapter extends android.widget.BaseAdapter { } if (args.view) { + this._listView._prepareItem(args.view, index); if (!args.view.parent) { if (args.view instanceof layoutBaseModule.LayoutBase) { this._listView._addView(args.view); @@ -221,8 +222,6 @@ class ListViewAdapter extends android.widget.BaseAdapter { this._listView._realizedItems[convertView.hashCode()] = args.view; // cache the realized index (used to raise the ItemLoading event upon scroll stop) args.view[REALIZED_INDEX] = index; - - this._listView._prepareItem(args.view, index); } return convertView; diff --git a/ui/list-view/list-view.ios.ts b/ui/list-view/list-view.ios.ts index 77b6c2fe8..9cfc07328 100644 --- a/ui/list-view/list-view.ios.ts +++ b/ui/list-view/list-view.ios.ts @@ -239,12 +239,12 @@ export class ListView extends common.ListView { var args = notifyForItemAtIndex(this, cell, ITEMLOADING, indexPath); var view = cell.view = args.view || this._getDefaultItemContent(indexPath.row); + this._prepareItem(view, indexPath.row); if (view && !view.parent && view.ios) { cell.contentView.addSubview(view.ios); this._addView(view); } - this._prepareItem(view, indexPath.row); cellHeight = this._layoutCell(view, indexPath); } finally { diff --git a/ui/repeater/repeater.ts b/ui/repeater/repeater.ts index a40540421..6600d7868 100644 --- a/ui/repeater/repeater.ts +++ b/ui/repeater/repeater.ts @@ -149,8 +149,8 @@ export class Repeater extends viewModule.CustomLayoutView implements definition. for (i = 0; i < this.items.length; i++) { var viewToAdd = !types.isNullOrUndefined(this.itemTemplate) ? builder.parse(this.itemTemplate, this) : this._getDefaultItemContent(i); if (!types.isNullOrUndefined(viewToAdd)) { - this.itemsLayout.addChild(viewToAdd); viewToAdd.bindingContext = this._getDataItem(i); + this.itemsLayout.addChild(viewToAdd); } } }