问题
I have a requirement to open a link in a native browser from CefSharp 3. I need to run the whole application in CefSharp 3's chromium browser except a form. when I click the form's link button (Eg - Register button. It has a link to the registration form) I need to open this link in the native browser (Eg - Internet Explorer).
Can we achieve this in CefSharp?
I searched the google as well as stack overflow. But unable to find a solution.
Thanks in advance.
回答1:
As suggested by holroy I have implemented the OnBeforeNavigation() method in the RequestHandler class in CefSharp.Example package.
This is the working code,
bool IRequestHandler.OnBeforeBrowse(IWebBrowser browserControl,
IBrowser browser, IFrame frame, IRequest request, bool isRedirect)
{
// If the url is Google open Default browser
if (request.Url.Equals("http://google.com/"))
{
// Open Google in Default browser
System.Diagnostics.Process.Start("http://google.com/");
return true;
}else
{
// Url except Google open in CefSharp's Chromium browser
return false;
}
}
I hope this will help to some one else in future.
Thanks,
回答2:
First you need to create a custom BrowserRequestHandler
class such as this:
public class BrowserRequestHandler : IRequestHandler
{
public bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect)
{
// Open in Default browser
if (!request.Url.StartsWith("file:"))
{
System.Diagnostics.Process.Start(request.Url);
return true;
}
return false;
}
public bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl,
WindowOpenDisposition targetDisposition, bool userGesture)
{
return false;
}
public bool OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl,
ISslInfo sslInfo, IRequestCallback callback)
{
callback.Dispose();
return false;
}
public void OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath)
{
throw new Exception("Plugin crashed!");
}
public CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request,
IRequestCallback callback)
{
return CefReturnValue.Continue;
}
public bool GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port,
string realm, string scheme, IAuthCallback callback)
{
callback.Dispose();
return false;
}
public bool OnSelectClientCertificate(IWebBrowser browserControl, IBrowser browser, bool isProxy, string host, int port,
X509Certificate2Collection certificates, ISelectClientCertificateCallback callback)
{
callback.Dispose();
return false;
}
public void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status)
{
throw new Exception("Browser render process is terminated!");
}
public bool OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl, long newSize,
IRequestCallback callback)
{
callback.Dispose();
return false;
}
public void OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response,
ref string newUrl)
{
var url = newUrl;
newUrl = url;
}
public bool OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url)
{
return url.StartsWith("mailto");
}
public void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser)
{
}
public bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)
{
return false;
}
public IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request,
IResponse response)
{
return null;
}
public void OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request,
IResponse response, UrlRequestStatus status, long receivedContentLength)
{
}
}
The important part in this code is:
public bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect)
{
// Open in Default browser
if (!request.Url.StartsWith("file:"))
{
System.Diagnostics.Process.Start(request.Url);
return true;
}
return false;
}
In my case, I was opening locally saved HTML files in CEF, and if those locally saved HTML files had external links, they would open up in the default browser.
Now that you created the custom BrowserRequestHandler
, you need to assign it to the browser.
If you are using MVVM, you can create a BrowserRequestHandler
in your model and assign it to the RequestHandler
dependency property of the browser control. But in my case there were timing issues as I had several browser instances in dynamically opening tabs and browsers were not opening fast enough and throwing errors.
So the second option is to set up a Loaded event using the interactivity namespace like this:
<wpf:ChromiumWebBrowser Address="{Binding Html, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" x:Name="ChromiumWebBrowser">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding BrowserLoadedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</wpf:ChromiumWebBrowser>
After this you can get the browser in your view model (or do this in a manager class), and set its RequestHandler
to your custom BrowserRequestHandler
like this:
browser.RequestHandler = new BrowserRequestHandler();
回答3:
It seems like it possible through use of the OnBeforeNavigation or OnBeforeBrowse events. See following references from "CEF Forum":
- How to have link open in user's default browser
- CEF 3 Open all link targets externally
- Open external browser from link
A suggested implementation of the OnBeforeNavigation method (from Sending information from Chromium Embeded (Javascript) to a containing C++ application):
//declare (i.e. in header)
virtual bool OnBeforeNavigation(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request,
NavigationType navigation_type, bool is_redirect) OVERRIDE;
//implementation
bool CClientApp::OnBeforeNavigation(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request,
NavigationType navigation_type, bool is_redirect)
{
CefString cefval = request->GetURL();
CString csval = cefval.c_str();
if ( /* Match csval against your url/link */ )
{
//process the command here
//this is a command and not really intended for navigation
return true;
}
return false; //true cancels navigation, false allows it
}
Disclaimer: I haven't tried this code myself, but it should do the trick
回答4:
I tried using a RequestHandler as suggested, however I found that while the link was opening correctly in a browser, the ChromiumWebBrowser was still opening an empty Chromium window. Instead of using the RequestHandler, I found that this could be more smoothly implemented using an ILifeSpanHandler - which allows me to catch the popup opening and redirect the process at that point.
Here is the code I used for the ILifeSpanHandler:
public class ChromiumLifeSpanHandler : ILifeSpanHandler
{
public bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
{
return false;
}
public void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser)
{
}
public void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
{
}
public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
{
newBrowser = null;
System.Diagnostics.Process.Start(targetUrl);
return true;
}
}
Here is how I implemented it in WPF (I just stuck it in a grid to have a place to show the static resource):
<Grid>
<Grid.Resources>
<local:ChromiumLifeSpanHandler x:Key="popupHander"/>
</Grid.Resources>
<cef:ChromiumWebBrowser Address="{Binding TwitterFeedAddress}"
LifeSpanHandler="{StaticResource popupHandler}"/>
</Grid>
来源:https://stackoverflow.com/questions/31241240/how-to-open-a-link-in-a-native-browser-from-cefsharp-3