locked
Javascript Navigator Bug?

    Question

  • Okay so this is not easily possible to practically do on touchscreen devices, but on my desktop I keep running into the same issue over and over:

    I'm using the same navigator provided with the grid template project. When I navigate to a new page (by clicking an item) and then back very quickly (by hitting backspace or the Mouse back button) the app crashes without any information as to why. (terminateAppHandler)

    This happens even when just using the template without any adjustments. I've used my G15 Keyboard to make a macro that selects an item and then hits backspace, with which I can get the app to crash 100% of the time.

    Can anyone confirm this or is it just me?

    Thanks

    Censored

    Thursday, July 5, 2012 9:56 AM

Answers

  • Can you try replacing navigator.js with this and let me know if you can still repro the problem?

    (function () {
        "use strict";

        var appView = Windows.UI.ViewManagement.ApplicationView;
        var displayProps = Windows.Graphics.Display.DisplayProperties;
        var nav = WinJS.Navigation;
        var ui = WinJS.UI;
        var utils = WinJS.Utilities;

        WinJS.Namespace.define("Application", {
            PageControlNavigator: WinJS.Class.define(
                // Define the constructor function for the PageControlNavigator.
                function PageControlNavigator(element, options) {
                    this.element = element || document.createElement("div");
                    this.element.appendChild(this._createPageElement());

                    this.home = options.home;
                    this.lastViewstate = appView.value;

                    nav.onnavigated = this._navigated.bind(this);
                    window.onresize = this._resized.bind(this);

                    document.body.onkeyup = this._keyupHandler.bind(this);
                    document.body.onkeypress = this._keypressHandler.bind(this);
                    document.body.onmspointerup = this._mspointerupHandler.bind(this);

                    Application.navigator = this;
                }, {
                    /// <field domElement="true" />
                    element: null,
                    home: "",
                    lastViewstate: 0,

                    // This function creates a new container for each page.
                    _createPageElement: function () {
                        var element = document.createElement("div");
                        element.style.width = "100%";
                        element.style.height = "100%";
                        return element;
                    },

                    // This function responds to keypresses to only navigate when
                    // the backspace key is not used elsewhere.
                    _keypressHandler: function (args) {
                        if (args.key === "Backspace") {
                            nav.back();
                        }
                    },

                    // This function responds to keyup to enable keyboard navigation.
                    _keyupHandler: function (args) {
                        if ((args.key === "Left" && args.altKey) || (args.key === "BrowserBack")) {
                            nav.back();
                        } else if ((args.key === "Right" && args.altKey) || (args.key === "BrowserForward")) {
                            nav.forward();
                        }
                    },

                    _mspointerupHandler: function (args) {
                        if (args.button === 3) {
                            nav.back();
                        } else if (args.button === 4) {
                            nav.forward();
                        }
                    },

                    // This function responds to navigation by adding new pages
                    // to the DOM.
                    _navigated: function (args) {
                        var newElement = this._createPageElement();
                        var parentedComplete;
                        var parented = new WinJS.Promise(function (c) { parentedComplete = c; });

                        args.detail.setPromise(
                            WinJS.Promise.timeout().then(function () {
                                if (this.pageElement.winControl && this.pageElement.winControl.unload) {
                                    this.pageElement.winControl.unload();
                                }
                                return WinJS.UI.Pages.render(args.detail.location, newElement, args.detail.state, parented);
                            }.bind(this)).then(function parentElement(control) {
                                this.element.appendChild(newElement);
                                var oldElement = this.pageElement;
                                this.element.removeChild(oldElement);
                                oldElement.innerText = "";
                                this._updateBackButton();
                                parentedComplete();
                            }.bind(this))
                        );
                    },
                    _updateBackButton: function () {
                        var backButton = this.pageElement.querySelector("header[role=banner] .win-backbutton");
                        if (backButton) {
                            backButton.onclick = function () { nav.back(); };

                            if (nav.canGoBack) {
                                backButton.removeAttribute("disabled");
                            } else {
                                backButton.setAttribute("disabled", "disabled");
                            }
                        }
                    },

                    _resized: function (args) {
                        if (this.pageControl && this.pageControl.updateLayout) {
                            this.pageControl.updateLayout.call(this.pageControl, this.pageElement, appView.value, this.lastViewstate);
                        }
                        this.lastViewstate = appView.value;
                    },

                    // This function updates application controls once a navigation
                    // has completed.
                    navigated: function () {
                        // Do application specific on-navigated work here
                        //var backButton = this.pageElement.querySelector("header[role=banner] .win-backbutton");
                        //if (backButton) {
                        //    backButton.onclick = function () { nav.back(); };

                        //    if (nav.canGoBack) {
                        //        backButton.removeAttribute("disabled");
                        //    } else {
                        //        backButton.setAttribute("disabled", "disabled");
                        //    }
                        //}
                    },

                    // This is the PageControlNavigator object.
                    pageControl: {
                        get: function () { return this.pageElement && this.pageElement.winControl; }
                    },

                    // This is the root element of the current page.
                    pageElement: {
                        get: function () { return this.element.firstElementChild; }
                    }
                }
            )
        });
    })();


    -Jeff

    Jeff Sanders (MSFT)



    Thursday, July 5, 2012 5:16 PM
    Moderator
  • It was a timing issue, if you run the original template in the debugger and break on all exceptions you will see the issue (an object was null because it was not yet constructed).


    Jeff Sanders (MSFT)

    • Marked as answer by Dino He Tuesday, July 10, 2012 1:40 AM
    Friday, July 6, 2012 6:33 PM
    Moderator

