// Copyright © 2013 The CefSharp Authors. All rights reserved. // // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. #pragma once #include "Stdafx.h" #include "include\cef_app.h" #include "include\cef_scheme.h" #include "CefSettingsBase.h" #include "CefSchemeHandlerFactoryAdapter.h" #include "Internals\CefSchemeRegistrarWrapper.h" using namespace CefSharp::Core; namespace CefSharp { namespace Internals { private class CefSharpApp : public CefApp, public CefBrowserProcessHandler { gcroot^> _customSchemes; gcroot _commandLineArgs; gcroot _app; bool _commandLineDisabled; bool _hasCustomScheme; gcroot _customSchemeArg; public: CefSharpApp(bool externalMessagePump, bool commandLineDisabled, CommandLineArgDictionary^ commandLineArgs, IEnumerable^ customSchemes, IApp^ app) : _commandLineDisabled(commandLineDisabled), _commandLineArgs(commandLineArgs), _customSchemes(customSchemes), _app(app), _hasCustomScheme(false) { auto isMissingHandler = Object::ReferenceEquals(app, nullptr) || Object::ReferenceEquals(app->BrowserProcessHandler, nullptr); if (externalMessagePump && isMissingHandler) { throw gcnew Exception("browserProcessHandler cannot be null when using cefSettings.ExternalMessagePump"); } if (System::Linq::Enumerable::Count(customSchemes) > 0) { String^ argument = "="; auto registeredSchemes = gcnew List(); for each (CefCustomScheme ^ scheme in customSchemes) { //We don't need to register http or https in the render process if (scheme->SchemeName == "http" || scheme->SchemeName == "https") { continue; } //We've already registered this scheme name if (registeredSchemes->Contains(scheme->SchemeName)) { continue; } _hasCustomScheme = true; registeredSchemes->Add(scheme->SchemeName); argument += scheme->SchemeName + "|"; argument += ((int)scheme->Options).ToString() + ";"; } if (_hasCustomScheme) { _customSchemeArg = argument->TrimEnd(';'); } } } ~CefSharpApp() { _customSchemes = nullptr; _commandLineArgs = nullptr; delete _app; _app = nullptr; } virtual CefRefPtr GetBrowserProcessHandler() override { return this; } virtual void OnContextInitialized() override { auto customSchemes = (IEnumerable^)_customSchemes; //CefRegisterSchemeHandlerFactory requires access to the Global CefRequestContext for each (CefCustomScheme^ cefCustomScheme in customSchemes) { if (!Object::ReferenceEquals(cefCustomScheme->SchemeHandlerFactory, nullptr)) { auto domainName = cefCustomScheme->DomainName ? cefCustomScheme->DomainName : String::Empty; CefRefPtr wrapper = new CefSchemeHandlerFactoryAdapter(cefCustomScheme->SchemeHandlerFactory); CefRegisterSchemeHandlerFactory(StringUtils::ToNative(cefCustomScheme->SchemeName), StringUtils::ToNative(domainName), wrapper); } } CefSharp::Internals::GlobalContextInitialized::SetResult(true); if (!Object::ReferenceEquals(_app, nullptr) && !Object::ReferenceEquals(_app->BrowserProcessHandler, nullptr)) { _app->BrowserProcessHandler->OnContextInitialized(); } } virtual void OnScheduleMessagePumpWork(int64_t delay_ms) override { //We rely on previous checks to make sure _app and _app->BrowserProcessHandler aren't null _app->BrowserProcessHandler->OnScheduleMessagePumpWork(delay_ms); } virtual bool OnAlreadyRunningAppRelaunch(CefRefPtr commandLine, const CefString& currentDirectory) override { if (Object::ReferenceEquals(_app, nullptr) || Object::ReferenceEquals(_app->BrowserProcessHandler, nullptr)) { return false; } auto managedArgs = gcnew Dictionary(); CefCommandLine::ArgumentList args; commandLine->GetArguments(args); for (auto arg : args) { managedArgs->Add(StringUtils::ToClr(arg), String::Empty); } CefCommandLine::SwitchMap switches; commandLine->GetSwitches(switches); for (auto s : switches) { managedArgs->Add(StringUtils::ToClr(s.first), StringUtils::ToClr(s.second)); } auto readOnlyArgs = gcnew System::Collections::ObjectModel::ReadOnlyDictionary(managedArgs); return _app->BrowserProcessHandler->OnAlreadyRunningAppRelaunch(readOnlyArgs, StringUtils::ToClr(currentDirectory)); } virtual void OnBeforeChildProcessLaunch(CefRefPtr commandLine) override { #ifndef NETCOREAPP if (CefSharpSettings::WcfEnabled) { commandLine->AppendArgument(StringUtils::ToNative(CefSharpArguments::WcfEnabledArgument)); } #endif if (CefSharpSettings::SubprocessExitIfParentProcessClosed) { commandLine->AppendSwitch(StringUtils::ToNative(CefSharpArguments::ExitIfParentProcessClosed)); } //ChannelId was removed in https://github.com/chromiumembedded/cef/issues/1912 //We need to know the process Id to establish WCF communication and for monitoring of parent process exit commandLine->AppendArgument(StringUtils::ToNative(CefSharpArguments::HostProcessIdArgument + "=" + Process::GetCurrentProcess()->Id)); if (_hasCustomScheme) { commandLine->AppendArgument(StringUtils::ToNative(CefSharpArguments::CustomSchemeArgument + _customSchemeArg)); } if (CefSharpSettings::FocusedNodeChangedEnabled) { commandLine->AppendArgument(StringUtils::ToNative(CefSharpArguments::FocusedNodeChangedEnabledArgument)); } } virtual void OnBeforeCommandLineProcessing(const CefString& process_type, CefRefPtr command_line) override { if (CefSharpSettings::Proxy != nullptr && !_commandLineDisabled) { command_line->AppendSwitchWithValue("proxy-server", StringUtils::ToNative(CefSharpSettings::Proxy->IP + ":" + CefSharpSettings::Proxy->Port)); if (!String::IsNullOrEmpty(CefSharpSettings::Proxy->BypassList)) { command_line->AppendSwitchWithValue("proxy-bypass-list", StringUtils::ToNative(CefSharpSettings::Proxy->BypassList)); } } if (_commandLineArgs->Count > 0) { auto commandLine = command_line.get(); // Not clear what should happen if we // * already have some command line flags given (is this possible? Perhaps from globalCommandLine) // * have no flags given (-> call SetProgramm() with first argument?) auto args = (CommandLineArgDictionary^)_commandLineArgs; for each(KeyValuePair^ kvp in args) { CefString name = StringUtils::ToNative(kvp->Key); CefString value = StringUtils::ToNative(kvp->Value); if (kvp->Key == "disable-features" || kvp->Key == "enable-features") { //Temp workaround so we can set the disable-features/enable-features command line argument // See https://github.com/cefsharp/CefSharp/issues/2408 commandLine->AppendSwitchWithValue(name, value); } // Right now the command line args handed to the application (global command line) have higher // precedence than command line args provided by the app else if (!commandLine->HasSwitch(name)) { if (String::IsNullOrEmpty(kvp->Value)) { commandLine->AppendSwitch(name); } else { commandLine->AppendSwitchWithValue(name, value); } } } } } virtual void OnRegisterCustomSchemes(CefRawPtr registrar) override { if (!Object::ReferenceEquals(_app, nullptr)) { CefSchemeRegistrarWrapper wrapper(registrar); _app->OnRegisterCustomSchemes(%wrapper); } }; IMPLEMENT_REFCOUNTINGM(CefSharpApp); }; } }