Example #1
0
 private bool IsJournalNavigation(NavigateInfo navInfo)
 {
     return navInfo != null &&
         (navInfo.NavigationMode == NavigationMode.Back || navInfo.NavigationMode == NavigationMode.Forward);
 }
Example #2
0
 /// <summary>
 /// When it is top level navigation away from loose XAML (not the intial navigation to the loose xaml,
 /// nor a refresh), we want to delegate to the browser right away. There is no need to go through our
 /// navigation process, because no matter what content type (xaml, html...) it is trying to navigate to,
 /// we always let the browser handle it when it is top-level navigation from inside XamlViewer.
 ///
 /// V3 SP1 Optimization:
 /// We can avoid the cost of recycling the host process when:
 ///     1) The new content is from the same site-of-origin. This way there is no danger of cross-domain
 ///         attacks made possible by poor cleanup. See SecurityNote below.
 ///     2) We can update the address bar with the new URL, or there is no address bar, which is when
 ///         XamlViewer is hosted in an HTML frame.
 /// </summary>
 /// <remarks>
 /// This function may return false and we still end up delegating to the browser. This will be the
 /// case when the top-level navigation is to something other than XAML. GetObjectFromResponse()
 /// handles this case.
 /// </remarks>
 /// <SecurityNote>
 /// There is no check for same site-of-origin here. A call to SecurityHelper.CallerHasWebPermission(
 /// resolvedUri) could be added, but that would create an additional, redundant code path in
 /// navigation. Here's what happens when the new URI is outside the site-of-origin:
 ///     - For `http://, CreateWebRequest() gets a SecurityException and delegates to the browser.
 ///     - For file://, HandleGetResponse() gets a SecurityException from
 ///         (File)WebRequest.EndGetResponse() and similarly delegates to the browser.
 /// </SecurityNote>
 private bool ShouldDelegateXamlViewerNavigationToBrowser(NavigateInfo navigateInfo, Uri resolvedUri)
 {
     bool shouldDelegate = false;
     if (BrowserInteropHelper.IsViewer)
     {
         Invariant.Assert(resolvedUri != null && resolvedUri.IsAbsoluteUri);
         shouldDelegate = !BrowserInteropHelper.IsInitialViewerNavigation &&
             (navigateInfo == null || navigateInfo.NavigationMode != NavigationMode.Refresh) &&
             IsTopLevelContainer &&
             // except when we can update the address bar or we are in a frame:
             !(!BrowserInteropHelper.IsAvalonTopLevel || HasTravelLogIntegration);
     }
     return shouldDelegate;
 }
Example #3
0
 bool IsConsistent(NavigateInfo navInfo)
 {
     return navInfo == null
         || navInfo.IsConsistent
            && (navInfo.JournalEntry == null || navInfo.JournalEntry.NavigationServiceId == _guidId);
 }
Example #4
0
        private void HandlePageFunction(NavigateInfo navInfo)
        {
            PageFunctionBase ps = (PageFunctionBase)_bp;

            if (IsJournalNavigation(navInfo))
            {
                Debug.Assert(ps._Resume); // should've been set by JournalEntryPFxx.ResumePageFunction()
                ps._Resume = true;
            }

            // 
            if (ps._Resume == false)
            {
                ps.CallStart();
            }
            else
            {
                // 
            }
        }
Example #5
0
        //
        // Create a web-request.
        //      May delegate to the browser for cross-domain case.
        //      Will return null if unable to create a web-request.
        //
        private WebRequest CreateWebRequest(Uri resolvedDestinationUri, NavigateInfo navInfo)
        {
            WebRequest request = null;

            // Ideally we would want to use RegisterPrefix and WebRequest.Create.
            // However, these two functions regress 700k working set in System.dll and System.xml.dll
            //  which is mostly for logging and config.
            // Call PackWebRequestFactory.CreateWebRequest to bypass the regression if possible
            //  by calling Create on PackWebRequest if uri is pack scheme
            try
            {
                request = PackWebRequestFactory.CreateWebRequest(resolvedDestinationUri);
            }
            catch (NotSupportedException)
            {
                LaunchResult launched = LaunchResult.NotLaunched;

                // Not supported exceptions are thrown for mailto: which we want to support.
                // So we detect mailto: here.
                launched = AppSecurityManager.SafeLaunchBrowserOnlyIfPossible(CurrentSource, resolvedDestinationUri, IsTopLevelContainer);

                if (launched == LaunchResult.NotLaunched)
                    throw;
            }
            catch (SecurityException e)
            {
                LaunchResult launched = LaunchResult.NotLaunched;

                // the scenario this code is enabling is navigation to Uri's outside of the app
                // for top-level.
                // So for example at an express app at domain http://www.example.com
                // click on a hyperlink to http://www.msn.com
                // We will get a security exception on the attempt to access msn.
                // So we delegate back to the top-level browser.
                //
                // IMPORTANT: Creating a WebRequest for a file:// URI doesn't fail here if the URI is outside
                // the site of origin. Instead, WebRequest.EndGetResponse() will throw SecurityException.
                // There is a similar case for such URIs there.
                // Callers of this method should not assume that the application has access to the given URI.

                if (e.PermissionType == typeof(System.Net.WebPermission))
                {
                    launched = AppSecurityManager.SafeLaunchBrowserOnlyIfPossible(CurrentSource, resolvedDestinationUri, IsTopLevelContainer);
                }

                if (launched == LaunchResult.NotLaunched)
                    throw;
            }

            bool isRefresh = navInfo == null ? false : navInfo.NavigationMode == NavigationMode.Refresh;
            WpfWebRequestHelper.ConfigCachePolicy(request, isRefresh);

            return request;
        }
