mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 10:01:08 +08:00
fix(core): safety-checks to prevent potential navigation exceptions (#10683)
Plus coding conventions and notes updates. [skip ci]
This commit is contained in:

committed by
GitHub

parent
9bd147c9d0
commit
03cca58712
@ -119,7 +119,7 @@ export function _setAndroidFragmentTransitions(animated: boolean, navigationTran
|
|||||||
curve: transition.getCurve(),
|
curve: transition.getCurve(),
|
||||||
},
|
},
|
||||||
newEntry,
|
newEntry,
|
||||||
transition
|
transition,
|
||||||
);
|
);
|
||||||
if (currentFragmentNeedsDifferentAnimation) {
|
if (currentFragmentNeedsDifferentAnimation) {
|
||||||
setupCurrentFragmentCustomTransition(
|
setupCurrentFragmentCustomTransition(
|
||||||
@ -128,7 +128,7 @@ export function _setAndroidFragmentTransitions(animated: boolean, navigationTran
|
|||||||
curve: transition.getCurve(),
|
curve: transition.getCurve(),
|
||||||
},
|
},
|
||||||
currentEntry,
|
currentEntry,
|
||||||
transition
|
transition,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (name === 'default') {
|
} else if (name === 'default') {
|
||||||
@ -356,7 +356,10 @@ function getTransitionListener(entry: ExpandedEntry, transition: androidx.transi
|
|||||||
@Interfaces([(<any>androidx).transition.Transition.TransitionListener])
|
@Interfaces([(<any>androidx).transition.Transition.TransitionListener])
|
||||||
class TransitionListenerImpl extends java.lang.Object implements androidx.transition.Transition.TransitionListener {
|
class TransitionListenerImpl extends java.lang.Object implements androidx.transition.Transition.TransitionListener {
|
||||||
public backEntry?: BackstackEntry;
|
public backEntry?: BackstackEntry;
|
||||||
constructor(public entry: ExpandedEntry, public transition: androidx.transition.Transition) {
|
constructor(
|
||||||
|
public entry: ExpandedEntry,
|
||||||
|
public transition: androidx.transition.Transition,
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
return global.__native(this);
|
return global.__native(this);
|
||||||
@ -702,7 +705,7 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry, backEntry: Backsta
|
|||||||
if (entries.size === 0) {
|
if (entries.size === 0) {
|
||||||
// We have 0 or 1 entry per frameId in completedEntries
|
// We have 0 or 1 entry per frameId in completedEntries
|
||||||
// So there is no need to make it to Set like waitingQueue
|
// So there is no need to make it to Set like waitingQueue
|
||||||
const previousCompletedAnimationEntry = completedEntries.get(frameId);
|
const previousCompletedEntry = completedEntries.get(frameId);
|
||||||
completedEntries.delete(frameId);
|
completedEntries.delete(frameId);
|
||||||
waitingQueue.delete(frameId);
|
waitingQueue.delete(frameId);
|
||||||
|
|
||||||
@ -716,8 +719,8 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry, backEntry: Backsta
|
|||||||
const navigationContext = frame._executingContext || {
|
const navigationContext = frame._executingContext || {
|
||||||
navigationType: NavigationType.back,
|
navigationType: NavigationType.back,
|
||||||
};
|
};
|
||||||
let current = frame.isCurrent(entry) ? previousCompletedAnimationEntry : entry;
|
const current = frame.isCurrent(entry) && previousCompletedEntry ? previousCompletedEntry : entry;
|
||||||
current = current || entry;
|
|
||||||
// Will be null if Frame is shown modally...
|
// Will be null if Frame is shown modally...
|
||||||
// transitionOrAnimationCompleted fires again (probably bug in android).
|
// transitionOrAnimationCompleted fires again (probably bug in android).
|
||||||
if (current) {
|
if (current) {
|
||||||
|
@ -41,7 +41,7 @@ export class FrameBase extends CustomLayoutView {
|
|||||||
private _animated: boolean;
|
private _animated: boolean;
|
||||||
private _transition: NavigationTransition;
|
private _transition: NavigationTransition;
|
||||||
private _backStack = new Array<BackstackEntry>();
|
private _backStack = new Array<BackstackEntry>();
|
||||||
_navigationQueue = new Array<NavigationContext>();
|
private _navigationQueue = new Array<NavigationContext>();
|
||||||
|
|
||||||
public actionBarVisibility: 'auto' | 'never' | 'always';
|
public actionBarVisibility: 'auto' | 'never' | 'always';
|
||||||
public _currentEntry: BackstackEntry;
|
public _currentEntry: BackstackEntry;
|
||||||
@ -300,7 +300,14 @@ export class FrameBase extends CustomLayoutView {
|
|||||||
this._backStack.pop();
|
this._backStack.pop();
|
||||||
} else if (!isReplace) {
|
} else if (!isReplace) {
|
||||||
if (entry.entry.clearHistory) {
|
if (entry.entry.clearHistory) {
|
||||||
this._backStack.forEach((e) => this._removeEntry(e));
|
this._backStack.forEach((e) => {
|
||||||
|
if (e !== entry) {
|
||||||
|
this._removeEntry(e);
|
||||||
|
} else {
|
||||||
|
// This case is extremely rare but can occur when fragment resumes
|
||||||
|
Trace.write(`Failed to dispose backstack entry ${entry}. This entry is the one frame is navigating to.`, Trace.categories.Navigation, Trace.messageType.warn);
|
||||||
|
}
|
||||||
|
});
|
||||||
this._backStack.length = 0;
|
this._backStack.length = 0;
|
||||||
} else if (FrameBase._isEntryBackstackVisible(current)) {
|
} else if (FrameBase._isEntryBackstackVisible(current)) {
|
||||||
this._backStack.push(current);
|
this._backStack.push(current);
|
||||||
@ -370,6 +377,16 @@ export class FrameBase extends CustomLayoutView {
|
|||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getNavigationQueueContextByEntry(entry: BackstackEntry): NavigationContext {
|
||||||
|
for (const context of this._navigationQueue) {
|
||||||
|
if (context.entry === entry) {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public navigationQueueIsEmpty(): boolean {
|
public navigationQueueIsEmpty(): boolean {
|
||||||
return this._navigationQueue.length === 0;
|
return this._navigationQueue.length === 0;
|
||||||
}
|
}
|
||||||
@ -429,16 +446,18 @@ export class FrameBase extends CustomLayoutView {
|
|||||||
|
|
||||||
@profile
|
@profile
|
||||||
performGoBack(navigationContext: NavigationContext) {
|
performGoBack(navigationContext: NavigationContext) {
|
||||||
let backstackEntry = navigationContext.entry;
|
|
||||||
const backstack = this._backStack;
|
const backstack = this._backStack;
|
||||||
if (!backstackEntry) {
|
const backstackEntry = navigationContext.entry || backstack[backstack.length - 1];
|
||||||
backstackEntry = backstack[backstack.length - 1];
|
|
||||||
|
if (backstackEntry) {
|
||||||
navigationContext.entry = backstackEntry;
|
navigationContext.entry = backstackEntry;
|
||||||
}
|
|
||||||
|
|
||||||
this._executingContext = navigationContext;
|
this._executingContext = navigationContext;
|
||||||
this._onNavigatingTo(backstackEntry, true);
|
this._onNavigatingTo(backstackEntry, true);
|
||||||
this._goBackCore(backstackEntry);
|
this._goBackCore(backstackEntry);
|
||||||
|
} else {
|
||||||
|
Trace.write('Frame.performGoBack: No backstack entry found to navigate back to', Trace.categories.Navigation, Trace.messageType.warn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public _goBackCore(backstackEntry: BackstackEntry) {
|
public _goBackCore(backstackEntry: BackstackEntry) {
|
||||||
|
4
packages/core/ui/frame/index.d.ts
vendored
4
packages/core/ui/frame/index.d.ts
vendored
@ -200,6 +200,10 @@ export class Frame extends FrameBase {
|
|||||||
* @param navigationType
|
* @param navigationType
|
||||||
*/
|
*/
|
||||||
setCurrent(entry: BackstackEntry, navigationType: NavigationType): void;
|
setCurrent(entry: BackstackEntry, navigationType: NavigationType): void;
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
getNavigationQueueContextByEntry(entry: BackstackEntry): NavigationContext;
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
@ -18,7 +18,7 @@ const DELEGATE = '_delegate';
|
|||||||
const TRANSITION = '_transition';
|
const TRANSITION = '_transition';
|
||||||
const NON_ANIMATED_TRANSITION = 'non-animated';
|
const NON_ANIMATED_TRANSITION = 'non-animated';
|
||||||
|
|
||||||
function isBackNavigationTo(page: Page, entry): boolean {
|
function isBackNavigationTo(page: Page, entry: BackstackEntry): boolean {
|
||||||
const frame = page.frame;
|
const frame = page.frame;
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
return false;
|
return false;
|
||||||
@ -37,14 +37,8 @@ function isBackNavigationTo(page: Page, entry): boolean {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigationQueue = (<any>frame)._navigationQueue;
|
const queueContext = frame.getNavigationQueueContextByEntry(entry);
|
||||||
for (let i = 0; i < navigationQueue.length; i++) {
|
return queueContext && queueContext.navigationType === NavigationType.back;
|
||||||
if (navigationQueue[i].entry === entry) {
|
|
||||||
return navigationQueue[i].navigationType === NavigationType.back;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isBackNavigationFrom(controller: UIViewControllerImpl, page: Page): boolean {
|
function isBackNavigationFrom(controller: UIViewControllerImpl, page: Page): boolean {
|
||||||
@ -121,7 +115,7 @@ class UIViewControllerImpl extends UIViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const frame: Frame = this.navigationController ? (<any>this.navigationController).owner : null;
|
const frame: Frame = this.navigationController ? (<any>this.navigationController).owner : null;
|
||||||
const newEntry = this[ENTRY];
|
const newEntry: BackstackEntry = this[ENTRY];
|
||||||
|
|
||||||
// Don't raise event if currentPage was showing modal page.
|
// Don't raise event if currentPage was showing modal page.
|
||||||
if (!owner._presentedViewController && newEntry && (!frame || frame.currentPage !== owner)) {
|
if (!owner._presentedViewController && newEntry && (!frame || frame.currentPage !== owner)) {
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
## Tabs vs Spaces
|
## Tabs vs Spaces
|
||||||
|
|
||||||
Use 4 spaces indentation.
|
Use tab width 2 indentation.
|
||||||
|
|
||||||
## Line length
|
## Line length
|
||||||
|
|
||||||
Try to limit your lines to 80 characters.
|
Try to limit your lines to 600 characters.
|
||||||
|
|
||||||
## Semicolons, statement Termination
|
## Semicolons, statement Termination
|
||||||
|
|
||||||
@ -26,18 +26,18 @@ let x = 1
|
|||||||
|
|
||||||
## Quotes
|
## Quotes
|
||||||
|
|
||||||
Use double quotes for strings:
|
Use single quotes for strings:
|
||||||
|
|
||||||
*Right:*
|
*Right:*
|
||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
let foo = "bar";
|
let foo = 'bar';
|
||||||
```
|
```
|
||||||
|
|
||||||
*Wrong:*
|
*Wrong:*
|
||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
let foo = 'bar';
|
let foo = "bar";
|
||||||
```
|
```
|
||||||
|
|
||||||
## Braces
|
## Braces
|
||||||
@ -48,7 +48,7 @@ Your opening braces go on the same line as the statement.
|
|||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
if (true) {
|
if (true) {
|
||||||
console.log("winning");
|
console.log('winning');
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ if (true) {
|
|||||||
```TypeScript
|
```TypeScript
|
||||||
if (true)
|
if (true)
|
||||||
{
|
{
|
||||||
console.log("losing");
|
console.log('losing');
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -69,9 +69,9 @@ Follow the JavaScript convention of stacking `else/catch` clauses on the same li
|
|||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
if (i % 2 === 0) {
|
if (i % 2 === 0) {
|
||||||
console.log("even");
|
console.log('even');
|
||||||
} else {
|
} else {
|
||||||
console.log("odd");
|
console.log('odd');
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -79,10 +79,10 @@ if (i % 2 === 0) {
|
|||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
if (i % 2 === 0) {
|
if (i % 2 === 0) {
|
||||||
console.log("even");
|
console.log('even');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("odd");
|
console.log('odd');
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -120,13 +120,13 @@ uncommon abbreviations should generally be avoided unless it is something well k
|
|||||||
*Right:*
|
*Right:*
|
||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
let adminUser = db.query("SELECT * FROM users ...");
|
let adminUser = db.query('SELECT * FROM users ...');
|
||||||
```
|
```
|
||||||
|
|
||||||
*Wrong:*
|
*Wrong:*
|
||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
let admin_user = db.query("SELECT * FROM users ...");
|
let admin_user = db.query('SELECT * FROM users ...');
|
||||||
```
|
```
|
||||||
|
|
||||||
[camelcase]: https://en.wikipedia.org/wiki/camelCase#Variations_and_synonyms
|
[camelcase]: https://en.wikipedia.org/wiki/camelCase#Variations_and_synonyms
|
||||||
@ -139,7 +139,7 @@ Type names should be capitalized using [upper camel case][camelcase].
|
|||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
class UserAccount() {
|
class UserAccount() {
|
||||||
this.field = "a";
|
this.field = 'a';
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ class UserAccount() {
|
|||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
class userAccount() {
|
class userAccount() {
|
||||||
this.field = "a";
|
this.field = 'a';
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -176,10 +176,10 @@ keys when your interpreter complains:
|
|||||||
*Right:*
|
*Right:*
|
||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
let a = ["hello", "world"];
|
let a = ['hello', 'world'];
|
||||||
let b = {
|
let b = {
|
||||||
good: "code",
|
good: 'code',
|
||||||
"is generally": "pretty",
|
'is generally': 'pretty',
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -187,23 +187,23 @@ let b = {
|
|||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
let a = [
|
let a = [
|
||||||
"hello", "world"
|
'hello', 'world'
|
||||||
];
|
];
|
||||||
let b = {"good": "code"
|
let b = {'good': 'code'
|
||||||
, is generally: "pretty"
|
, is generally: 'pretty'
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
## Equality operator
|
## Equality operator
|
||||||
|
|
||||||
Use the [strict comparison operators][comparisonoperators]. The triple equality operator helps to maintain data type integrity throughout the code.
|
Use the [strict comparison operators][comparisonoperators] when needed. The triple equality operator helps to maintain data type integrity throughout the code.
|
||||||
|
|
||||||
*Right:*
|
*Right:*
|
||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
let a = 0;
|
let a = 0;
|
||||||
if (a === "") {
|
if (a === '') {
|
||||||
console.log("winning");
|
console.log('winning');
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -212,8 +212,8 @@ if (a === "") {
|
|||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
let a = 0;
|
let a = 0;
|
||||||
if (a == "") {
|
if (a == '') {
|
||||||
console.log("losing");
|
console.log('losing');
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -227,8 +227,8 @@ Try to avoid short-hand operators except in very simple scenarios.
|
|||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
let default = x || 50;
|
let default = x || 50;
|
||||||
let extraLarge = "xxl";
|
let extraLarge = 'xxl';
|
||||||
let small = "s"
|
let small = 's'
|
||||||
let big = (x > 10) ? extraLarge : small;
|
let big = (x > 10) ? extraLarge : small;
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ Always use curly braces even in the cases of one line conditional operations.
|
|||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
if (a) {
|
if (a) {
|
||||||
return "winning";
|
return 'winning';
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -257,9 +257,9 @@ if (a) {
|
|||||||
```TypeScript
|
```TypeScript
|
||||||
|
|
||||||
if (a)
|
if (a)
|
||||||
return "winning";
|
return 'winning';
|
||||||
|
|
||||||
if (a) return "winning";
|
if (a) return 'winning';
|
||||||
```
|
```
|
||||||
|
|
||||||
## Boolean comparisons
|
## Boolean comparisons
|
||||||
@ -271,11 +271,11 @@ if (a) return "winning";
|
|||||||
```TypeScript
|
```TypeScript
|
||||||
|
|
||||||
if (condition) {
|
if (condition) {
|
||||||
console.log("winning");
|
console.log('winning');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
console.log("winning");
|
console.log('winning');
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -285,15 +285,15 @@ if (!condition) {
|
|||||||
```TypeScript
|
```TypeScript
|
||||||
|
|
||||||
if (condition === true) {
|
if (condition === true) {
|
||||||
console.log("losing");
|
console.log('losing');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (condition !== true) {
|
if (condition !== true) {
|
||||||
console.log("losing");
|
console.log('losing');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (condition !== false) {
|
if (condition !== false) {
|
||||||
console.log("losing");
|
console.log('losing');
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -306,7 +306,7 @@ Do not use the **Yoda Conditions** when writing boolean expressions:
|
|||||||
```TypeScript
|
```TypeScript
|
||||||
let num;
|
let num;
|
||||||
if (num >= 0) {
|
if (num >= 0) {
|
||||||
console.log("winning");
|
console.log('winning');
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -315,14 +315,14 @@ if (num >= 0) {
|
|||||||
```TypeScript
|
```TypeScript
|
||||||
let num;
|
let num;
|
||||||
if (0 <= num) {
|
if (0 <= num) {
|
||||||
console.log("losing");
|
console.log('losing');
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**NOTE** It is OK to use constants on the left when comparing for a range.
|
**NOTE** It is OK to use constants on the left when comparing for a range.
|
||||||
```TypeScript
|
```TypeScript
|
||||||
if (0 <= num && num <= 100) {
|
if (0 <= num && num <= 100) {
|
||||||
console.log("winning");
|
console.log('winning');
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -392,7 +392,7 @@ Use arrow functions over anonymous function expressions. Typescript will take ca
|
|||||||
*Right:*
|
*Right:*
|
||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
req.on("end", () => {
|
req.on('end', () => {
|
||||||
exp1();
|
exp1();
|
||||||
exp2();
|
exp2();
|
||||||
this.doSomething();
|
this.doSomething();
|
||||||
@ -403,7 +403,7 @@ req.on("end", () => {
|
|||||||
|
|
||||||
```TypeScript
|
```TypeScript
|
||||||
let that = this;
|
let that = this;
|
||||||
req.on("end", function () {
|
req.on('end', function () {
|
||||||
exp1();
|
exp1();
|
||||||
exp2();
|
exp2();
|
||||||
that.doSomething();
|
that.doSomething();
|
||||||
|
Reference in New Issue
Block a user