Registering custom schema handlers.

Having problems with building or using CEF's C/C++ APIs? This forum is here to help. Please do not post bug reports or feature requests here.

Registering custom schema handlers.

Postby denisv » Wed Mar 03, 2021 2:43 pm

Hi, all.

My CEF implementation is built the following way:

On main application thread I call CefInitialize, in settings I set browser_subprocess_path to a different .exe, which I implement as well
In the subprocess .exe I implement a class inheriting from CefApp and CefExecuteProcess, passing an instance of that class.

Now what I've seen in documentation regarding custom schema handlers is a bit confusing.

Now I understood I need to implement a class inheriting CefResourceHandler and implement Open method.
Then I need to implement a class inheriting CefSchemeHandlerFactory and call CefRegisterSchemeHandlerFactory. Create method should return an instance of a class inheriting CefResourceHandler.
But in addition I see that for custom schema I also need to override CefApp::OnRegisterCustomSchemes to register custom scheme.

So the question is, what process do I call all that on?
My CefApp implementation is on subprocess .exe, so should I implement CefApp::OnRegisterCustomSchemes there? In my case it means it will run on renderer process.
On the other hand calling CefRegisterSchemeHandlerFactory makes sense right after CefInitialize, which is on browser process.

Is that how I'm supposed to implement it?

Thanks in advance,
Denis.
denisv
Techie
 
Posts: 12
Joined: Fri Feb 12, 2021 5:02 pm

Re: Registering custom schema handlers.

Postby amaitland » Wed Mar 03, 2021 2:53 pm

CefRegisterSchemeHandlerFactory should only be called in the browser process.

If you are registering a custom scheme then you'll need to register that in browser process and all the sub processes.

If you use the built in http/https scheme then you need only call CefRegisterSchemeHandlerFactory. Using https scheme and calling CefRegisterSchemeHandlerFactory specifying a domainName so only requests for that domain are sent to your scheme handler is the option I recommend.
Maintainer of the CefSharp project.
amaitland
Virtuoso
 
Posts: 1291
Joined: Wed Jan 14, 2015 2:35 am

Re: Registering custom schema handlers.

Postby denisv » Wed Mar 03, 2021 4:14 pm

I'm registering a custom schema. So that means I need to call CefRegisterSchemeHandlerFactory in browser process and also register handlers in all sub processes using CefApp::OnRegisterCustomSchemes?
denisv
Techie
 
Posts: 12
Joined: Fri Feb 12, 2021 5:02 pm

Re: Registering custom schema handlers.

Postby amaitland » Wed Mar 03, 2021 4:29 pm

You need to register the custom scheme in all processes, this includes the browser process.

You only register the scheme handler factory in the browser process.

Registering a scheme handler factory with a domain using the https scheme is much simpler.
Maintainer of the CefSharp project.
amaitland
Virtuoso
 
Posts: 1291
Joined: Wed Jan 14, 2015 2:35 am

Re: Registering custom schema handlers.

Postby denisv » Thu Mar 04, 2021 2:57 am

Much thanks, seems to be working, the handler is being called.

However one thing I've noticed is when I try to invoke the schema, in my case I've implemented a "mailto" schema, like this

Code: Select all
window.location.href = "mailto:mail@example.org";


it calls OnBrowserCreated in my CefRenderProcessHandler class and it seems that browser it passes to it has no main frame. Is that normal behavior for CEF to start a new subprocess when I change location.href? Should I just ignore the call to OnBrowserCreated in such case?

EDIT:

Also for some reason it opens file save dialog, even though my handler for "mailto" schema returns true

Code: Select all
bool TMailScheme::Open(CefRefPtr<CefRequest> request,
               bool& handle_request,
               CefRefPtr<CefCallback> callback)
{
   handle_request = true;
   CefString address= request->GetURL();
   ShellExecute(NULL, L"open", address.c_str(), NULL, NULL, SW_SHOWNORMAL);

   return true;
}


ANOTHER EDIT: Can this be because "mailto" is not a custom schema? When I open the same page in chrome it works just fine, so does that mean I just need to enable the "mailto" schema in CEF without custom handler? However when I remove the registration of SchemeHandlerFactory for "mailto" in my browser process, it crashes when mentioned above JS code is invoked.

Thanks,
Denis.
denisv
Techie
 
Posts: 12
Joined: Fri Feb 12, 2021 5:02 pm

Re: Registering custom schema handlers.

Postby amaitland » Thu Mar 04, 2021 4:10 am

mailto can be handled in OnBeforeBrowse without needing to register a scheme or scheme handler factory.

https://bitbucket.org/chromiumembedded/ ... t-56773936 describes the basic concept.
Maintainer of the CefSharp project.
amaitland
Virtuoso
 
Posts: 1291
Joined: Wed Jan 14, 2015 2:35 am

Re: Registering custom schema handlers.

