/// <summary> Called by the browser to serialize the entire journal or just the index of /// the current entry. The second case is when an internal Journal update needs to be /// reflected in the TravelLog. /// </summary> /// <param name="arg"> true is the entire Journal is to serialized </param> private object _GetSaveHistoryBytesDelegate(object arg) { bool entireJournal = (bool)arg; SaveHistoryReturnInfo info = new SaveHistoryReturnInfo(); // DevDiv 716414 / DevDiv2 196517 & 224724: // Checking _serviceProvider for null due to COM reentrancy issues observed by customers. // Users who perform frequent refreshes may experience shutdown while we are still starting up and are not fully initialized. // The ServiceProvider field is one of the last things to be set during initialization, so if this is null // we know that we have not finished initialization much less run the app and thus have no need to save history. if (_serviceProvider == null) { return(null); } // When we are here, the browser has just started to shut down, so we should only check // whether the application object is shutting down. if (Application.IsApplicationObjectShuttingDown == true) { return(null); } Invariant.Assert(_rbw.Value != null, "BrowserJournalingError: _rbw should not be null"); Journal journal = _rbw.Value.Journal; Invariant.Assert(journal != null, "BrowserJournalingError: Could not get internal journal for the window"); JournalEntry entry; if (entireJournal) // The application is about to be shut down... { NavigationService topNavSvc = _rbw.Value.NavigationService; try { topNavSvc.RequestCustomContentStateOnAppShutdown(); } catch (Exception e) { if (CriticalExceptions.IsCriticalException(e)) { throw; } } journal.PruneKeepAliveEntries(); // Since the current page is not added to the journal until it is replaced, // we add it here explicitly to the internal Journal before serializing it. entry = topNavSvc.MakeJournalEntry(JournalReason.NewContentNavigation); if (entry != null && !entry.IsAlive()) { if (entry.JEGroupState.JournalDataStreams != null) { entry.JEGroupState.JournalDataStreams.PrepareForSerialization(); } journal.UpdateCurrentEntry(entry); } else // Maybe the current content is null or a PageFunction doesn't want to be journaled. { // Then the previous navigable page, if any, should be remembered as current. entry = journal.GetGoBackEntry(); // i. _LoadHistoryStreamDelegate() has a similar special case. } } else { // (Brittle) Assumption: GetSaveHistoryBytes() is called after the current entry has // been updated in the internal journal but before the new navigation is committed. // This means journal.CurrentEntry is what was just added (or updated). // Note that it would be wrong to call topNavSvc.MakeJournalEntry() in this case because // the navigation that just took place may be in a different NavigationService (in a // frame), and here we don't know which one it is. entry = journal.CurrentEntry; // The entry may be null here when the user has selected "New Window" or pressed Ctrl+N. // In this case the browser calls us on IPersistHistory::Save and then throws that data // away. Hopefully at some point in the future that saved data will be loaded in the new // window via IPersistHistory::Load. This unusual behavior is tracked in bug 1353584. } if (entry != null) { info.title = entry.Name; info.entryId = entry.Id; } else { info.title = _rbw.Value.Title; } // We only use the base URI here because the travel log will validate a file URI when making a PIDL. // We use the URI stored in the JournalEntry, and the travel log doesn't care what the URI is, so // duplicates don't matter. info.uri = BindUriHelper.UriToString(Uri); MemoryStream saveStream = new MemoryStream(); saveStream.Seek(0, SeekOrigin.Begin); if (entireJournal) { //Save the Journal and BaseUri also. We don't need BaseUri except for the xaml case //since this is set specially for the container case (ssres scheme). Exe case //will pretty much set it to the exe path. For the xaml case it is set to the path //of the first uri(eg BaseDir\page1.xaml) that was navigated to. //BaseDir/Subdir/page2.xaml is also considered to be in the same extent and when //we navigate back to the app from a webpage, the baseUri should still be BaseDir //not BaseDir/Subdir. We were setting the BaseDir from JournalEntry.Uri but we may //end up setting BaseUri to BaseDir/Subdir which is not the same. So explicitly //persist BaseUri as well BrowserJournal browserJournal = new BrowserJournal(journal, BindUriHelper.BaseUri); new SecurityPermission(SecurityPermissionFlag.SerializationFormatter).Assert(); try { saveStream.WriteByte(BrowserJournalHeader); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(saveStream, browserJournal); } catch (Exception e) { if (CriticalExceptions.IsCriticalException(e)) { throw; } // The application is shutting down and the exception would not be reported anyway. // This is here to help with debugging and failure analysis. Invariant.Assert(false, "Failed to serialize the navigation journal: " + e); } finally { CodeAccessPermission.RevertAll(); } } else { saveStream.WriteByte(JournalIdHeader); WriteInt32(saveStream, info.entryId); } info.saveByteArray = saveStream.ToArray(); ((IDisposable)saveStream).Dispose(); return(info); }
private object _LoadHistoryStreamDelegate(object arg) { Journal journal = null; JournalEntry entry = null; LoadHistoryStreamInfo info = (LoadHistoryStreamInfo)arg; if (IsShutdown() == true) { return(null); } // Reset the memory stream pointer back to the begining and get the persisted object info.loadStream.Seek(0, System.IO.SeekOrigin.Begin); object journaledObject = DeserializeJournaledObject(info.loadStream); //This is the very first load from history, so need to set the BaseUri and StartupUri. if (info.firstLoadFromHistory) { // The journal does not get saved on Ctrl+N. Because of this, // here we can get just an index, like in the 'else' case below. if (!(journaledObject is BrowserJournal)) { return(null); } BrowserJournal browserJournal = (BrowserJournal)journaledObject; journal = browserJournal.Journal; entry = journal.CurrentEntry; if (entry == null) // See special case in _GetSaveHistoryBytesDelegate(). { entry = journal.GetGoBackEntry(); // could still be null } //This will create the frame to use for hosting { NavigationService navigationService = null; navigationService = _rbw.Value.NavigationService; } _rbw.Value.SetJournalForBrowserInterop(journal); //This should already be set for the container and exe cases. The former //sets it to the transformed ssres scheme and we don't want to overwrite it. if (BindUriHelper.BaseUri == null) { BindUriHelper.BaseUri = browserJournal.BaseUri; } //CHECK: For xaml case, what should we set as the Startup Uri ? We set it the initial //uri we started with, should this be changed to creating the window explicitly //and navigating the window instead of setting the StartupUri? //(Application.Current as Application).StartupUri = entry.Uri; Debug.Assert(Application.Current != null, "BrowserJournalingError: Application object should already be created"); if (entry != null) { //Prevent navigations to StartupUri for history loads by canceling the StartingUp event Application.Current.Startup += new System.Windows.StartupEventHandler(this.OnStartup); _rbw.Value.JournalNavigationScope.NavigateToEntry(entry); } //else: fall back on navigating to StartupUri } else { if (!(journaledObject is int)) { return(null); } journal = _rbw.Value.Journal; int index = journal.FindIndexForEntryWithId((int)journaledObject); Debug.Assert(journal[index].Id == (int)journaledObject, "BrowserJournalingError: Index retrieved from journal stream does not match index of journal entry"); // Check whether the navigation is canceled. if (!_rbw.Value.JournalNavigationScope.NavigateToEntry(index)) { // When the navigation is canceled, we want to notify browser to prevent the internal journal from // getting out of sync with the browser's. // The exception will be caught by the interop layer and browser will cancel the navigation as a result. // If the navigation is initiated pragmatically by calling GoBack/Forward (comparing to user initiated // by clicking the back/forward button), this will result in a managed exception at the call to ibcs.GoBack() // in rbw.GoBackOverride(). rbw catches the exception when this happens. throw new OperationCanceledException(); } } return(null); }
private object _GetSaveHistoryBytesDelegate(object arg) { bool entireJournal = (bool)arg; SaveHistoryReturnInfo info = new SaveHistoryReturnInfo(); // When we are here, the browser has just started to shut down, so we should only check // whether the application object is shutting down. if (Application.IsApplicationObjectShuttingDown == true) return null; Invariant.Assert(_rbw.Value != null, "BrowserJournalingError: _rbw should not be null"); Journal journal = _rbw.Value.Journal; Invariant.Assert(journal != null, "BrowserJournalingError: Could not get internal journal for the window"); JournalEntry entry; if (entireJournal) // The application is about to be shut down... { NavigationService topNavSvc = _rbw.Value.NavigationService; try { topNavSvc.RequestCustomContentStateOnAppShutdown(); } catch(Exception e) { if(CriticalExceptions.IsCriticalException(e)) { throw; } } journal.PruneKeepAliveEntries(); // Since the current page is not added to the journal until it is replaced, // we add it here explicitly to the internal Journal before serializing it. entry = topNavSvc.MakeJournalEntry(JournalReason.NewContentNavigation); if (entry != null && !entry.IsAlive()) { if (entry.JEGroupState.JournalDataStreams != null) { entry.JEGroupState.JournalDataStreams.PrepareForSerialization(); } journal.UpdateCurrentEntry(entry); } else // Maybe the current content is null or a PageFunction doesn't want to be journaled. { // Then the previous navigable page, if any, should be remembered as current. entry = journal.GetGoBackEntry(); // i. _LoadHistoryStreamDelegate() has a similar special case. } } else { // (Brittle) Assumption: GetSaveHistoryBytes() is called after the current entry has // been updated in the internal journal but before the new navigation is committed. // This means journal.CurrentEntry is what was just added (or updated). // Note that it would be wrong to call topNavSvc.MakeJournalEntry() in this case because // the navigation that just took place may be in a different NavigationService (in a // frame), and here we don't know which one it is. entry = journal.CurrentEntry; // The entry may be null here when the user has selected "New Window" or pressed Ctrl+N. // In this case the browser calls us on IPersistHistory::Save and then throws that data // away. Hopefully at some point in the future that saved data will be loaded in the new // window via IPersistHistory::Load. This unusual behavior is tracked in bug 1353584. } if (entry != null) { info.title = entry.Name; info.entryId = entry.Id; } else { info.title = _rbw.Value.Title; } // We only use the base URI here because the travel log will validate a file URI when making a PIDL. // We use the URI stored in the JournalEntry, and the travel log doesn't care what the URI is, so // duplicates don't matter. info.uri = BindUriHelper.UriToString(Uri); MemoryStream saveStream = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); saveStream.Seek(0, SeekOrigin.Begin); object objectToSave = info.entryId; if (entireJournal) { //Save the Journal and BaseUri also. We don't need BaseUri except for the xaml case //since this is set specially for the container case (ssres scheme). Exe case //will pretty much set it to the exe path. For the xaml case it is set to the path //of the first uri(eg BaseDir\page1.xaml) that was navigated to. //BaseDir/Subdir/page2.xaml is also considered to be in the same extent and when //we navigate back to the app from a webpage, the baseUri should still be BaseDir //not BaseDir/Subdir. We were setting the BaseDir from JournalEntry.Uri but we may //end up setting BaseUri to BaseDir/Subdir which is not the same. So explicitly //persist BaseUri as well BrowserJournal browserJournal = new BrowserJournal(journal, BindUriHelper.BaseUri); objectToSave = browserJournal; } new SecurityPermission(SecurityPermissionFlag.SerializationFormatter).Assert(); try { formatter.Serialize(saveStream, objectToSave); } catch(Exception e) { if(CriticalExceptions.IsCriticalException(e)) { throw; } // The application is shutting down and the exception would not be reported anyway. // This is here to help with debugging and failure analysis. Invariant.Assert(false, "Failed to serialize the navigation journal: " + e); } finally { CodeAccessPermission.RevertAll() ; } info.saveByteArray = saveStream.ToArray(); ((IDisposable)saveStream).Dispose(); return info ; }