All replies

  • Hi C,

    I was able to repro this and will file a bug.

    I will also see if there is a way you can workaround this (I have a couple of ideas).

    -Jeff


    Jeff Sanders (MSFT)

    Thursday, July 5, 2012 5:10 PM
    Moderator
  • Can you try replacing navigator.js with this and let me know if you can still repro the problem?

    (function () {
        "use strict";

        var appView = Windows.UI.ViewManagement.ApplicationView;
        var displayProps = Windows.Graphics.Display.DisplayProperties;
        var nav = WinJS.Navigation;
        var ui = WinJS.UI;
        var utils = WinJS.Utilities;

        WinJS.Namespace.define("Application", {
            PageControlNavigator: WinJS.Class.define(
                // Define the constructor function for the PageControlNavigator.
                function PageControlNavigator(element, options) {
                    this.element = element || document.createElement("div");
                    this.element.appendChild(this._createPageElement());

                    this.home = options.home;
                    this.lastViewstate = appView.value;

                    nav.onnavigated = this._navigated.bind(this);
                    window.onresize = this._resized.bind(this);

                    document.body.onkeyup = this._keyupHandler.bind(this);
                    document.body.onkeypress = this._keypressHandler.bind(this);
                    document.body.onmspointerup = this._mspointerupHandler.bind(this);

                    Application.navigator = this;
                }, {
                    /// <field domElement="true" />
                    element: null,
                    home: "",
                    lastViewstate: 0,

                    // This function creates a new container for each page.
                    _createPageElement: function () {
                        var element = document.createElement("div");
                        element.style.width = "100%";
                        element.style.height = "100%";
                        return element;
                    },

                    // This function responds to keypresses to only navigate when
                    // the backspace key is not used elsewhere.
                    _keypressHandler: function (args) {
                        if (args.key === "Backspace") {
                            nav.back();
                        }
                    },

                    // This function responds to keyup to enable keyboard navigation.
                    _keyupHandler: function (args) {
                        if ((args.key === "Left" && args.altKey) || (args.key === "BrowserBack")) {
                            nav.back();
                        } else if ((args.key === "Right" && args.altKey) || (args.key === "BrowserForward")) {
                            nav.forward();
                        }
                    },

                    _mspointerupHandler: function (args) {
                        if (args.button === 3) {
                            nav.back();
                        } else if (args.button === 4) {
                            nav.forward();
                        }
                    },

                    // This function responds to navigation by adding new pages
                    // to the DOM.
                    _navigated: function (args) {
                        var newElement = this._createPageElement();
                        var parentedComplete;
                        var parented = new WinJS.Promise(function (c) { parentedComplete = c; });

                        args.detail.setPromise(
                            WinJS.Promise.timeout().then(function () {
                                if (this.pageElement.winControl && this.pageElement.winControl.unload) {
                                    this.pageElement.winControl.unload();
                                }
                                return WinJS.UI.Pages.render(args.detail.location, newElement, args.detail.state, parented);
                            }.bind(this)).then(function parentElement(control) {
                                this.element.appendChild(newElement);
                                var oldElement = this.pageElement;
                                this.element.removeChild(oldElement);
                                oldElement.innerText = "";
                                this._updateBackButton();
                                parentedComplete();
                            }.bind(this))
                        );
                    },
                    _updateBackButton: function () {
                        var backButton = this.pageElement.querySelector("header[role=banner] .win-backbutton");
                        if (backButton) {
                            backButton.onclick = function () { nav.back(); };

                            if (nav.canGoBack) {
                                backButton.removeAttribute("disabled");
                            } else {
                                backButton.setAttribute("disabled", "disabled");
                            }
                        }
                    },

                    _resized: function (args) {
                        if (this.pageControl && this.pageControl.updateLayout) {
                            this.pageControl.updateLayout.call(this.pageControl, this.pageElement, appView.value, this.lastViewstate);
                        }
                        this.lastViewstate = appView.value;
                    },

                    // This function updates application controls once a navigation
                    // has completed.
                    navigated: function () {
                        // Do application specific on-navigated work here
                        //var backButton = this.pageElement.querySelector("header[role=banner] .win-backbutton");
                        //if (backButton) {
                        //    backButton.onclick = function () { nav.back(); };

                        //    if (nav.canGoBack) {
                        //        backButton.removeAttribute("disabled");
                        //    } else {
                        //        backButton.setAttribute("disabled", "disabled");
                        //    }
                        //}
                    },

                    // This is the PageControlNavigator object.
                    pageControl: {
                        get: function () { return this.pageElement && this.pageElement.winControl; }
                    },

                    // This is the root element of the current page.
                    pageElement: {
                        get: function () { return this.element.firstElementChild; }
                    }
                }
            )
        });
    })();


    -Jeff

    Jeff Sanders (MSFT)



    Thursday, July 5, 2012 5:16 PM
    Moderator
  • Hi Jeff,

    thanks for confirming this. I just tried your (updated) workaround and I can't seem to repro the issue anymore.

    Thanks a lot!

    Censored

    Thursday, July 5, 2012 8:23 PM
  • Jeff, what exactly is the workaround? Commenting out the adjustments for the backbutton? Wouldn't that seriously break any app?
    Friday, July 6, 2012 8:43 AM
  • Phil,

    Inspect the code more carefully, you will see a new function:  _updateBackButton

    -Jeff


    Jeff Sanders (MSFT)

    Friday, July 6, 2012 12:14 PM
    Moderator
  • ok, I see it now. Can you describe the bug please? Why is setting the backbutton after the "navigated" event a problem?
    Friday, July 6, 2012 3:01 PM
  • It was a timing issue, if you run the original template in the debugger and break on all exceptions you will see the issue (an object was null because it was not yet constructed).


    Jeff Sanders (MSFT)

    • Marked as answer by Dino He Tuesday, July 10, 2012 1:40 AM
    Friday, July 6, 2012 6:33 PM
    Moderator
  • Thank you jpsanders!! I had similar problems, navigate to a page and it would crash on 'bind'. This copy of navigate.js seems to fix my issues. One comment, page animation seems to be different /off, but, not on back navigation.

    BTW, I'm using VStudio RTM (version 11.0.50727.1 RTMREL) on Win8 RTM (build 9200) and still had this issue.

    David J.




    Thursday, August 30, 2012 1:01 AM