Example #6
0
        /// <returns> whether to continue with committing the navigation to the new content </returns>
        private bool OnBeforeSwitchContent(Object newBP, NavigateInfo navInfo, Uri newUri)
        {
            Debug.Assert(IsConsistent(navInfo));

#if DEBUG_CLR_MEM
            bool clrTracingEnabled = false;

            if (CLRProfilerControl.ProcessIsUnderCLRProfiler &&
               (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Verbose))
            {
                clrTracingEnabled = true;
                ++_navigationCLRPass;
                CLRProfilerControl.CLRLogWriteLine("Begin_OnBeforeSwitchContent_{0}", _navigationCLRPass);
            }
#endif // DEBUG_CLR_MEM

            // The order of those actions are:
            // 1. Config the new tree, which includes two steps: PageFunction related stuff (where Child PageFunction's Return event is fired)
            //    and setting NavigationServiceProperty to this.
            // 2. Journal is updated with current page
            // 3. Clean up the old tree, which includes three steps: setting NavigationServiceProperty to null, setting focus to null, and
            //    PageFunction related stuff.
            // 4. Dispose the old tree if it can be disposed.
            // We intentionally fires the PageFunction Return event at the beginning for exception continuality: if an exception occurs in
            // the event handler, we would maintain a clean state.
            if (newBP != null && !HookupNewTree(newBP, navInfo, newUri))
            {
                Debug.Assert(!JournalScope.Journal.HasUncommittedNavigation);
                return false;
            }

            Debug.Assert(_navigateQueueItem == null);

            // Workaround for the reentrance problem from browser (bug 128689).
            // Call into browser before we update journal. If there is another navigation waiting, e.g,
            // user starts a new navigation using the browser back/forward button, it will
            // re-enter with this call. We can detect whether a new navigation has started by checking
            // _navigateQueueItem. The goal is to check for reentrance before we update journal. It should
            // be safe to cancel the current navigation at this point (before any journal changes).
            if (HasTravelLogIntegration)
            {
                DispatchPendingCallFromBrowser();
                if (_navigateQueueItem != null)
                {
                    return false;
                }
            }

            if (navInfo == null)
            {
                UpdateJournal(NavigationMode.New, JournalReason.NewContentNavigation, null);
            }
            else if (navInfo.NavigationMode != NavigationMode.Refresh)
            {
                UpdateJournal(navInfo.NavigationMode, JournalReason.NewContentNavigation, navInfo.JournalEntry);
            }

            // Check for reentrance again before we proceed. UpdateJournal calls CallUpdateTravelLog which calls
            // into browser that can cause a new navigation to reenter.
            // 











            if (_navigateQueueItem != null)
            {
                return false;
            }

            bool canDispose = UnhookOldTree(_bp);

#if DEBUG_CLR_MEM
            if (clrTracingEnabled && CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Verbose)
            {
                CLRProfilerControl.CLRLogWriteLine("End_OnBeforeSwitchContent_{0}", _navigationCLRPass);
            }
#endif // DEBUG_CLR_MEM

            //
            // Dispose the old tree after all the required work is done.
            //
            if (canDispose)
            {
                DisposeTreeQueueItem disposeItem = new DisposeTreeQueueItem(_bp);
                Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(disposeItem.Dispatch), null);
            }

            return true;
        }
