Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Navigation / Journal.cs / 1305600 / Journal.cs
//---------------------------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: // Implements the Avalon Journal Object // // The WCP application journal enables users to retrace their steps backward // and forward in a linear navigation sequence. Whether a navigation application // is hosted in the browser or in a standalone NavigationWindow, each navigation // is persisted in the journal, and can be revisited in a linear sequence by // using the Forward and Back buttons. An application can have multiple // NavigationWindows. Each NavigationWindow has its own Journal. // // The Windows Client Platform will also provide some value adds over the // current journaling behavior. Developers will be able to add their own journal entries, // and to remove entries from the journal (within their own application). // // History: // 07/30/2001: t-ypchen Created // 06/11/2003: kusumav Matched spec at http://avalon/app/Journalling/Journaling.doc // // Copyright (C) 2003 by Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.Serialization; using System.Windows.Threading; using System.Security; using System.Security.Permissions; using MS.Internal; using MS.Internal.AppModel; using MS.Utility; using System.ComponentModel; // Since we disable PreSharp warnings in this file, we first need to disable warnings about unknown message numbers and unknown pragmas. #pragma warning disable 1634, 1691 namespace System.Windows.Navigation { ////// Journal object is provided for each NavigationWindow for linear /// navigations in history. Developers can also add or remove entries /// from the journal. /// ///http://avalon/app/Journalling/Journaling.doc [Serializable] internal sealed class Journal : ISerializable { ////// Construct a new Journal instance. /// internal Journal() { _Initialize(); } [SecurityPermissionAttribute(SecurityAction.LinkDemand, SerializationFormatter = true)] void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("_journalEntryList", _journalEntryList); info.AddValue("_currentEntryIndex", _currentEntryIndex); info.AddValue("_journalEntryId", _journalEntryId); } ////// Ctor for ISerializable implementation /// private Journal(SerializationInfo info, StreamingContext context) { _Initialize(); _journalEntryList = (List)info.GetValue("_journalEntryList", typeof(List )); _currentEntryIndex = info.GetInt32("_currentEntryIndex"); _uncommittedCurrentIndex = _currentEntryIndex; _journalEntryId = info.GetInt32("_journalEntryId"); } //----------------------------------------------------- // // Internal Properties // //----------------------------------------------------- #region Internal Properties #region Operator Overloads /// /// Gets the journal entry at the specified index. /// /// The zero-based index of the journal entry to get or set. ///The journal entry at the specified index. // internal JournalEntry this[int index] { get { return _journalEntryList[index]; } } #endregion Operator Overloads // Total number of entries in the journal including non-navigable entries internal int TotalCount { get { return _journalEntryList.Count; } } ////// Current index - could be in the middle of the list when in history /// navigation. Else will be at the end of the list, a new entry will be /// added at this index for normal navigations /// internal int CurrentIndex { get { return _currentEntryIndex; } } ////// Get the current journal entry. /// internal JournalEntry CurrentEntry { get { if (_currentEntryIndex >= 0 && _currentEntryIndex < TotalCount) { return _journalEntryList[_currentEntryIndex]; } else { return null; } } } internal bool HasUncommittedNavigation { get { return _uncommittedCurrentIndex != _currentEntryIndex; } } ////// The getter for the BackStack /// ///Gets the BackStack internal JournalEntryStack BackStack { get { return _backStack; } } ////// The getter for the ForwardStack /// ///Gets the ForwardStack internal JournalEntryStack ForwardStack { get { return _forwardStack; } } ////// Check if there are journal entries for going back. /// internal bool CanGoBack { get { return GetGoBackEntry() != null; } } ////// Check if there are journal entries for going forward. /// internal bool CanGoForward { get { int index; GetGoForwardEntryIndex(out index); return index != -1; } } ////// Returns a journal version used to invalidate old enumerators after journal data changes /// ///Current journal version internal int Version { get { return _version; } } internal JournalEntryFilter Filter { get { return _filter; } set { _filter = value; BackStack.Filter = _filter; ForwardStack.Filter = _filter; } } #endregion Internal Properties //------------------------------------------------------ // // Internal Events // //----------------------------------------------------- #region Internal Events ////// Raised when the contents of the BackStack or ForwardStack changes. /// Note that this doesn't always mean CanGoBack/CanGoForward has changed. /// internal event EventHandler BackForwardStateChange { add { _backForwardStateChange += value; } remove { _backForwardStateChange -= value; } } [NonSerialized()] EventHandler _backForwardStateChange; #endregion //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods ////// Remove the top JournalEntry from back entry /// // Not a true "remove" internal JournalEntry RemoveBackEntry() { Debug.Assert(ValidateIndexes()); int index = _currentEntryIndex; // start from current but do not change it do { if (--index < 0) { return null; } } while (IsNavigable(_journalEntryList[index]) == false); JournalEntry removedEntry = RemoveEntryInternal(index); Debug.Assert(ValidateIndexes()); UpdateView(); return removedEntry; } ////// Ensures current data about the current page is stored in the journal. /// This either updates an existing entry or adds a new one. /// /// internal void UpdateCurrentEntry(JournalEntry journalEntry) { if (journalEntry == null) { throw new ArgumentNullException("journalEntry"); } Debug.Assert(journalEntry.ContentId != 0); Debug.Assert(!(journalEntry.IsAlive() && journalEntry.JEGroupState.JournalDataStreams != null), "Keep-alive content state should not be serialized."); if (_currentEntryIndex > -1 && _currentEntryIndex < TotalCount) { // update existing entry using the old entry's index. // Note: the new entry can be for a different NavigationService. JournalEntry oldEntry = _journalEntryList[_currentEntryIndex]; journalEntry.Id = oldEntry.Id; _journalEntryList[_currentEntryIndex] = journalEntry; } else { // add new entry to the front journalEntry.Id = ++_journalEntryId; _journalEntryList.Add(journalEntry); } _version++; // If the next navigation is not #fragment or CustomContentState, this entry should be // remembered as the "exit" entry for the group, so when navigating back to the same // page, it will be shown. (It is not necessarily the last one in the group.) // Journal filtering will hide all other entries while at another page (different // NavigationService.Content object). journalEntry.JEGroupState.GroupExitEntry = journalEntry; } internal void RecordNewNavigation() { Invariant.Assert(ValidateIndexes()); Debug.Assert(_uncommittedCurrentIndex == _currentEntryIndex, "This method should be called only in steady state."); // moves _currentEntryIndex forward // clear forward entries if necessary _currentEntryIndex++; _uncommittedCurrentIndex = _currentEntryIndex; if (!ClearForwardStack()) { // If ClearForwardStack() didn't change the journal, UpdateView() needs to be // called here to enable the Back button. UpdateView(); } } internal bool ClearForwardStack() { Debug.Assert(ValidateIndexes()); if (_currentEntryIndex >= TotalCount) return false; // nothing to do if(_uncommittedCurrentIndex > _currentEntryIndex) throw new InvalidOperationException(SR.Get(SRID.InvalidOperation_CannotClearFwdStack)); _journalEntryList.RemoveRange(_currentEntryIndex, _journalEntryList.Count - _currentEntryIndex); UpdateView(); return true; } internal void CommitJournalNavigation(JournalEntry navigated) { NavigateTo(navigated); } internal void AbortJournalNavigation() { _uncommittedCurrentIndex = _currentEntryIndex; UpdateView(); } ////// Get the previous journal entry without changing any indexes. /// ///Null if we cannot go back, otherwise the journal entry on the top of the back stack internal JournalEntry BeginBackNavigation() { Invariant.Assert(ValidateIndexes()); int index; JournalEntry journalEntry = GetGoBackEntry(out index); if (journalEntry == null) throw new InvalidOperationException(SR.Get(SRID.NoBackEntry)); _uncommittedCurrentIndex = index; UpdateView(); if (_uncommittedCurrentIndex == _currentEntryIndex) return null; // See BeginForwardNavigation() for explanation of this special case. return journalEntry; } internal JournalEntry BeginForwardNavigation() { Invariant.Assert(ValidateIndexes()); int fwdEntryIndex; GetGoForwardEntryIndex(out fwdEntryIndex); if (fwdEntryIndex == -1) throw new InvalidOperationException(SR.Get(SRID.NoForwardEntry)); _uncommittedCurrentIndex = fwdEntryIndex; UpdateView(); if (fwdEntryIndex == _currentEntryIndex) { // this is a special case where the user BeginBackNavigation() was called but not allowed to finish // before BeginForwardNavigation() was called. // Note that _uncommittedCurrentIndex may be less than _currentEntryIndex-1 at this // point. That's because there might be non-navigable entries between the two indexes... // Returning null indicates to the caller that it should stop any current navigation // and remain at the current page. If reloading of the current page were allowed, // its controls' state would be lost. return null; } return _journalEntryList[fwdEntryIndex]; } ////// For jump navigation this determines if it is a backwards or forwards navigation /// internal NavigationMode GetNavigationMode(JournalEntry entry) { int index = _journalEntryList.IndexOf(entry); if (index <= _currentEntryIndex) { // If index = _currentEntryIndex it means the application is being navigated back to // in the browser. The browser has just loaded the journal and is restoring the // current page. This would also work if we chose "forward" but it must be one of the // two so that NavigationService will complete the navigation with CommitJournalNavigation() return NavigationMode.Back; } else { return NavigationMode.Forward; } } internal void NavigateTo(JournalEntry target) { Debug.Assert(IsNavigable(target), "target must be navigable"); Debug.Assert(ValidateIndexes()); int index = _journalEntryList.IndexOf(target); // When navigating back to a page which contains a previously navigated frame a // saved journal entry is replayed to restore the frame’s location, in many cases // this entry is not in the journal. if (index > -1) { _currentEntryIndex = index; _uncommittedCurrentIndex = _currentEntryIndex; UpdateView(); } } internal int FindIndexForEntryWithId(int id) { // Search the list for (int i = 0; i < TotalCount; i++) { if (this[i].Id == id) { return i; } } // Didn't find it return -1; } // This is only called from ApplicationProxyInternal.GetSaveHistoryBytes when // we are persisting the entire journal; we only do that when we're quitting. // [new] Also when navigating a Frame that has its own journal. // internal void PruneKeepAliveEntries() { for (int i = TotalCount - 1; i >= 0; --i) { JournalEntry je = _journalEntryList[i]; if (je.IsAlive()) { RemoveEntryInternal(i); } else { Debug.Assert(je.GetType().IsSerializable); // There can be keep-alive JEs creates for child frames. DataStreams jds = je.JEGroupState.JournalDataStreams; if (jds != null) { jds.PrepareForSerialization(); } if (je.RootViewerState != null) { je.RootViewerState.PrepareForSerialization(); } } } } ///The caller is responsible for calling UpdateView(). internal JournalEntry RemoveEntryInternal(int index) { Debug.Assert(index < TotalCount && index >= 0, "Invalid index passed to RemoveEntryInternal"); Debug.Assert(_uncommittedCurrentIndex == _currentEntryIndex, "This method should be called only in steady state."); JournalEntry theEntry = _journalEntryList[index]; Debug.Assert(theEntry != null, "Journal list state is messed up"); // Increase version always, see note above the data member declaration _version++; _journalEntryList.RemoveAt(index); if (_currentEntryIndex > index) { _currentEntryIndex--; } if (_uncommittedCurrentIndex > index) { _uncommittedCurrentIndex--; } return theEntry; } internal void RemoveEntries(Guid navSvcId) { for (int i = TotalCount - 1; i >= 0; i--) { // The entry at _currentEntryIndex is just a placeholder. It should not be deleted. // Otherwise, the following entry (first one in the "forward stack") will get overwritten // if a Back navigation occurs next. if (i != _currentEntryIndex) { JournalEntry entry = _journalEntryList[i]; if (entry.NavigationServiceId == navSvcId) { RemoveEntryInternal(i); } } } UpdateView(); } //[IsKeepAlive() moved to NavigationService.IsContentKeepAlive()] internal void UpdateView() { BackStack.OnCollectionChanged(); ForwardStack.OnCollectionChanged(); if (_backForwardStateChange != null) { _backForwardStateChange(this, EventArgs.Empty); } } ///Returns the entry the GoBack command would navigate to; null/-1 if can't go back. internal JournalEntry GetGoBackEntry(out int index) { for (index = _uncommittedCurrentIndex - 1; index >= 0; index--) { JournalEntry je = _journalEntryList[index]; if (IsNavigable(je)) { return je; } } return null; // and index=-1 } internal JournalEntry GetGoBackEntry() { int unused; return GetGoBackEntry(out unused); } ////// Returns the index of the entry the GoForward command would navigate to; -1 if can't /// go forward. /// ////// This funtion is not symmetric to GetGoBackEntry() becaue of the special case when /// _currentEntryIndex=TotalCount and _uncommittedCurrentIndex=TotalCount-1. Then there is /// no JournalEntry object to return (but fwd navigation is allowed--to the current page). /// internal void GetGoForwardEntryIndex(out int index) { Debug.Assert(ValidateIndexes()); // Special case: _uncommittedCurrentIndex=_currentEntryIndex=TotalCount-1. // Then we can't go fwd. But if _currentEntryIndex=TotalCount, we can. // See also the special case in BeginForwardNavigation(). index = _uncommittedCurrentIndex; do { index++; if (index == _currentEntryIndex) { return; } if (index >= TotalCount) { index = -1; return; } } while (!IsNavigable(_journalEntryList[index])); } #endregion Internal Methods //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods ///Checks that the internal indices are not out of range. If an index is equal /// to TotalCount, it is valid, but there is no JournalEntry created yet (for the current page). /// private bool ValidateIndexes() { return _currentEntryIndex >= 0 && _currentEntryIndex <= TotalCount && _uncommittedCurrentIndex >= 0 && _uncommittedCurrentIndex <= TotalCount; } private void _Initialize() { _backStack = new JournalEntryBackStack(this); _forwardStack = new JournalEntryForwardStack(this); } internal bool IsNavigable(JournalEntry entry) { if (entry == null) return false; // Fallback to entry.IsNavigable if the Filter hasn't been specified return (Filter != null) ? Filter(entry) : entry.IsNavigable(); } #endregion //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- #region Private Fields private JournalEntryFilter _filter; JournalEntryBackStack _backStack; JournalEntryForwardStack _forwardStack; // This is where we get the id we assign to all JournalEntries. // It will be incremented each time. // This is stored in WINDOWDATA structure of the browser's travellog. Trident uses it to // identify frame windows and decide if a frame journal entry is invocable or not. We use it // to identify the JournalEntry which has the NavigationService Guid to identify navigable frame // entries in the current context. When the travelentry is invoked we use this id to find // the JournalEntry to navigate it to. Since we don't explicitly remove the entry from the browser's // travellog when it is removed from the internal Avalon journal, we need to keep this id // unique so we can respond correctly to the CanInvokeEntry calls from the browser. As such // this id needs to be serialized so we can continue to assign unique numbers to each journal entry // if we navigate away and back to the avalon app in the journal // // ISSUE: Multiple browser applications activated in the same browser window (incl. multiple // instances of the same app) need to also use unique ids. Otherwise they can get mixed up. // This can also happen when opening a new window from the current one (Ctrl+N). Then the // TravelLog is copied. // Unfortunately, IE does not distinguish the entries of multiple instances of the same // DocObject when making calls on ITravelLogClient. It gives us only a DWORD for the id // ('dwWindowID'). Attempts to ensure a globally unique instance id across all PresentationHost // instances proved impractical due to restricted access rights. The solution here is to use // the system tick count as an initial value and keep incrementing it. This should be good // enough in all normal usage scenarios. private int _journalEntryId = MS.Win32.SafeNativeMethods.GetTickCount(); private List_journalEntryList = new List (); private int _currentEntryIndex = 0; // This index is used to support the case where the back/forward is called multiple times // without letting the first navigation finish loading. For example if the page is at 'C' // and the back stack contains 'b','a' and Back() is called twice the user should end up at // 'a'. Navigation to 'b' starts but is canceled before it finishes when navigation to 'a' begins. private int _uncommittedCurrentIndex = 0; // Incremented everytime a journal entry is added/removed/updated. The enumerator // operation will then be invalidated since the list it was enumerating over has now // changed. This is the standard implementation used by the .Net ArrayList enumerator too. // We could optimize for the case for when the changes happen at an index greater than // the enumerator index (enumerator would do this check against a lastDirtyIndex that the // journal would maintain). But this will be bad if we decided to implement ICollection later // since we would then export the Count which would need to be invalidated as well. // Also if the enumerator user maintains some kind of count of entries he is interested in // then the index/count would be invalidated. private int _version; #endregion } internal delegate bool JournalEntryFilter(JournalEntry entry); } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------------------------- // // // Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: // Implements the Avalon Journal Object // // The WCP application journal enables users to retrace their steps backward // and forward in a linear navigation sequence. Whether a navigation application // is hosted in the browser or in a standalone NavigationWindow, each navigation // is persisted in the journal, and can be revisited in a linear sequence by // using the Forward and Back buttons. An application can have multiple // NavigationWindows. Each NavigationWindow has its own Journal. // // The Windows Client Platform will also provide some value adds over the // current journaling behavior. Developers will be able to add their own journal entries, // and to remove entries from the journal (within their own application). // // History: // 07/30/2001: t-ypchen Created // 06/11/2003: kusumav Matched spec at http://avalon/app/Journalling/Journaling.doc // // Copyright (C) 2003 by Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.Serialization; using System.Windows.Threading; using System.Security; using System.Security.Permissions; using MS.Internal; using MS.Internal.AppModel; using MS.Utility; using System.ComponentModel; // Since we disable PreSharp warnings in this file, we first need to disable warnings about unknown message numbers and unknown pragmas. #pragma warning disable 1634, 1691 namespace System.Windows.Navigation { ////// Journal object is provided for each NavigationWindow for linear /// navigations in history. Developers can also add or remove entries /// from the journal. /// ///http://avalon/app/Journalling/Journaling.doc [Serializable] internal sealed class Journal : ISerializable { ////// Construct a new Journal instance. /// internal Journal() { _Initialize(); } [SecurityPermissionAttribute(SecurityAction.LinkDemand, SerializationFormatter = true)] void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("_journalEntryList", _journalEntryList); info.AddValue("_currentEntryIndex", _currentEntryIndex); info.AddValue("_journalEntryId", _journalEntryId); } ////// Ctor for ISerializable implementation /// private Journal(SerializationInfo info, StreamingContext context) { _Initialize(); _journalEntryList = (List)info.GetValue("_journalEntryList", typeof(List )); _currentEntryIndex = info.GetInt32("_currentEntryIndex"); _uncommittedCurrentIndex = _currentEntryIndex; _journalEntryId = info.GetInt32("_journalEntryId"); } //----------------------------------------------------- // // Internal Properties // //----------------------------------------------------- #region Internal Properties #region Operator Overloads /// /// Gets the journal entry at the specified index. /// /// The zero-based index of the journal entry to get or set. ///The journal entry at the specified index. // internal JournalEntry this[int index] { get { return _journalEntryList[index]; } } #endregion Operator Overloads // Total number of entries in the journal including non-navigable entries internal int TotalCount { get { return _journalEntryList.Count; } } ////// Current index - could be in the middle of the list when in history /// navigation. Else will be at the end of the list, a new entry will be /// added at this index for normal navigations /// internal int CurrentIndex { get { return _currentEntryIndex; } } ////// Get the current journal entry. /// internal JournalEntry CurrentEntry { get { if (_currentEntryIndex >= 0 && _currentEntryIndex < TotalCount) { return _journalEntryList[_currentEntryIndex]; } else { return null; } } } internal bool HasUncommittedNavigation { get { return _uncommittedCurrentIndex != _currentEntryIndex; } } ////// The getter for the BackStack /// ///Gets the BackStack internal JournalEntryStack BackStack { get { return _backStack; } } ////// The getter for the ForwardStack /// ///Gets the ForwardStack internal JournalEntryStack ForwardStack { get { return _forwardStack; } } ////// Check if there are journal entries for going back. /// internal bool CanGoBack { get { return GetGoBackEntry() != null; } } ////// Check if there are journal entries for going forward. /// internal bool CanGoForward { get { int index; GetGoForwardEntryIndex(out index); return index != -1; } } ////// Returns a journal version used to invalidate old enumerators after journal data changes /// ///Current journal version internal int Version { get { return _version; } } internal JournalEntryFilter Filter { get { return _filter; } set { _filter = value; BackStack.Filter = _filter; ForwardStack.Filter = _filter; } } #endregion Internal Properties //------------------------------------------------------ // // Internal Events // //----------------------------------------------------- #region Internal Events ////// Raised when the contents of the BackStack or ForwardStack changes. /// Note that this doesn't always mean CanGoBack/CanGoForward has changed. /// internal event EventHandler BackForwardStateChange { add { _backForwardStateChange += value; } remove { _backForwardStateChange -= value; } } [NonSerialized()] EventHandler _backForwardStateChange; #endregion //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods ////// Remove the top JournalEntry from back entry /// // Not a true "remove" internal JournalEntry RemoveBackEntry() { Debug.Assert(ValidateIndexes()); int index = _currentEntryIndex; // start from current but do not change it do { if (--index < 0) { return null; } } while (IsNavigable(_journalEntryList[index]) == false); JournalEntry removedEntry = RemoveEntryInternal(index); Debug.Assert(ValidateIndexes()); UpdateView(); return removedEntry; } ////// Ensures current data about the current page is stored in the journal. /// This either updates an existing entry or adds a new one. /// /// internal void UpdateCurrentEntry(JournalEntry journalEntry) { if (journalEntry == null) { throw new ArgumentNullException("journalEntry"); } Debug.Assert(journalEntry.ContentId != 0); Debug.Assert(!(journalEntry.IsAlive() && journalEntry.JEGroupState.JournalDataStreams != null), "Keep-alive content state should not be serialized."); if (_currentEntryIndex > -1 && _currentEntryIndex < TotalCount) { // update existing entry using the old entry's index. // Note: the new entry can be for a different NavigationService. JournalEntry oldEntry = _journalEntryList[_currentEntryIndex]; journalEntry.Id = oldEntry.Id; _journalEntryList[_currentEntryIndex] = journalEntry; } else { // add new entry to the front journalEntry.Id = ++_journalEntryId; _journalEntryList.Add(journalEntry); } _version++; // If the next navigation is not #fragment or CustomContentState, this entry should be // remembered as the "exit" entry for the group, so when navigating back to the same // page, it will be shown. (It is not necessarily the last one in the group.) // Journal filtering will hide all other entries while at another page (different // NavigationService.Content object). journalEntry.JEGroupState.GroupExitEntry = journalEntry; } internal void RecordNewNavigation() { Invariant.Assert(ValidateIndexes()); Debug.Assert(_uncommittedCurrentIndex == _currentEntryIndex, "This method should be called only in steady state."); // moves _currentEntryIndex forward // clear forward entries if necessary _currentEntryIndex++; _uncommittedCurrentIndex = _currentEntryIndex; if (!ClearForwardStack()) { // If ClearForwardStack() didn't change the journal, UpdateView() needs to be // called here to enable the Back button. UpdateView(); } } internal bool ClearForwardStack() { Debug.Assert(ValidateIndexes()); if (_currentEntryIndex >= TotalCount) return false; // nothing to do if(_uncommittedCurrentIndex > _currentEntryIndex) throw new InvalidOperationException(SR.Get(SRID.InvalidOperation_CannotClearFwdStack)); _journalEntryList.RemoveRange(_currentEntryIndex, _journalEntryList.Count - _currentEntryIndex); UpdateView(); return true; } internal void CommitJournalNavigation(JournalEntry navigated) { NavigateTo(navigated); } internal void AbortJournalNavigation() { _uncommittedCurrentIndex = _currentEntryIndex; UpdateView(); } ////// Get the previous journal entry without changing any indexes. /// ///Null if we cannot go back, otherwise the journal entry on the top of the back stack internal JournalEntry BeginBackNavigation() { Invariant.Assert(ValidateIndexes()); int index; JournalEntry journalEntry = GetGoBackEntry(out index); if (journalEntry == null) throw new InvalidOperationException(SR.Get(SRID.NoBackEntry)); _uncommittedCurrentIndex = index; UpdateView(); if (_uncommittedCurrentIndex == _currentEntryIndex) return null; // See BeginForwardNavigation() for explanation of this special case. return journalEntry; } internal JournalEntry BeginForwardNavigation() { Invariant.Assert(ValidateIndexes()); int fwdEntryIndex; GetGoForwardEntryIndex(out fwdEntryIndex); if (fwdEntryIndex == -1) throw new InvalidOperationException(SR.Get(SRID.NoForwardEntry)); _uncommittedCurrentIndex = fwdEntryIndex; UpdateView(); if (fwdEntryIndex == _currentEntryIndex) { // this is a special case where the user BeginBackNavigation() was called but not allowed to finish // before BeginForwardNavigation() was called. // Note that _uncommittedCurrentIndex may be less than _currentEntryIndex-1 at this // point. That's because there might be non-navigable entries between the two indexes... // Returning null indicates to the caller that it should stop any current navigation // and remain at the current page. If reloading of the current page were allowed, // its controls' state would be lost. return null; } return _journalEntryList[fwdEntryIndex]; } ////// For jump navigation this determines if it is a backwards or forwards navigation /// internal NavigationMode GetNavigationMode(JournalEntry entry) { int index = _journalEntryList.IndexOf(entry); if (index <= _currentEntryIndex) { // If index = _currentEntryIndex it means the application is being navigated back to // in the browser. The browser has just loaded the journal and is restoring the // current page. This would also work if we chose "forward" but it must be one of the // two so that NavigationService will complete the navigation with CommitJournalNavigation() return NavigationMode.Back; } else { return NavigationMode.Forward; } } internal void NavigateTo(JournalEntry target) { Debug.Assert(IsNavigable(target), "target must be navigable"); Debug.Assert(ValidateIndexes()); int index = _journalEntryList.IndexOf(target); // When navigating back to a page which contains a previously navigated frame a // saved journal entry is replayed to restore the frame’s location, in many cases // this entry is not in the journal. if (index > -1) { _currentEntryIndex = index; _uncommittedCurrentIndex = _currentEntryIndex; UpdateView(); } } internal int FindIndexForEntryWithId(int id) { // Search the list for (int i = 0; i < TotalCount; i++) { if (this[i].Id == id) { return i; } } // Didn't find it return -1; } // This is only called from ApplicationProxyInternal.GetSaveHistoryBytes when // we are persisting the entire journal; we only do that when we're quitting. // [new] Also when navigating a Frame that has its own journal. // internal void PruneKeepAliveEntries() { for (int i = TotalCount - 1; i >= 0; --i) { JournalEntry je = _journalEntryList[i]; if (je.IsAlive()) { RemoveEntryInternal(i); } else { Debug.Assert(je.GetType().IsSerializable); // There can be keep-alive JEs creates for child frames. DataStreams jds = je.JEGroupState.JournalDataStreams; if (jds != null) { jds.PrepareForSerialization(); } if (je.RootViewerState != null) { je.RootViewerState.PrepareForSerialization(); } } } } ///The caller is responsible for calling UpdateView(). internal JournalEntry RemoveEntryInternal(int index) { Debug.Assert(index < TotalCount && index >= 0, "Invalid index passed to RemoveEntryInternal"); Debug.Assert(_uncommittedCurrentIndex == _currentEntryIndex, "This method should be called only in steady state."); JournalEntry theEntry = _journalEntryList[index]; Debug.Assert(theEntry != null, "Journal list state is messed up"); // Increase version always, see note above the data member declaration _version++; _journalEntryList.RemoveAt(index); if (_currentEntryIndex > index) { _currentEntryIndex--; } if (_uncommittedCurrentIndex > index) { _uncommittedCurrentIndex--; } return theEntry; } internal void RemoveEntries(Guid navSvcId) { for (int i = TotalCount - 1; i >= 0; i--) { // The entry at _currentEntryIndex is just a placeholder. It should not be deleted. // Otherwise, the following entry (first one in the "forward stack") will get overwritten // if a Back navigation occurs next. if (i != _currentEntryIndex) { JournalEntry entry = _journalEntryList[i]; if (entry.NavigationServiceId == navSvcId) { RemoveEntryInternal(i); } } } UpdateView(); } //[IsKeepAlive() moved to NavigationService.IsContentKeepAlive()] internal void UpdateView() { BackStack.OnCollectionChanged(); ForwardStack.OnCollectionChanged(); if (_backForwardStateChange != null) { _backForwardStateChange(this, EventArgs.Empty); } } ///Returns the entry the GoBack command would navigate to; null/-1 if can't go back. internal JournalEntry GetGoBackEntry(out int index) { for (index = _uncommittedCurrentIndex - 1; index >= 0; index--) { JournalEntry je = _journalEntryList[index]; if (IsNavigable(je)) { return je; } } return null; // and index=-1 } internal JournalEntry GetGoBackEntry() { int unused; return GetGoBackEntry(out unused); } ////// Returns the index of the entry the GoForward command would navigate to; -1 if can't /// go forward. /// ////// This funtion is not symmetric to GetGoBackEntry() becaue of the special case when /// _currentEntryIndex=TotalCount and _uncommittedCurrentIndex=TotalCount-1. Then there is /// no JournalEntry object to return (but fwd navigation is allowed--to the current page). /// internal void GetGoForwardEntryIndex(out int index) { Debug.Assert(ValidateIndexes()); // Special case: _uncommittedCurrentIndex=_currentEntryIndex=TotalCount-1. // Then we can't go fwd. But if _currentEntryIndex=TotalCount, we can. // See also the special case in BeginForwardNavigation(). index = _uncommittedCurrentIndex; do { index++; if (index == _currentEntryIndex) { return; } if (index >= TotalCount) { index = -1; return; } } while (!IsNavigable(_journalEntryList[index])); } #endregion Internal Methods //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods ///Checks that the internal indices are not out of range. If an index is equal /// to TotalCount, it is valid, but there is no JournalEntry created yet (for the current page). /// private bool ValidateIndexes() { return _currentEntryIndex >= 0 && _currentEntryIndex <= TotalCount && _uncommittedCurrentIndex >= 0 && _uncommittedCurrentIndex <= TotalCount; } private void _Initialize() { _backStack = new JournalEntryBackStack(this); _forwardStack = new JournalEntryForwardStack(this); } internal bool IsNavigable(JournalEntry entry) { if (entry == null) return false; // Fallback to entry.IsNavigable if the Filter hasn't been specified return (Filter != null) ? Filter(entry) : entry.IsNavigable(); } #endregion //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- #region Private Fields private JournalEntryFilter _filter; JournalEntryBackStack _backStack; JournalEntryForwardStack _forwardStack; // This is where we get the id we assign to all JournalEntries. // It will be incremented each time. // This is stored in WINDOWDATA structure of the browser's travellog. Trident uses it to // identify frame windows and decide if a frame journal entry is invocable or not. We use it // to identify the JournalEntry which has the NavigationService Guid to identify navigable frame // entries in the current context. When the travelentry is invoked we use this id to find // the JournalEntry to navigate it to. Since we don't explicitly remove the entry from the browser's // travellog when it is removed from the internal Avalon journal, we need to keep this id // unique so we can respond correctly to the CanInvokeEntry calls from the browser. As such // this id needs to be serialized so we can continue to assign unique numbers to each journal entry // if we navigate away and back to the avalon app in the journal // // ISSUE: Multiple browser applications activated in the same browser window (incl. multiple // instances of the same app) need to also use unique ids. Otherwise they can get mixed up. // This can also happen when opening a new window from the current one (Ctrl+N). Then the // TravelLog is copied. // Unfortunately, IE does not distinguish the entries of multiple instances of the same // DocObject when making calls on ITravelLogClient. It gives us only a DWORD for the id // ('dwWindowID'). Attempts to ensure a globally unique instance id across all PresentationHost // instances proved impractical due to restricted access rights. The solution here is to use // the system tick count as an initial value and keep incrementing it. This should be good // enough in all normal usage scenarios. private int _journalEntryId = MS.Win32.SafeNativeMethods.GetTickCount(); private List_journalEntryList = new List (); private int _currentEntryIndex = 0; // This index is used to support the case where the back/forward is called multiple times // without letting the first navigation finish loading. For example if the page is at 'C' // and the back stack contains 'b','a' and Back() is called twice the user should end up at // 'a'. Navigation to 'b' starts but is canceled before it finishes when navigation to 'a' begins. private int _uncommittedCurrentIndex = 0; // Incremented everytime a journal entry is added/removed/updated. The enumerator // operation will then be invalidated since the list it was enumerating over has now // changed. This is the standard implementation used by the .Net ArrayList enumerator too. // We could optimize for the case for when the changes happen at an index greater than // the enumerator index (enumerator would do this check against a lastDirtyIndex that the // journal would maintain). But this will be bad if we decided to implement ICollection later // since we would then export the Count which would need to be invalidated as well. // Also if the enumerator user maintains some kind of count of entries he is interested in // then the index/count would be invalidated. private int _version; #endregion } internal delegate bool JournalEntryFilter(JournalEntry entry); } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DataTableReaderListener.cs
- OperatingSystem.cs
- NamedElement.cs
- RegistryPermission.cs
- TypeGeneratedEventArgs.cs
- MethodRental.cs
- XmlUTF8TextWriter.cs
- HyperLink.cs
- UDPClient.cs
- XmlSchemaSearchPattern.cs
- StateWorkerRequest.cs
- XmlSchemaValidationException.cs
- ValueConversionAttribute.cs
- WebRequestModuleElement.cs
- WindowsTitleBar.cs
- MimeMapping.cs
- HitTestFilterBehavior.cs
- OrderedDictionary.cs
- UnsafeNativeMethodsPenimc.cs
- TrackBar.cs
- SimpleMailWebEventProvider.cs
- ConfigurationConverterBase.cs
- AdornerDecorator.cs
- CompositeFontInfo.cs
- HttpProfileBase.cs
- ContentPlaceHolder.cs
- TextEditorLists.cs
- PeerContact.cs
- RightsManagementInformation.cs
- PictureBox.cs
- XPathAxisIterator.cs
- APCustomTypeDescriptor.cs
- WCFBuildProvider.cs
- PageContentCollection.cs
- WhiteSpaceTrimStringConverter.cs
- InputChannelBinder.cs
- HMAC.cs
- ArrayConverter.cs
- XmlWriterSettings.cs
- EndpointFilterProvider.cs
- ColumnResizeUndoUnit.cs
- DrawingContext.cs
- DropShadowBitmapEffect.cs
- NamedObject.cs
- MemoryRecordBuffer.cs
- ContextMenuStripGroupCollection.cs
- TypeUsageBuilder.cs
- TransformPatternIdentifiers.cs
- ChunkedMemoryStream.cs
- WebBrowserPermission.cs
- ObjectAssociationEndMapping.cs
- HandleCollector.cs
- Hex.cs
- DataGridViewTextBoxEditingControl.cs
- SafeHandles.cs
- CustomAssemblyResolver.cs
- Vector3DAnimationBase.cs
- OpenTypeCommon.cs
- EventDescriptorCollection.cs
- TransactionChannel.cs
- GridViewSelectEventArgs.cs
- OdbcConnectionFactory.cs
- ByteRangeDownloader.cs
- DiscoveryClientProtocol.cs
- XmlExtensionFunction.cs
- ClockController.cs
- ipaddressinformationcollection.cs
- WebPartConnectionsDisconnectVerb.cs
- CompletionProxy.cs
- XmlEncodedRawTextWriter.cs
- SqlDependencyUtils.cs
- SourceExpressionException.cs
- Rules.cs
- PagesSection.cs
- RtType.cs
- FieldNameLookup.cs
- ManipulationStartedEventArgs.cs
- GeometryModel3D.cs
- TimeoutException.cs
- PointCollection.cs
- CheckBox.cs
- HttpNamespaceReservationInstallComponent.cs
- StateManagedCollection.cs
- WebPageTraceListener.cs
- PropertyExpression.cs
- ApplicationGesture.cs
- DoubleStorage.cs
- TraceContextRecord.cs
- DateTimeValueSerializerContext.cs
- FastPropertyAccessor.cs
- Speller.cs
- _CookieModule.cs
- HttpPostProtocolImporter.cs
- PrivilegedConfigurationManager.cs
- StorageSetMapping.cs
- ViewManager.cs
- AutomationProperty.cs
- ListControlBoundActionList.cs
- Rotation3D.cs
- ImageUrlEditor.cs