help using command line arguments

Having problems with building or using the JCEF Java binding? Ask your questions here.

help using command line arguments

Postby jcefnewb2 » Wed Feb 27, 2019 2:07 pm

hey everyone, i need to be able to add command line switches to my CEF. Ultimately I need to be able to specify a proxy. For now, I'm trying to use (and modify) a code sample i saw on stackoverflow..

Code: Select all
ArrayList<String> mySwitches = new ArrayList<>();
    mySwitches.add("--persist-session-cookies=true");
    CefApp app = CefApp.getInstance(mySwitches.toArray(new String[mySwitches.size()]));
    CefClient client = app.createClient();
    CefBrowser browser = client.createBrowser("http://www.google.com", false, false);


I tried modifying it to:

Code: Select all
                mySwitches.add("--user-agent=\"Custom1111\"");


i can get the browser to launch/etc, but it seems the command line settings are being ignored. i'm validating it by visiting a webpage i created that echo's back the user agent request to screen. it does work properly if i use a CefSettings object:

Code: Select all
      CefSettings settings = new CefSettings();
                settings.user_agent = "custom11111";
                cefApp_ = CefApp.getInstance(settings);


so i know my server is echo'ing the output correctly.

my problem is i want to setup a proxy, and "proxy-server" doesnt exist within CefSettings. So, I'm assuming I need to pass raw command line settings. i'm just using the user-agent as a way of seeing whether CEF is accepting my arguments for testing/etc (so i can't go with CefSettings long term)

thoughts on how to proceed? thanks!
jcefnewb2
Newbie
 
Posts: 1
Joined: Wed Feb 27, 2019 1:58 pm

Re: help using command line arguments

Postby ndesktop » Thu Feb 28, 2019 1:54 am

EDIT: What follows has been tried only on Windows. You might want to check/change certain includes (and maybe add some specific code, I didn't tried) if you plan to support this also for Mac and Linux.

One way would be to fork CEF and implement a CefProxyHandler new class.
What I did in my projects is this:

- declare proxy type structs and classes
include/internal/cef_types.h
Code: Select all
...
enum cef_proxy_type_t {
  CEF_PROXY_TYPE_DIRECT = 0,
  CEF_PROXY_TYPE_NAMED,
  CEF_PROXY_TYPE_PAC_STRING,
};

typedef struct _cef_proxy_info_t {
  enum cef_proxy_type_t proxyType;
  cef_string_t proxyList;
} cef_proxy_info_t;
...


include/internal/cef_types_wrappers.h
Code: Select all
struct CefProxyInfoTraits {
  typedef cef_proxy_info_t struct_type;

  static inline void init(struct_type* s) {}

  static inline void clear(struct_type* s) {
    cef_string_clear(&s->proxyList);
  }

  static inline void set(const struct_type* src, struct_type* target,
      bool copy) {
    target->proxyType = src->proxyType;
    cef_string_set(src->proxyList.str, src->proxyList.length,
        &target->proxyList, copy);
  }
};

class CefProxyInfo : public CefStructBase<CefProxyInfoTraits> {
 public:
  void UseDirect() {
    proxyType = CEF_PROXY_TYPE_DIRECT;
  }
  void UseNamedProxy(const CefString& proxy_uri_list) {
    proxyType = CEF_PROXY_TYPE_NAMED;
    (CefString(&proxyList)) = proxy_uri_list;
  }
  void UsePacString(const CefString& pac_string) {
    proxyType = CEF_PROXY_TYPE_PAC_STRING;
    (CefString(&proxyList)) = pac_string;
  }

  bool IsDirect() const { return proxyType == CEF_PROXY_TYPE_DIRECT; }
  bool IsNamedProxy() const { return proxyType == CEF_PROXY_TYPE_NAMED; }
  bool IsPacString() const { return proxyType == CEF_PROXY_TYPE_PAC_STRING; }

  CefString ProxyList() const { return CefString(&proxyList); }
};


- add include/cef_proxy_handler.h
Code: Select all
class CefProxyHandler : public virtual CefBaseRefCounted {
 public:
  ///
  // Called to retrieve proxy information for the specified |url|.
  ///
  /*--cef()--*/
  virtual void GetProxyForUrl(const CefString& url,
                              CefProxyInfo& proxy_info) {}
};


- declare WT_ for the new object in libcef/libcef_dll/wrapper_types.h
Code: Select all
enum CefWrapperType {
  WT_BASE_REF_COUNTED = 1,
  WT_BASE_SCOPED,
...
  WT_PRINT_HANDLER,
  WT_PROXY_HANDLER,
  WT_PRINT_JOB_CALLBACK,
...
  WT_LAST
};


- generate/add manually cpptoc and ctocpp files

libcef_dll/cpptoc/proxy_handler_cpptoc.cc
Code: Select all
...
#include "libcef_dll/cpptoc/proxy_handler_cpptoc.h"

// MEMBER FUNCTIONS - Body may be edited by hand.

void CEF_CALLBACK proxy_handler_get_proxy_for_url(
    struct _cef_proxy_handler_t* self, const cef_string_t* url,
    struct _cef_proxy_info_t* proxy_info) {
  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING

  DCHECK(self);
  if (!self)
    return;
  // Verify param: url; type: string_byref_const
  DCHECK(url);
  if (!url)
    return;
  // Verify param: proxy_info; type: struct_byref
  DCHECK(proxy_info);
  if (!proxy_info)
    return;

  // Translate param: proxy_info; type: struct_byref
  CefProxyInfo proxy_infoObj;
  if (proxy_info)
    proxy_infoObj.AttachTo(*proxy_info);

  // Execute
  CefProxyHandlerCppToC::Get(self)->GetProxyForUrl(
      CefString(url),
      proxy_infoObj);

  // Restore param: proxy_info; type: struct_byref
  if (proxy_info)
    proxy_infoObj.DetachTo(*proxy_info);
}


// CONSTRUCTOR - Do not edit by hand.

CefProxyHandlerCppToC::CefProxyHandlerCppToC() {
  GetStruct()->get_proxy_for_url = proxy_handler_get_proxy_for_url;
}

template<>
CefRefPtr<CefProxyHandler> CefCppToCRefCounted<
  CefProxyHandlerCppToC,
  CefProxyHandler,
  cef_proxy_handler_t>::UnwrapDerived(CefWrapperType type,
                                      cef_proxy_handler_t* s) {
  NOTREACHED() << "Unexpected class type: " << type;
  return NULL;
}

#ifndef NDEBUG
template<>
base::AtomicRefCount CefCppToCRefCounted<CefProxyHandlerCppToC,
                                         CefProxyHandler,
                                         cef_proxy_handler_t>::DebugObjCt = 0;
#endif

template<>
CefWrapperType CefCppToCRefCounted<CefProxyHandlerCppToC,
                                   CefProxyHandler,
                                   cef_proxy_handler_t>::kWrapperType =
    WT_PROXY_HANDLER;


libcef_dll/ctocpp/proxy_handler_cpptoc.h
Code: Select all
#ifndef CEF_LIBCEF_DLL_CPPTOC_PROXY_HANDLER_CPPTOC_H_
#define CEF_LIBCEF_DLL_CPPTOC_PROXY_HANDLER_CPPTOC_H_
#pragma once

#if !defined(WRAPPING_CEF_SHARED)
#error This file can be included wrapper-side only
#endif

#include "include/cef_proxy_handler.h"
#include "include/capi/cef_proxy_handler_capi.h"
#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"

// Wrap a C++ class with a C structure.
// This class may be instantiated and accessed wrapper-side only.
class CefProxyHandlerCppToC : public CefCppToCRefCounted<CefProxyHandlerCppToC,
                                                         CefProxyHandler,
                                                         cef_proxy_handler_t> {
 public:
  CefProxyHandlerCppToC();
};

#endif  // CEF_LIBCEF_DLL_CPPTOC_PROXY_HANDLER_CPPTOC_H_


libcef_dll/ctocpp/proxy_handler_ctocpp.cc
Code: Select all
#include "libcef_dll/ctocpp/proxy_handler_ctocpp.h"

// VIRTUAL METHODS - Body may be edited by hand.

void CefProxyHandlerCToCpp::GetProxyForUrl(const CefString& url,
    CefProxyInfo& proxy_info) {
  cef_proxy_handler_t* _struct = GetStruct();
  if (CEF_MEMBER_MISSING(_struct, get_proxy_for_url))
    return;

  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING

  // Verify param: url; type: string_byref_const
  DCHECK(!url.empty());
  if (url.empty())
    return;

  // Execute
  _struct->get_proxy_for_url(_struct,
      url.GetStruct(),
      &proxy_info);
}

// CONSTRUCTOR - Do not edit by hand.

CefProxyHandlerCToCpp::CefProxyHandlerCToCpp() {
}

template<>
cef_proxy_handler_t*
CefCToCppRefCounted<CefProxyHandlerCToCpp, CefProxyHandler, cef_proxy_handler_t>::
  UnwrapDerived(CefWrapperType type, CefProxyHandler* c) {
  NOTREACHED() << "Unexpected class type: " << type;
  return NULL;
}

#if DCHECK_IS_ON()
template<>
base::AtomicRefCount CefCToCppRefCounted<CefProxyHandlerCToCpp,
                                         CefProxyHandler,
                                         cef_proxy_handler_t>::DebugObjCt
    ATOMIC_DECLARATION;
#endif

template<>
CefWrapperType CefCToCppRefCounted<CefProxyHandlerCToCpp,
                                   CefProxyHandler,
                                   cef_proxy_handler_t>::kWrapperType =
    WT_PROXY_HANDLER;


libcef_dll/ctocpp/proxy_handler_ctocpp.h
Code: Select all
class CefProxyHandlerCToCpp : public CefCToCppRefCounted<CefProxyHandlerCToCpp,
                                                         CefProxyHandler,
                                                         cef_proxy_handler_t> {
 public:
  CefProxyHandlerCToCpp();

  // CefProxyHandler methods
  virtual void GetProxyForUrl(const CefString& url,
      CefProxyInfo& proxy_info) override;
};



- modify accordingly app files

libcef_dll/cpptoc/app_ctocpp.cc
Code: Select all
NO_SANITIZE("cfi-icall")
CefRefPtr<CefProxyHandler> CefAppCToCpp::GetProxyHandler() {
  cef_app_t* _struct = GetStruct();
  if (CEF_MEMBER_MISSING(_struct, get_proxy_handler))
    return NULL;

  // Execute
  cef_proxy_handler_t* _retval = _struct->get_proxy_handler(_struct);
 
  // Return type: refptr_same
  return CefProxyHandlerCToCpp::Wrap(_retval);
}


libcef_dll/ctocpp/app_cpptoc.cc
Code: Select all
struct _cef_proxy_handler_t* CEF_CALLBACK app_get_proxy_handler(
    struct _cef_app_t* self) {
  // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING

  DCHECK(self);
  if (!self)
    return NULL;

  // Execute
  CefRefPtr<CefProxyHandler> _retval = CefAppCppToC::Get(self)->GetProxyHandler(
      );

  // Return type: refptr_same
  return CefProxyHandlerCppToC::Wrap(_retval);
}
...
CefAppCppToC::CefAppCppToC() {
...
  GetStruct()->get_proxy_handler = app_get_proxy_handler;
...
}


- modify cef_app.h and add handler
Code: Select all
  virtual CefRefPtr<CefProxyHandler> GetProxyHandler() {
    return NULL;
  }


- modify libcef/browser/net/url_request_context_getter_impl.cc to use proxy:
Code: Select all
...
#include "net/proxy_resolution/proxy_config_service.h"
#include "net/proxy_resolution/proxy_resolver_factory.h"
#include "net/proxy_resolution/proxy_resolver.h"
...

namespace {
class ProxyConfigServiceNull : public net::ProxyConfigService {
 public:
  ProxyConfigServiceNull() {}
  virtual void AddObserver(Observer* observer) override {}
  virtual void RemoveObserver(Observer* observer) override {}
  virtual ProxyConfigService::ConfigAvailability
      GetLatestProxyConfig(net::ProxyConfigWithAnnotation* config) override
      { return ProxyConfigService::CONFIG_VALID; }
  virtual void OnLazyPoll() override {}
};

// An implementation of |HttpUserAgentSettings| that provides a static
class CefProxyResolver
    : public net::ProxyResolver {
 public:
  explicit CefProxyResolver(CefRefPtr<CefProxyHandler> handler)
    : handler_(handler) {}
  virtual ~CefProxyResolver() {}
// HTTP Accept-Language header value and uses |content::GetUserAgent|
  virtual int GetProxyForURL(const GURL& url,
                             net::ProxyInfo* results,
                             const net::CompletionCallback& callback,
                             std::unique_ptr<Request>* request,
                             const net::NetLogWithSource& net_log) override {
    CefProxyInfo proxy_info;
    handler_->GetProxyForUrl(url.spec(), proxy_info);
    if (proxy_info.IsDirect())
      results->UseDirect();
    else if (proxy_info.IsNamedProxy())
      results->UseNamedProxy(proxy_info.ProxyList());
    else if (proxy_info.IsPacString())
      results->UsePacString(proxy_info.ProxyList());
// to provide the HTTP User-Agent header value.
    return net::OK;
  }
 protected:
  CefRefPtr<CefProxyHandler> handler_;
};

class CefProxyResolverFactory
    : public net::ProxyResolverFactory {
 public:
  explicit CefProxyResolverFactory(CefRefPtr<CefProxyHandler> handler)
      : net::ProxyResolverFactory(false)
      , handler_(handler) {
  }

  virtual ~CefProxyResolverFactory() {
  }

  // Creates a new ProxyResolver. The caller is responsible for freeing this
  // object.
  virtual int CreateProxyResolver(
      const scoped_refptr<net::PacFileData>& pac_script,
      std::unique_ptr<net::ProxyResolver>* resolver,
      const net::CompletionCallback& callback,
      std::unique_ptr<Request>* request) override {
      resolver->reset(new CefProxyResolver(handler_));
      return net::OK;
  }

 private:
  CefRefPtr<CefProxyHandler> handler_;
  DISALLOW_COPY_AND_ASSIGN(CefProxyResolverFactory);
};

};  //  namespace

...

net::URLRequestContext* CefURLRequestContextGetterImpl::GetURLRequestContext() {
  CEF_REQUIRE_IOT();

...
    io_state_->storage_->set_ct_policy_enforcer(std::move(ct_policy_enforcer));

// START HERE ==>
    bool fCustomProxyHandler = false;
    if(app.get()) {
        CefRefPtr<CefProxyHandler> handler = app->GetProxyHandler();
        if(handler.get()) {
            // Force auto-detect so the client resolver will be called.
            net::ProxyConfigServiceWin::set_force_auto_detect(true);

            // The client will provide proxy resolution.
            std::unique_ptr<net::ProxyResolutionService> custom_proxy_service(
                new net::ProxyResolutionService(
                    net::ProxyResolutionService::CreateSystemProxyConfigService(
                        io_state_->io_task_runner_),
                    base::WrapUnique(
                        new CefProxyResolverFactory(handler)),
                nullptr));

            io_state_->storage_->set_proxy_resolution_service(
              std::move(custom_proxy_service));
            fCustomProxyHandler = true;
        }
    }
    if(!fCustomProxyHandler) {
        //  custom proxy resolution not provided
        std::unique_ptr<net::ProxyResolutionService> system_proxy_service =
            CreateProxyResolutionService(
                io_state_->net_log_,
                io_state_->url_request_context_.get(),
                io_state_->url_request_context_->network_delegate(),
                std::move(io_state_->proxy_resolver_factory_),
                std::move(io_state_->proxy_config_service_), *command_line,
                quick_check_enabled_.GetValue(),
                pac_https_url_stripping_enabled_.GetValue());
        io_state_->storage_->set_proxy_resolution_service(
            std::move(system_proxy_service));
    }

// <== END HERE
    io_state_->storage_->set_ssl_config_service(
        std::make_unique<net::SSLConfigServiceDefaults>());
...
}
...


- modify cef_paths.gypi to add the new files
Should be enough to add in
Code: Select all
...
    'autogen_client_side': [
...
      'libcef_dll/cpptoc/proxy_handler_cpptoc.cc',
      'libcef_dll/cpptoc/proxy_handler_cpptoc.h',
...
    ],
...


- add it to the objects known to libcef dll:
libcef_dll/libcef_dll.cc
Code: Select all
...
#include "libcef_dll/ctocpp/proxy_handler_ctocpp.h"
...

CEF_EXPORT void cef_shutdown() {
...
  DCHECK(base::AtomicRefCountIsZero(&CefProxyHandlerCToCpp::DebugObjCt));
...
}


- finally, in cefclient the usage should be straightforward
tests/shared/common/client_app.h
Code: Select all
class ClientApp : public CefApp, public CefProxyHandler {
...
CefRefPtr<CefProxyHandler> GetProxyHandler() override;
void GetProxyForUrl(const CefString& url, CefProxyInfo& proxy_info) override;
...
};


tests/shared/common/client_app.cc
Code: Select all
...
CefRefPtr<CefProxyHandler> ClientApp::GetProxyHandler()
{
    return this;
}

void ClientApp::GetProxyForUrl(const CefString& url,
                            CefProxyInfo& proxy_info)
{
  // dummy values
  proxy_info.proxyType = CEF_PROXY_TYPE_NAMED;
  CefString(&proxy_info.proxyList) = "http://my-proxy-server:12345";
}
...


I might have forgotten/skipped something, but that's what I used.
Basically is what CEF1 had once updated for CEF3.
ndesktop
Master
 
Posts: 750
Joined: Thu Dec 03, 2015 10:10 am

Re: help using command line arguments

Postby magreenblatt » Thu Feb 28, 2019 12:27 pm

Forking CEF to fix a problem with setting command-line arguments seems a bit excessive as a first step. I would suggest to instead start by debugging the JCEF code to figure out why the command-line argument isn’t being handled or passed correctly.
magreenblatt
Site Admin
 
Posts: 12382
Joined: Fri May 29, 2009 6:57 pm

Re: help using command line arguments

Postby ndesktop » Thu Feb 28, 2019 12:40 pm

magreenblatt wrote:Forking CEF to fix a problem with setting command-line arguments seems a bit excessive as a first step. I would suggest to instead start by debugging the JCEF code to figure out why the command-line argument isn’t being handled or passed correctly.

True. In my use case the proxy (especially password) should not be passed in command line in clear text (and control per tab and per URL was needed due to specific constraints).
ndesktop
Master
 
Posts: 750
Joined: Thu Dec 03, 2015 10:10 am

Re: help using command line arguments

Postby amaitland » Thu Feb 28, 2019 2:15 pm

You can set a different proxy per CefRequestContext using a call to SetPreference with the relevant key/values (and making sure to execute it on the CEF UI thread).

No idea if JCEF supports this. If you only need a single proxy then command line is easiest option
Maintainer of the CefSharp project.
amaitland
Virtuoso
 
Posts: 1290
Joined: Wed Jan 14, 2015 2:35 am

Re: help using command line arguments

Postby magreenblatt » Thu Feb 28, 2019 5:38 pm

ndesktop wrote:
magreenblatt wrote:Forking CEF to fix a problem with setting command-line arguments seems a bit excessive as a first step. I would suggest to instead start by debugging the JCEF code to figure out why the command-line argument isn’t being handled or passed correctly.

True. In my use case the proxy (especially password) should not be passed in command line in clear text (and control per tab and per URL was needed due to specific constraints).

Just a note, the password cannot be set via command-line. It’s retrieved from CefRequestHandler::GetAuthCredentials with an |isProxy| value of true.
magreenblatt
Site Admin
 
Posts: 12382
Joined: Fri May 29, 2009 6:57 pm


Return to JCEF Forum

Who is online

Users browsing this forum: No registered users and 19 guests