Postby denisv » Thu Mar 04, 2021 4:37 am

I tried to change it to a different schema, which is custom, but still same thing happens. Handler is running just fine, but then Save File dialog opens.

Implemented the following way:

Code: Select all
class TReportScheme : public CefResourceHandler
{
   virtual bool Open(CefRefPtr<CefRequest> request,
               bool& handle_request,
               CefRefPtr<CefCallback> callback) override;

   virtual void GetResponseHeaders(CefRefPtr<CefResponse> response,
                                  int64& response_length,
                                  CefString& redirectUrl) override;

   virtual void Cancel() override;

   IMPLEMENT_REFCOUNTING(TReportScheme);
};

class SchemeHandlerFactory : public CefSchemeHandlerFactory
{
public:
   virtual CefRefPtr<CefResourceHandler> Create(
      CefRefPtr<CefBrowser> browser,
   CefRefPtr<CefFrame> frame,
   const CefString& scheme_name,
   CefRefPtr<CefRequest> request) override;

   IMPLEMENT_REFCOUNTING(SchemeHandlerFactory);
};

bool TReportScheme::Open(CefRefPtr<CefRequest> request,
               bool& handle_request,
               CefRefPtr<CefCallback> callback)
{
   handle_request = true;
   CefString address= request->GetURL();
   RunReport(address); // Custom function that does something on main app thread

   return true;
}

void TReportScheme::GetResponseHeaders(CefRefPtr<CefResponse> response,
                                  int64& response_length,
                                  CefString& redirectUrl)
{
   response_length = 0;
}

void TReportScheme::Cancel()
{
}

CefRefPtr<CefResourceHandler> SchemeHandlerFactory::Create(
      CefRefPtr<CefBrowser> browser,
   CefRefPtr<CefFrame> frame,
   const CefString& scheme_name,
   CefRefPtr<CefRequest> request)
{
   if(scheme_name == "report")
      return new TReportScheme;

   return NULL;
}


This one runs on both browser and render processes:

Code: Select all
class ClientApp : public CefApp
{
public:
   virtual void OnRegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar) override
   {
      bool s = registrar->AddCustomScheme("report", 0);
   }

   IMPLEMENT_REFCOUNTING(ClientApp);
};

void ClientApp::OnRegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar)
{
   bool s = registrar->AddCustomScheme("report", 0);
}


On browser (main app)

Code: Select all
__declspec(dllexport) void CefCreate()
{
   CefSettings settings;

   LPWSTR path = new TCHAR[1024];
   GetCurrentDirectory( 1024, path);

   settings.multi_threaded_message_loop = true;
   settings.remote_debugging_port = 8088;
   CefString(&settings.browser_subprocess_path) = std::wstring(path) + L"\\OAChrome.exe";
   CefMainArgs main_args;
   CefRefPtr<CefApp> app(new ClientApp);
   CefInitialize(main_args, settings, app, NULL);

   bool s= CefRegisterSchemeHandlerFactory(L"report", L"", new SchemeHandlerFactory);
}




On render (subprocess)

Code: Select all
int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
   UNREFERENCED_PARAMETER(hPrevInstance);
   UNREFERENCED_PARAMETER(lpCmdLine);

   CefMainArgs main_args(hInstance);
   CefRefPtr<CefApp> app(new ClientApp);
    return CefExecuteProcess(main_args, app, NULL);
}
denisv
Techie
 
Posts: 12
Joined: Fri Feb 12, 2021 5:02 pm

Re: Registering custom schema handlers.

Postby denisv » Thu Mar 04, 2021 6:04 am

EDIT:

I've implemented it in OnBeforeBrowse and it works just fine. I execute whatever I need to for both "mailto" and other custom schemes I want to support and then return true to stop the request and it works.

Question is though is it a right and clean way to implement this, I mean the SchemeHandlerFactory class is there for a reason...
denisv
Techie
 
Posts: 12
Joined: Fri Feb 12, 2021 5:02 pm

Re: Registering custom schema handlers.

Postby amaitland » Thu Mar 04, 2021 3:25 pm

Onprotocolexecution was intended specifically for this use case, unfortunately after transitioning to the network service it leaves you with a black page (As described in the issue I linked).

OnBeforeBrowse is the approach I'd recommend for external schemes (ones that will be handled by an external application like mailto).

Schemehandlerfactory is often used to serve content from disk or memory (no network request is made by default).


You've registered your scheme without specifying any options, try using CEF_SCHEME_OPTION_STANDARD
. https://github.com/chromiumembedded/cef ... es.h#L2877

I've never tried to use a custom scheme for mailto.
Maintainer of the CefSharp project.
amaitland
Virtuoso
 
Posts: 1291
Joined: Wed Jan 14, 2015 2:35 am


Return to Support Forum

Who is online

Users browsing this forum: salvadordf and 27 guests