Files
UnrealEngineUWP/Engine/Source/Runtime/WebBrowser/Private/WebBrowserSingleton.cpp
Josh Adams d0bf843c9c - Merging Dev-Kairos/Engine/... to Main/Engine/...
- Brings over the necessary engine changes for embedding UE4 mobile as a dylib/so in native mobile app
- Various changes for facial animation, screen recording, others
- ARKit and ARCore plugins were removed, as deemed "not ready"
#rb many people


#ROBOMERGE-OWNER: josh.adams
#ROBOMERGE-AUTHOR: josh.adams
#ROBOMERGE-SOURCE: CL 5201138 via CL 5203024

[CL 5226277 by Josh Adams in Main branch]
2019-02-27 11:57:17 -05:00

790 lines
25 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "WebBrowserSingleton.h"
#include "Misc/Paths.h"
#include "GenericPlatform/GenericPlatformFile.h"
#include "Misc/CommandLine.h"
#include "Misc/ConfigCacheIni.h"
#include "Internationalization/Culture.h"
#include "Misc/App.h"
#include "WebBrowserModule.h"
#include "Misc/EngineVersion.h"
#include "Framework/Application/SlateApplication.h"
#include "IWebBrowserCookieManager.h"
#include "WebBrowserLog.h"
#if PLATFORM_WINDOWS
#include "Windows/WindowsHWrapper.h"
#endif
#if WITH_CEF3
#include "Misc/ScopeLock.h"
#include "CEF/CEFBrowserApp.h"
#include "CEF/CEFBrowserHandler.h"
#include "CEF/CEFWebBrowserWindow.h"
#include "CEF/CEFSchemeHandler.h"
# if PLATFORM_WINDOWS
# include "Windows/AllowWindowsPlatformTypes.h"
# endif
# pragma push_macro("OVERRIDE")
# undef OVERRIDE // cef headers provide their own OVERRIDE macro
THIRD_PARTY_INCLUDES_START
#if PLATFORM_APPLE
PRAGMA_DISABLE_DEPRECATION_WARNINGS
#endif
# include "include/cef_app.h"
#if PLATFORM_APPLE
PRAGMA_ENABLE_DEPRECATION_WARNINGS
#endif
THIRD_PARTY_INCLUDES_END
# pragma pop_macro("OVERRIDE")
# if PLATFORM_WINDOWS
# include "Windows/HideWindowsPlatformTypes.h"
# endif
#endif
#if BUILD_EMBEDDED_APP
# include "Native/NativeWebBrowserProxy.h"
#endif
#if PLATFORM_ANDROID && USE_ANDROID_JNI
# include "Android/AndroidWebBrowserWindow.h"
# include <Android/AndroidCookieManager.h>
#elif PLATFORM_IOS
# include <IOS/IOSPlatformWebBrowser.h>
# include <IOS/IOSCookieManager.h>
#elif PLATFORM_PS4
# include <PS4/PS4PlatformWebBrowser.h>
#endif
// Define some platform-dependent file locations
#if WITH_CEF3
# define CEF3_BIN_DIR TEXT("Binaries/ThirdParty/CEF3")
# if PLATFORM_WINDOWS && PLATFORM_64BITS
# define CEF3_RESOURCES_DIR CEF3_BIN_DIR TEXT("/Win64/Resources")
# define CEF3_SUBPROCES_EXE TEXT("Binaries/Win64/UnrealCEFSubProcess.exe")
# elif PLATFORM_WINDOWS && PLATFORM_32BITS
# define CEF3_RESOURCES_DIR CEF3_BIN_DIR TEXT("/Win32/Resources")
# define CEF3_SUBPROCES_EXE TEXT("Binaries/Win32/UnrealCEFSubProcess.exe")
# elif PLATFORM_MAC
# define CEF3_FRAMEWORK_DIR CEF3_BIN_DIR TEXT("/Mac/Chromium Embedded Framework.framework")
# define CEF3_RESOURCES_DIR CEF3_FRAMEWORK_DIR TEXT("/Resources")
# define CEF3_SUBPROCES_EXE TEXT("Binaries/Mac/UnrealCEFSubProcess.app/Contents/MacOS/UnrealCEFSubProcess")
# elif PLATFORM_LINUX // @todo Linux
# define CEF3_RESOURCES_DIR CEF3_BIN_DIR TEXT("/Linux/Resources")
# define CEF3_SUBPROCES_EXE TEXT("Binaries/Linux/UnrealCEFSubProcess")
# endif
// Caching is enabled by default.
# ifndef CEF3_DEFAULT_CACHE
# define CEF3_DEFAULT_CACHE 1
# endif
#endif
namespace {
/**
* Helper function to set the current thread name, visible by the debugger.
* @param ThreadName Name to set
*/
void SetCurrentThreadName(char* ThreadName)
{
#if PLATFORM_MAC
pthread_setname_np(ThreadName);
#elif PLATFORM_LINUX
pthread_setname_np(pthread_self(), ThreadName);
#elif PLATFORM_WINDOWS && !PLATFORM_SEH_EXCEPTIONS_DISABLED
/**
* Code setting the thread name for use in the debugger.
* Copied implementation from WindowsRunnableThread as it is private.
*
* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
*/
const uint32 MS_VC_EXCEPTION=0x406D1388;
struct THREADNAME_INFO
{
uint32 dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
uint32 dwThreadID; // Thread ID (-1=caller thread).
uint32 dwFlags; // Reserved for future use, must be zero.
};
THREADNAME_INFO ThreadNameInfo = {0x1000, ThreadName, -1, 0};
__try
{
RaiseException( MS_VC_EXCEPTION, 0, sizeof(ThreadNameInfo)/sizeof(ULONG_PTR), (ULONG_PTR*)&ThreadNameInfo );
}
__except( EXCEPTION_EXECUTE_HANDLER )
CA_SUPPRESS(6322)
{
}
#endif
}
}
FString FWebBrowserSingleton::ApplicationCacheDir() const
{
#if PLATFORM_MAC
// OSX wants caches in a separate location from other app data
static TCHAR Result[MAC_MAX_PATH] = TEXT("");
if (!Result[0])
{
SCOPED_AUTORELEASE_POOL;
NSString *CacheBaseDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex: 0];
NSString* BundleID = [[NSBundle mainBundle] bundleIdentifier];
if(!BundleID)
{
BundleID = [[NSProcessInfo processInfo] processName];
}
check(BundleID);
NSString* AppCacheDir = [CacheBaseDir stringByAppendingPathComponent: BundleID];
FPlatformString::CFStringToTCHAR((CFStringRef)AppCacheDir, Result);
}
return FString(Result);
#else
// Other platforms use the application data directory
return FPaths::ProjectSavedDir();
#endif
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
class FWebBrowserWindowFactory
: public IWebBrowserWindowFactory
{
public:
virtual ~FWebBrowserWindowFactory()
{ }
virtual TSharedPtr<IWebBrowserWindow> Create(
TSharedPtr<FCEFWebBrowserWindow>& BrowserWindowParent,
TSharedPtr<FWebBrowserWindowInfo>& BrowserWindowInfo) override
{
return IWebBrowserModule::Get().GetSingleton()->CreateBrowserWindow(
BrowserWindowParent,
BrowserWindowInfo);
}
virtual TSharedPtr<IWebBrowserWindow> Create(
void* OSWindowHandle,
FString InitialURL,
bool bUseTransparency,
bool bThumbMouseButtonNavigation,
TOptional<FString> ContentsToLoad = TOptional<FString>(),
bool ShowErrorMessage = true,
FColor BackgroundColor = FColor(255, 255, 255, 255)) override
{
return IWebBrowserModule::Get().GetSingleton()->CreateBrowserWindow(
OSWindowHandle,
InitialURL,
bUseTransparency,
bThumbMouseButtonNavigation,
ContentsToLoad,
ShowErrorMessage,
BackgroundColor);
}
};
PRAGMA_ENABLE_DEPRECATION_WARNINGS
class FNoWebBrowserWindowFactory
: public IWebBrowserWindowFactory
{
public:
virtual ~FNoWebBrowserWindowFactory()
{ }
virtual TSharedPtr<IWebBrowserWindow> Create(
TSharedPtr<FCEFWebBrowserWindow>& BrowserWindowParent,
TSharedPtr<FWebBrowserWindowInfo>& BrowserWindowInfo) override
{
return nullptr;
}
virtual TSharedPtr<IWebBrowserWindow> Create(
void* OSWindowHandle,
FString InitialURL,
bool bUseTransparency,
bool bThumbMouseButtonNavigation,
TOptional<FString> ContentsToLoad = TOptional<FString>(),
bool ShowErrorMessage = true,
FColor BackgroundColor = FColor(255, 255, 255, 255)) override
{
return nullptr;
}
};
FWebBrowserSingleton::FWebBrowserSingleton(const FWebBrowserInitSettings& WebBrowserInitSettings)
#if WITH_CEF3
: WebBrowserWindowFactory(MakeShareable(new FWebBrowserWindowFactory()))
#else
: WebBrowserWindowFactory(MakeShareable(new FNoWebBrowserWindowFactory()))
#endif
, bDevToolsShortcutEnabled(UE_BUILD_DEBUG)
, bJSBindingsToLoweringEnabled(true)
, DefaultMaterial(nullptr)
, DefaultTranslucentMaterial(nullptr)
{
#if WITH_CEF3
// The FWebBrowserSingleton must be initialized on the game thread
check(IsInGameThread());
// Provide CEF with command-line arguments.
#if PLATFORM_WINDOWS
CefMainArgs MainArgs(hInstance);
#else
CefMainArgs MainArgs;
#endif
bool bVerboseLogging = FParse::Param(FCommandLine::Get(), TEXT("cefverbose")) || FParse::Param(FCommandLine::Get(), TEXT("debuglog"));
// CEFBrowserApp implements application-level callbacks.
CEFBrowserApp = new FCEFBrowserApp;
CEFBrowserApp->OnRenderProcessThreadCreated().BindRaw(this, &FWebBrowserSingleton::HandleRenderProcessCreated);
// Specify CEF global settings here.
CefSettings Settings;
Settings.no_sandbox = true;
Settings.command_line_args_disabled = true;
#if !PLATFORM_LINUX
Settings.enable_net_security_expiration = true;
Settings.external_message_pump = true;
#endif
//@todo change to threaded version instead of using external_message_pump & OnScheduleMessagePumpWork
Settings.multi_threaded_message_loop = false;
FString CefLogFile(FPaths::Combine(*FPaths::ProjectLogDir(), TEXT("cef3.log")));
CefLogFile = FPaths::ConvertRelativePathToFull(CefLogFile);
CefString(&Settings.log_file) = TCHAR_TO_WCHAR(*CefLogFile);
Settings.log_severity = bVerboseLogging ? LOGSEVERITY_VERBOSE : LOGSEVERITY_WARNING;
uint16 DebugPort;
if(FParse::Value(FCommandLine::Get(), TEXT("cefdebug="), DebugPort))
{
Settings.remote_debugging_port = DebugPort;
}
// Specify locale from our settings
FString LocaleCode = GetCurrentLocaleCode();
CefString(&Settings.locale) = TCHAR_TO_WCHAR(*LocaleCode);
// Append engine version to the user agent string.
CefString(&Settings.product_version) = TCHAR_TO_WCHAR(*WebBrowserInitSettings.ProductVersion);
#if CEF3_DEFAULT_CACHE
// Enable on disk cache
FString CachePath(FPaths::Combine(ApplicationCacheDir(), TEXT("webcache")));
CachePath = FPaths::ConvertRelativePathToFull(CachePath);
CefString(&Settings.cache_path) = TCHAR_TO_WCHAR(*CachePath);
#endif
// Specify path to resources
FString ResourcesPath(FPaths::Combine(*FPaths::EngineDir(), CEF3_RESOURCES_DIR));
ResourcesPath = FPaths::ConvertRelativePathToFull(ResourcesPath);
if (!FPaths::DirectoryExists(ResourcesPath))
{
UE_LOG(LogWebBrowser, Error, TEXT("Chromium Resources information not found at: %s."), *ResourcesPath);
}
CefString(&Settings.resources_dir_path) = TCHAR_TO_WCHAR(*ResourcesPath);
#if !PLATFORM_MAC
// On Mac Chromium ignores custom locales dir. Files need to be stored in Resources folder in the app bundle
FString LocalesPath(FPaths::Combine(*ResourcesPath, TEXT("locales")));
LocalesPath = FPaths::ConvertRelativePathToFull(LocalesPath);
if (!FPaths::DirectoryExists(LocalesPath))
{
UE_LOG(LogWebBrowser, Error, TEXT("Chromium Locales information not found at: %s."), *LocalesPath);
}
CefString(&Settings.locales_dir_path) = TCHAR_TO_WCHAR(*LocalesPath);
#endif
// Specify path to sub process exe
FString SubProcessPath(FPaths::Combine(*FPaths::EngineDir(), CEF3_SUBPROCES_EXE));
SubProcessPath = FPaths::ConvertRelativePathToFull(SubProcessPath);
if (!IPlatformFile::GetPlatformPhysical().FileExists(*SubProcessPath))
{
UE_LOG(LogWebBrowser, Error, TEXT("UnrealCEFSubProcess.exe not found, check that this program has been built and is placed in: %s."), *SubProcessPath);
}
CefString(&Settings.browser_subprocess_path) = TCHAR_TO_WCHAR(*SubProcessPath);
// Initialize CEF.
bool bSuccess = CefInitialize(MainArgs, Settings, CEFBrowserApp.get(), nullptr);
check(bSuccess);
// Set the thread name back to GameThread.
SetCurrentThreadName(TCHAR_TO_ANSI( *(FName( NAME_GameThread ).GetPlainNameString()) ));
DefaultCookieManager = FCefWebBrowserCookieManagerFactory::Create(CefCookieManager::GetGlobalManager(nullptr));
#elif PLATFORM_IOS && !BUILD_EMBEDDED_APP
DefaultCookieManager = MakeShareable(new FIOSCookieManager());
#elif PLATFORM_ANDROID
DefaultCookieManager = MakeShareable(new FAndroidCookieManager());
#endif
}
#if WITH_CEF3
void FWebBrowserSingleton::HandleRenderProcessCreated(CefRefPtr<CefListValue> ExtraInfo)
{
FScopeLock Lock(&WindowInterfacesCS);
for (int32 Index = WindowInterfaces.Num() - 1; Index >= 0; --Index)
{
TSharedPtr<FCEFWebBrowserWindow> BrowserWindow = WindowInterfaces[Index].Pin();
if (BrowserWindow.IsValid())
{
CefRefPtr<CefDictionaryValue> Bindings = BrowserWindow->GetProcessInfo();
if (Bindings.get())
{
ExtraInfo->SetDictionary(ExtraInfo->GetSize(), Bindings);
}
}
}
}
#endif
FWebBrowserSingleton::~FWebBrowserSingleton()
{
#if WITH_CEF3
{
FScopeLock Lock(&WindowInterfacesCS);
// Force all existing browsers to close in case any haven't been deleted
for (int32 Index = 0; Index < WindowInterfaces.Num(); ++Index)
{
auto BrowserWindow = WindowInterfaces[Index].Pin();
if (BrowserWindow.IsValid() && BrowserWindow->IsValid())
{
// Call CloseBrowser directly on the Host object as FWebBrowserWindow::CloseBrowser is delayed
BrowserWindow->InternalCefBrowser->GetHost()->CloseBrowser(true);
}
}
// Clear this before CefShutdown() below
WindowInterfaces.Reset();
}
// Remove references to the scheme handler factories
CefClearSchemeHandlerFactories();
for (const TPair<FString, CefRefPtr<CefRequestContext>>& RequestContextPair : RequestContexts)
{
RequestContextPair.Value->ClearSchemeHandlerFactories();
}
// Clear this before CefShutdown() below
RequestContexts.Reset();
// Just in case, although we deallocate CEFBrowserApp right after this.
CEFBrowserApp->OnRenderProcessThreadCreated().Unbind();
// CefRefPtr takes care of delete
CEFBrowserApp = nullptr;
// Shut down CEF.
CefShutdown();
#elif PLATFORM_IOS || PLATFORM_PS4 || (PLATFORM_ANDROID && USE_ANDROID_JNI)
{
FScopeLock Lock(&WindowInterfacesCS);
// Clear this before CefShutdown() below
WindowInterfaces.Reset();
}
#endif
}
TSharedRef<IWebBrowserWindowFactory> FWebBrowserSingleton::GetWebBrowserWindowFactory() const
{
return WebBrowserWindowFactory;
}
TSharedPtr<IWebBrowserWindow> FWebBrowserSingleton::CreateBrowserWindow(
TSharedPtr<FCEFWebBrowserWindow>& BrowserWindowParent,
TSharedPtr<FWebBrowserWindowInfo>& BrowserWindowInfo
)
{
#if WITH_CEF3
TOptional<FString> ContentsToLoad;
bool bShowErrorMessage = BrowserWindowParent->IsShowingErrorMessages();
bool bThumbMouseButtonNavigation = BrowserWindowParent->IsThumbMouseButtonNavigationEnabled();
bool bUseTransparency = BrowserWindowParent->UseTransparency();
FString InitialURL = WCHAR_TO_TCHAR(BrowserWindowInfo->Browser->GetMainFrame()->GetURL().ToWString().c_str());
TSharedPtr<FCEFWebBrowserWindow> NewBrowserWindow(new FCEFWebBrowserWindow(BrowserWindowInfo->Browser, BrowserWindowInfo->Handler, InitialURL, ContentsToLoad, bShowErrorMessage, bThumbMouseButtonNavigation, bUseTransparency, bJSBindingsToLoweringEnabled));
BrowserWindowInfo->Handler->SetBrowserWindow(NewBrowserWindow);
{
FScopeLock Lock(&WindowInterfacesCS);
WindowInterfaces.Add(NewBrowserWindow);
}
return NewBrowserWindow;
#endif
return nullptr;
}
TSharedPtr<IWebBrowserWindow> FWebBrowserSingleton::CreateBrowserWindow(
void* OSWindowHandle,
FString InitialURL,
bool bUseTransparency,
bool bThumbMouseButtonNavigation,
TOptional<FString> ContentsToLoad,
bool ShowErrorMessage,
FColor BackgroundColor,
int BrowserFrameRate,
const TArray<FString>& AltRetryDomains)
{
FCreateBrowserWindowSettings Settings;
Settings.OSWindowHandle = OSWindowHandle;
Settings.InitialURL = InitialURL;
Settings.bUseTransparency = bUseTransparency;
Settings.bThumbMouseButtonNavigation = bThumbMouseButtonNavigation;
Settings.ContentsToLoad = ContentsToLoad;
Settings.bShowErrorMessage = ShowErrorMessage;
Settings.BackgroundColor = BackgroundColor;
Settings.BrowserFrameRate = BrowserFrameRate;
Settings.AltRetryDomains = AltRetryDomains;
return CreateBrowserWindow(Settings);
}
TSharedPtr<IWebBrowserWindow> FWebBrowserSingleton::CreateBrowserWindow(const FCreateBrowserWindowSettings& WindowSettings)
{
bool bBrowserEnabled = true;
GConfig->GetBool(TEXT("Browser"), TEXT("bEnabled"), bBrowserEnabled, GEngineIni);
if (!bBrowserEnabled || !FApp::CanEverRender())
{
return nullptr;
}
#if WITH_CEF3
static bool AllowCEF = !FParse::Param(FCommandLine::Get(), TEXT("nocef"));
if (AllowCEF)
{
// Information used when creating the native window.
CefWindowInfo WindowInfo;
// Specify CEF browser settings here.
CefBrowserSettings BrowserSettings;
// Set max framerate to maximum supported.
BrowserSettings.background_color = CefColorSetARGB(WindowSettings.bUseTransparency ? 0 : WindowSettings.BackgroundColor.A, WindowSettings.BackgroundColor.R, WindowSettings.BackgroundColor.G, WindowSettings.BackgroundColor.B);
// Disable plugins
BrowserSettings.plugins = STATE_DISABLED;
#if PLATFORM_WINDOWS
// Create the widget as a child window on windows when passing in a parent window
if (WindowSettings.OSWindowHandle != nullptr)
{
RECT ClientRect = { 0, 0, 0, 0 };
WindowInfo.SetAsChild((CefWindowHandle)WindowSettings.OSWindowHandle, ClientRect);
}
else
#endif
{
// Use off screen rendering so we can integrate with our windows
#if PLATFORM_LINUX
WindowInfo.SetAsWindowless(kNullWindowHandle, WindowSettings.bUseTransparency);
#else
WindowInfo.SetAsWindowless(kNullWindowHandle);
#endif
BrowserSettings.windowless_frame_rate = WindowSettings.BrowserFrameRate;
}
// WebBrowserHandler implements browser-level callbacks.
CefRefPtr<FCEFBrowserHandler> NewHandler(new FCEFBrowserHandler(WindowSettings.bUseTransparency, WindowSettings.AltRetryDomains));
CefRefPtr<CefRequestContext> RequestContext = nullptr;
if (WindowSettings.Context.IsSet())
{
const FBrowserContextSettings Context = WindowSettings.Context.GetValue();
const CefRefPtr<CefRequestContext>* ExistingRequestContext = RequestContexts.Find(Context.Id);
if (ExistingRequestContext == nullptr)
{
CefRequestContextSettings RequestContextSettings;
CefString(&RequestContextSettings.accept_language_list) = TCHAR_TO_WCHAR(*Context.AcceptLanguageList);
CefString(&RequestContextSettings.cache_path) = TCHAR_TO_WCHAR(*Context.CookieStorageLocation);
RequestContextSettings.persist_session_cookies = Context.bPersistSessionCookies;
RequestContextSettings.ignore_certificate_errors = Context.bIgnoreCertificateErrors;
#if !PLATFORM_LINUX
RequestContextSettings.enable_net_security_expiration = Context.bEnableNetSecurityExpiration;
#endif
//Create a new one
RequestContext = CefRequestContext::CreateContext(RequestContextSettings, nullptr);
RequestContexts.Add(Context.Id, RequestContext);
}
else
{
RequestContext = *ExistingRequestContext;
}
SchemeHandlerFactories.RegisterFactoriesWith(RequestContext);
}
// Create the CEF browser window.
CefRefPtr<CefBrowser> Browser = CefBrowserHost::CreateBrowserSync(WindowInfo, NewHandler.get(), TCHAR_TO_WCHAR(*WindowSettings.InitialURL), BrowserSettings, RequestContext);
if (Browser.get())
{
// Create new window
TSharedPtr<FCEFWebBrowserWindow> NewBrowserWindow = MakeShareable(new FCEFWebBrowserWindow(
Browser,
NewHandler,
WindowSettings.InitialURL,
WindowSettings.ContentsToLoad,
WindowSettings.bShowErrorMessage,
WindowSettings.bThumbMouseButtonNavigation,
WindowSettings.bUseTransparency,
bJSBindingsToLoweringEnabled));
NewHandler->SetBrowserWindow(NewBrowserWindow);
{
FScopeLock Lock(&WindowInterfacesCS);
WindowInterfaces.Add(NewBrowserWindow);
}
return NewBrowserWindow;
}
}
#elif PLATFORM_ANDROID && USE_ANDROID_JNI
// Create new window
TSharedPtr<FAndroidWebBrowserWindow> NewBrowserWindow = MakeShareable(new FAndroidWebBrowserWindow(
WindowSettings.InitialURL,
WindowSettings.ContentsToLoad,
WindowSettings.bShowErrorMessage,
WindowSettings.bThumbMouseButtonNavigation,
WindowSettings.bUseTransparency,
bJSBindingsToLoweringEnabled));
{
FScopeLock Lock(&WindowInterfacesCS);
WindowInterfaces.Add(NewBrowserWindow);
}
return NewBrowserWindow;
#elif PLATFORM_IOS
// Create new window
TSharedPtr<FWebBrowserWindow> NewBrowserWindow = MakeShareable(new FWebBrowserWindow(
WindowSettings.InitialURL,
WindowSettings.ContentsToLoad,
WindowSettings.bShowErrorMessage,
WindowSettings.bThumbMouseButtonNavigation,
WindowSettings.bUseTransparency,
bJSBindingsToLoweringEnabled));
{
FScopeLock Lock(&WindowInterfacesCS);
WindowInterfaces.Add(NewBrowserWindow);
}
return NewBrowserWindow;
#elif PLATFORM_PS4
// Create new window
TSharedPtr<FWebBrowserWindow> NewBrowserWindow = MakeShareable(new FWebBrowserWindow(
WindowSettings.InitialURL,
WindowSettings.ContentsToLoad,
WindowSettings.bShowErrorMessage,
WindowSettings.bThumbMouseButtonNavigation,
WindowSettings.bUseTransparency));
{
FScopeLock Lock(&WindowInterfacesCS);
WindowInterfaces.Add(NewBrowserWindow);
}
return NewBrowserWindow;
#endif
return nullptr;
}
#if BUILD_EMBEDDED_APP
TSharedPtr<IWebBrowserWindow> FWebBrowserSingleton::CreateNativeBrowserProxy()
{
TSharedPtr<FNativeWebBrowserProxy> NewBrowserWindow = MakeShareable(new FNativeWebBrowserProxy(
bJSBindingsToLoweringEnabled
));
NewBrowserWindow->Initialize();
return NewBrowserWindow;
}
#endif //BUILD_EMBEDDED_APP
bool FWebBrowserSingleton::Tick(float DeltaTime)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FWebBrowserSingleton_Tick);
#if WITH_CEF3
{
FScopeLock Lock(&WindowInterfacesCS);
bool bIsSlateAwake = FSlateApplication::IsInitialized() && !FSlateApplication::Get().IsSlateAsleep();
// Remove any windows that have been deleted and check whether it's currently visible
for (int32 Index = WindowInterfaces.Num() - 1; Index >= 0; --Index)
{
if (!WindowInterfaces[Index].IsValid())
{
WindowInterfaces.RemoveAt(Index);
}
else if (bIsSlateAwake) // only check for Tick activity if Slate is currently ticking
{
TSharedPtr<FCEFWebBrowserWindow> BrowserWindow = WindowInterfaces[Index].Pin();
if(BrowserWindow.IsValid())
{
// Test if we've ticked recently. If not assume the browser window has become hidden.
BrowserWindow->CheckTickActivity();
}
}
}
}
bool bForceMessageLoop = false;
GConfig->GetBool(TEXT("Browser"), TEXT("bForceMessageLoop"), bForceMessageLoop, GEngineIni);
if (CEFBrowserApp != nullptr)
{
// force via config override or if there are active browser windows
const bool bForce = bForceMessageLoop || WindowInterfaces.Num() > 0;
// tick the CEF app to determine when to run CefDoMessageLoopWork
CEFBrowserApp->TickMessagePump(DeltaTime, bForce);
}
// Update video buffering for any windows that need it
for (int32 Index = 0; Index < WindowInterfaces.Num(); Index++)
{
if (WindowInterfaces[Index].IsValid())
{
TSharedPtr<FCEFWebBrowserWindow> BrowserWindow = WindowInterfaces[Index].Pin();
if (BrowserWindow.IsValid())
{
BrowserWindow->UpdateVideoBuffering();
}
}
}
#elif PLATFORM_IOS || PLATFORM_PS4 || (PLATFORM_ANDROID && USE_ANDROID_JNI)
FScopeLock Lock(&WindowInterfacesCS);
bool bIsSlateAwake = FSlateApplication::IsInitialized() && !FSlateApplication::Get().IsSlateAsleep();
// Remove any windows that have been deleted and check whether it's currently visible
for (int32 Index = WindowInterfaces.Num() - 1; Index >= 0; --Index)
{
if (!WindowInterfaces[Index].IsValid())
{
WindowInterfaces.RemoveAt(Index);
}
else if (bIsSlateAwake) // only check for Tick activity if Slate is currently ticking
{
TSharedPtr<IWebBrowserWindow> BrowserWindow = WindowInterfaces[Index].Pin();
if (BrowserWindow.IsValid())
{
// Test if we've ticked recently. If not assume the browser window has become hidden.
BrowserWindow->CheckTickActivity();
}
}
}
#endif
return true;
}
FString FWebBrowserSingleton::GetCurrentLocaleCode()
{
FCultureRef Culture = FInternationalization::Get().GetCurrentCulture();
FString LocaleCode = Culture->GetTwoLetterISOLanguageName();
FString Country = Culture->GetRegion();
if (!Country.IsEmpty())
{
LocaleCode = LocaleCode + TEXT("-") + Country;
}
return LocaleCode;
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void FWebBrowserSingleton::DeleteBrowserCookies(FString URL, FString CookieName, TFunction<void(int)> Completed)
{
if (DefaultCookieManager.IsValid())
{
DefaultCookieManager->DeleteCookies(URL, CookieName, Completed);
}
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
TSharedPtr<IWebBrowserCookieManager> FWebBrowserSingleton::GetCookieManager(TOptional<FString> ContextId) const
{
if (ContextId.IsSet())
{
#if WITH_CEF3
const CefRefPtr<CefRequestContext>* ExistingContext = RequestContexts.Find(ContextId.GetValue());
if (ExistingContext)
{
// Cache these cookie managers?
return FCefWebBrowserCookieManagerFactory::Create((*ExistingContext)->GetDefaultCookieManager(nullptr));
}
else
{
UE_LOG(LogWebBrowser, Log, TEXT("No cookie manager for ContextId=%s. Using default cookie manager"), *ContextId.GetValue());
}
#endif
}
// No ContextId or cookie manager instance associated with it. Use default
return DefaultCookieManager;
}
bool FWebBrowserSingleton::RegisterContext(const FBrowserContextSettings& Settings)
{
#if WITH_CEF3
const CefRefPtr<CefRequestContext>* ExistingContext = RequestContexts.Find(Settings.Id);
if (ExistingContext != nullptr)
{
// You can't register the same context twice and
// you can't update the settings for a context that already exists
return false;
}
CefRequestContextSettings RequestContextSettings;
CefString(&RequestContextSettings.accept_language_list) = TCHAR_TO_WCHAR(*Settings.AcceptLanguageList);
CefString(&RequestContextSettings.cache_path) = TCHAR_TO_WCHAR(*Settings.CookieStorageLocation);
RequestContextSettings.persist_session_cookies = Settings.bPersistSessionCookies;
RequestContextSettings.ignore_certificate_errors = Settings.bIgnoreCertificateErrors;
#if !PLATFORM_LINUX
RequestContextSettings.enable_net_security_expiration = Settings.bEnableNetSecurityExpiration;
#endif
//Create a new one
CefRefPtr<CefRequestContext> RequestContext = CefRequestContext::CreateContext(RequestContextSettings, nullptr);
RequestContexts.Add(Settings.Id, RequestContext);
return true;
#else
return false;
#endif
}
bool FWebBrowserSingleton::UnregisterContext(const FString& ContextId)
{
#if WITH_CEF3
CefRefPtr<CefRequestContext> Context;
if (RequestContexts.RemoveAndCopyValue(ContextId, Context))
{
Context->ClearSchemeHandlerFactories();
return true;
}
#endif
return false;
}
bool FWebBrowserSingleton::RegisterSchemeHandlerFactory(FString Scheme, FString Domain, IWebBrowserSchemeHandlerFactory* WebBrowserSchemeHandlerFactory)
{
#if WITH_CEF3
SchemeHandlerFactories.AddSchemeHandlerFactory(MoveTemp(Scheme), MoveTemp(Domain), WebBrowserSchemeHandlerFactory);
return true;
#else
return false;
#endif
}
bool FWebBrowserSingleton::UnregisterSchemeHandlerFactory(IWebBrowserSchemeHandlerFactory* WebBrowserSchemeHandlerFactory)
{
#if WITH_CEF3
SchemeHandlerFactories.RemoveSchemeHandlerFactory(WebBrowserSchemeHandlerFactory);
return true;
#else
return false;
#endif
}
// Cleanup macros to avoid having them leak outside this source file
#undef CEF3_BIN_DIR
#undef CEF3_FRAMEWORK_DIR
#undef CEF3_RESOURCES_DIR
#undef CEF3_SUBPROCES_EXE