Example #7
0
        /// <returns> False, if the navigation is canceled. This can currently happen only when
        /// a PageFunction returns to a non-PF parent page, and the Return event handler starts a
        /// new navigation. This case should be handled consistently with HandleFinish().
        /// </returns>
        private bool HookupNewTree(Object newTree, NavigateInfo navInfo, Uri newUri)
        {
            Debug.Assert(_navigateQueueItem == null && _navStatus == NavigationStatus.Navigated);

            // Restore the page state
            if (newTree != null && IsJournalNavigation(navInfo))
            {
                navInfo.JournalEntry.RestoreState(newTree);
                // Note: When a PageFunction is being resumed because its child finished, RestoreState()
                // is called earlier. Because it clears the JournalDataStreams, the call here will do
                // nothing.

                // Note: journalEntry.CustomContentState.Replay() is called from HandleNavigated().
            }

            //--------------------------------------------------------------------------------
            //
            // Step 1: Do PageFunction related stuff
            // This step is intentionally put as the first for event handler exception continuality:
            // if an exception occurs in the Return event handler, we would maintain a clean state.

            PageFunctionReturnInfo pfReturnInfo = navInfo as PageFunctionReturnInfo;
            // This will be non-null IFF a PageFunction with a non-PageFunction parent has finished.
            // Then navInfo.NavigationMode may be Back or New.
            // (New iff finishingChildPageFunction.RemoveFromJournal==false).
            PageFunctionBase finishingChildPageFunction = (pfReturnInfo != null) ? pfReturnInfo.FinishingChildPageFunction : null;
            Debug.Assert(finishingChildPageFunction == null ||
                !IsPageFunction(newTree) &&
                (finishingChildPageFunction.RemoveFromJournal && navInfo.NavigationMode == NavigationMode.Back ||
                 !finishingChildPageFunction.RemoveFromJournal && navInfo.NavigationMode == NavigationMode.New));

            // Reattach the Return Event handler and fire the child PageFunction's Return event
            // if we are about to switch to the non-PageFunction parent of a PageFunction that
            // has just finished
            if (finishingChildPageFunction != null)
            {
                object returnEventArgs = (pfReturnInfo != null) ? pfReturnInfo.ReturnEventArgs : null;

                if (newTree != null)
                {
                    FireChildPageFunctionReturnEvent(newTree, finishingChildPageFunction, returnEventArgs);

                    if (_navigateQueueItem != null)
                    {
                        // Return event handler should not be left attached.
                        Debug.Assert(finishingChildPageFunction._Return == null);

                        if (pfReturnInfo.JournalEntry != null)
                        {
                            pfReturnInfo.JournalEntry.SaveState(newTree);
                        }
                        return false;
                    }
                }
                // else
                // {
                //      





            }

            // Note this special case: finishingChildPageFunction=null, but Content is PageFunctionBase.
            // This happens when navigating to a PF and then doing GoBack. Then the special
            // OnReturn/OnFinish PF handling is not done.

            if (IsPageFunction(newTree))
            {
                // Attach the handler to the new one so we know when it Finishes
                SetupPageFunctionHandlers(newTree);

                // If a page function is started without attaching a Return event handler to it,
                // it doesn't know which parent page to return to. So, set it here in this case.
                // (See also PageFunctionBase._AddEventHandler().)
                if ((navInfo == null || navInfo.NavigationMode == NavigationMode.New)
                    && !_doNotJournalCurrentContent) // the current PF may have been RemoveFromJournal'ed
                {
                    Debug.Assert(pfReturnInfo == null);
                    PageFunctionBase pf = (PageFunctionBase)newTree;
                    // pf._Resume=true when a PF returns and recording a new navigation for the parent PF
                    if (!pf._Resume && pf.ParentPageFunctionId == Guid.Empty && _bp is PageFunctionBase)
                    {
                        pf.ParentPageFunctionId = ((PageFunctionBase)_bp).PageFunctionId;
                        Debug.Assert(pf.ParentPageFunctionId != Guid.Empty);
                    }
                }
            }
            //
            //--------------------------------------------------------------------------------

            //--------------------------------------------------------------------------------
            //
            // Step 2: Set NavigationService property and WebBrowser
            //
            DependencyObject dobj = newTree as DependencyObject;
            if ((dobj != null) && (! dobj.IsSealed))
            {
                // Note: setting NavigationService has a non-obvious side effect -
                // if dobj has any data-bound properties that use ElementName binding,
                // the name will be resolved in the "inner scope", not the "outer
                // scope".  (Bug 1765041)
                dobj.SetValue(NavigationServiceProperty, this);

                // Set BaseUriHelper.BaseUriProperty.
                // Special case: When returning to a Source-less element tree in which fragment
                // navigation was done, newUri will be just "#fragment". Don't set it then.
                if (newUri != null && !BindUriHelper.StartWithFragment(newUri))
                {
                    SetBaseUri(dobj, newUri);
                }
            }

            _webBrowser = newTree as WebBrowser;
            //
            //--------------------------------------------------------------------------------

            return true;
        }