123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918 |
- // Engine/Source/Runtime/WebBrowser/Private/CEF/CEFBrowserHandler.cpp
- #include "CEF/CEFInterfaceBrowserHandler.h"
- #include "HAL/PlatformApplicationMisc.h"
- #if WITH_CEF3
- //#define DEBUG_ONBEFORELOAD // Debug print beforebrowse steps
- #include "WebInterfaceBrowserModule.h"
- #include "CEFInterfaceBrowserClosureTask.h"
- #include "IWebInterfaceBrowserSingleton.h"
- #include "WebInterfaceBrowserSingleton.h"
- #include "CEFInterfaceBrowserPopupFeatures.h"
- #include "CEFWebInterfaceBrowserWindow.h"
- #include "CEFInterfaceBrowserByteResource.h"
- #include "Framework/Application/SlateApplication.h"
- #include "HAL/ThreadingBase.h"
- #include "PlatformHttp.h"
- #include "Misc/CommandLine.h"
- #define LOCTEXT_NAMESPACE "WebInterfaceBrowserHandler"
- #ifdef DEBUG_ONBEFORELOAD
- // Debug helper function to track URL loads
- void LogCEFLoad(const FString &Msg, CefRefPtr<CefRequest> Request)
- {
- auto url = Request->GetURL();
- auto type = Request->GetResourceType();
- if (type == CefRequest::ResourceType::RT_MAIN_FRAME || type == CefRequest::ResourceType::RT_XHR || type == CefRequest::ResourceType::RT_SUB_RESOURCE|| type == CefRequest::ResourceType::RT_SUB_FRAME)
- {
- GLog->Logf(ELogVerbosity::Display, TEXT("%s :%s type:%s"), *Msg, url.c_str(), *ResourceTypeToString(type));
- }
- }
- #define LOG_CEF_LOAD(MSG) LogCEFLoad(#MSG, Request)
- #else
- #define LOG_CEF_LOAD(MSG)
- #endif
- // Used to force returning custom content instead of performing a request.
- const FString CustomContentMethod(TEXT("X-GET-CUSTOM-CONTENT"));
- FCEFInterfaceBrowserHandler::FCEFInterfaceBrowserHandler(bool InUseTransparency, bool InInterceptLoadRequests, const TArray<FString>& InAltRetryDomains, const TArray<FString>& InAuthorizationHeaderAllowListURLS)
- : bUseTransparency(InUseTransparency),
- bAllowAllCookies(false),
- bInterceptLoadRequests(InInterceptLoadRequests),
- AltRetryDomains(InAltRetryDomains),
- AuthorizationHeaderAllowListURLS(InAuthorizationHeaderAllowListURLS)
- {
- // should we forcefully allow all cookies to be set rather than filtering a couple store side ones
- bAllowAllCookies = FParse::Param(FCommandLine::Get(), TEXT("CefAllowAllCookies"));
- }
- void FCEFInterfaceBrowserHandler::OnTitleChange(CefRefPtr<CefBrowser> Browser, const CefString& Title)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->SetTitle(Title);
- }
- }
- void FCEFInterfaceBrowserHandler::OnAddressChange(CefRefPtr<CefBrowser> Browser, CefRefPtr<CefFrame> Frame, const CefString& Url)
- {
- if (Frame->IsMain())
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->SetUrl(Url);
- }
- }
- }
- bool FCEFInterfaceBrowserHandler::OnTooltip(CefRefPtr<CefBrowser> Browser, CefString& Text)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->SetToolTip(Text);
- }
- return false;
- }
- bool FCEFInterfaceBrowserHandler::OnConsoleMessage(CefRefPtr<CefBrowser> Browser, cef_log_severity_t level, const CefString& Message, const CefString& Source, int Line)
- {
- ConsoleMessageDelegate.ExecuteIfBound(Browser, level, Message, Source, Line);
- // Return false to let it output to console.
- return false;
- }
- void FCEFInterfaceBrowserHandler::OnAfterCreated(CefRefPtr<CefBrowser> Browser)
- {
- if(Browser->IsPopup())
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindowParent = ParentHandler.get() ? ParentHandler->BrowserWindowPtr.Pin() : nullptr;
- if(BrowserWindowParent.IsValid() && ParentHandler->OnCreateWindow().IsBound())
- {
- TSharedPtr<FWebInterfaceBrowserWindowInfo> NewBrowserWindowInfo = MakeShareable(new FWebInterfaceBrowserWindowInfo(Browser, this));
- TSharedPtr<IWebInterfaceBrowserWindow> NewBrowserWindow = IWebInterfaceBrowserModule::Get().GetSingleton()->CreateBrowserWindow(
- BrowserWindowParent,
- NewBrowserWindowInfo
- );
- {
- // @todo: At the moment we need to downcast since the handler does not support using the interface.
- TSharedPtr<FCEFWebInterfaceBrowserWindow> HandlerSpecificBrowserWindow = StaticCastSharedPtr<FCEFWebInterfaceBrowserWindow>(NewBrowserWindow);
- BrowserWindowPtr = HandlerSpecificBrowserWindow;
- }
- // Request a UI window for the browser. If it is not created we do some cleanup.
- bool bUIWindowCreated = ParentHandler->OnCreateWindow().Execute(TWeakPtr<IWebInterfaceBrowserWindow>(NewBrowserWindow), TWeakPtr<IWebInterfaceBrowserPopupFeatures>(BrowserPopupFeatures));
- if(!bUIWindowCreated)
- {
- NewBrowserWindow->CloseBrowser(true);
- }
- else
- {
- checkf(!NewBrowserWindow.IsUnique(), TEXT("Handler indicated that new window UI was created, but failed to save the new WebBrowserWindow instance."));
- }
- }
- else
- {
- Browser->GetHost()->CloseBrowser(true);
- }
- }
- }
- bool FCEFInterfaceBrowserHandler::DoClose(CefRefPtr<CefBrowser> Browser)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if(BrowserWindow.IsValid())
- {
- BrowserWindow->OnBrowserClosing();
- }
- #if PLATFORM_WINDOWS
- // If we have a window handle, we're rendering directly to the screen and not off-screen
- HWND NativeWindowHandle = Browser->GetHost()->GetWindowHandle();
- if (NativeWindowHandle != nullptr)
- {
- HWND ParentWindow = ::GetParent(NativeWindowHandle);
- if (ParentWindow)
- {
- HWND FocusHandle = ::GetFocus();
- if (FocusHandle && (FocusHandle == NativeWindowHandle || ::IsChild(NativeWindowHandle, FocusHandle)))
- {
- // Set focus to the parent window, otherwise keyboard and mouse wheel input will become wonky
- ::SetFocus(ParentWindow);
- }
- // CEF will send a WM_CLOSE to the parent window and potentially exit the application if we don't do this
- ::SetParent(NativeWindowHandle, nullptr);
- }
- }
- #endif
- return false;
- }
- void FCEFInterfaceBrowserHandler::OnBeforeClose(CefRefPtr<CefBrowser> Browser)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->OnBrowserClosed();
- }
- }
- bool FCEFInterfaceBrowserHandler::OnBeforePopup( CefRefPtr<CefBrowser> Browser,
- CefRefPtr<CefFrame> Frame,
- const CefString& TargetUrl,
- const CefString& TargetFrameName,
- const CefPopupFeatures& PopupFeatures,
- CefWindowInfo& OutWindowInfo,
- CefRefPtr<CefClient>& OutClient,
- CefBrowserSettings& OutSettings,
- bool* OutNoJavascriptAccess )
- {
- FString URL = WCHAR_TO_TCHAR(TargetUrl.ToWString().c_str());
- FString FrameName = WCHAR_TO_TCHAR(TargetFrameName.ToWString().c_str());
- /* If OnBeforePopup() is not bound, we allow creating new windows as long as OnCreateWindow() is bound.
- The BeforePopup delegate is always executed even if OnCreateWindow is not bound to anything .
- */
- if((OnBeforePopup().IsBound() && OnBeforePopup().Execute(URL, FrameName)) || !OnCreateWindow().IsBound())
- {
- return true;
- }
- else
- {
- TSharedPtr<FCEFInterfaceBrowserPopupFeatures> NewBrowserPopupFeatures = MakeShareable(new FCEFInterfaceBrowserPopupFeatures(PopupFeatures));
- bool bIsDevtools = URL.Contains(TEXT("chrome-devtools"));
- bool shouldUseTransparency = bIsDevtools ? false : bUseTransparency;
- NewBrowserPopupFeatures->SetResizable(bIsDevtools); // only have the window for DevTools have resize options
- cef_color_t Alpha = shouldUseTransparency ? 0 : CefColorGetA(OutSettings.background_color);
- cef_color_t R = CefColorGetR(OutSettings.background_color);
- cef_color_t G = CefColorGetG(OutSettings.background_color);
- cef_color_t B = CefColorGetB(OutSettings.background_color);
- OutSettings.background_color = CefColorSetARGB(Alpha, R, G, B);
- CefRefPtr<FCEFInterfaceBrowserHandler> NewHandler(new FCEFInterfaceBrowserHandler(shouldUseTransparency, true /*InterceptLoadRequests*/));
- NewHandler->ParentHandler = this;
- NewHandler->SetPopupFeatures(NewBrowserPopupFeatures);
- OutClient = NewHandler;
- // Always use off screen rendering so we can integrate with our windows
- #if PLATFORM_LINUX
- OutWindowInfo.SetAsWindowless(kNullWindowHandle);
- #elif PLATFORM_WINDOWS
- OutWindowInfo.SetAsWindowless(kNullWindowHandle);
- OutWindowInfo.shared_texture_enabled = 0; // always render popups with the simple OSR renderer
- #elif PLATFORM_MAC
- OutWindowInfo.SetAsWindowless(kNullWindowHandle);
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- OutWindowInfo.shared_texture_enabled = BrowserWindow->UsingAcceleratedPaint() ? 1 : 0; // match what other windows do
- }
- else
- {
- OutWindowInfo.shared_texture_enabled = 0;
- }
- #else
- OutWindowInfo.SetAsWindowless(kNullWindowHandle);
- #endif
- // We need to rely on CEF to create our window so we set the WindowInfo, BrowserSettings, Client, and then return false
- return false;
- }
- }
- bool FCEFInterfaceBrowserHandler::OnCertificateError(CefRefPtr<CefBrowser> Browser,
- cef_errorcode_t CertError,
- const CefString &RequestUrl,
- CefRefPtr<CefSSLInfo> SslInfo,
- CefRefPtr<CefRequestCallback> Callback)
- {
- // Forward the cert error to the normal load error handler
- CefString ErrorText = "Certificate error";
- OnLoadError(Browser, Browser->GetMainFrame(), CertError, ErrorText, RequestUrl);
- return false;
- }
- void FCEFInterfaceBrowserHandler::OnLoadError(CefRefPtr<CefBrowser> Browser,
- CefRefPtr<CefFrame> Frame,
- CefLoadHandler::ErrorCode InErrorCode,
- const CefString& ErrorText,
- const CefString& FailedUrl)
- {
- // notify browser window
- if (Frame->IsMain())
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- if (AltRetryDomains.Num() > 0 && AltRetryDomainIdx < (uint32)AltRetryDomains.Num())
- {
- FString Url = WCHAR_TO_TCHAR(FailedUrl.ToWString().c_str());
- FString OriginalUrlDomain = FPlatformHttp::GetUrlDomain(Url);
- if (!OriginalUrlDomain.IsEmpty())
- {
- const FString NewUrl(Url.Replace(*OriginalUrlDomain, *AltRetryDomains[AltRetryDomainIdx++]));
- BrowserWindow->LoadURL(NewUrl);
- return;
- }
- }
- BrowserWindow->NotifyDocumentError(InErrorCode, ErrorText, FailedUrl);
- }
- }
- }
- void FCEFInterfaceBrowserHandler::OnLoadStart(CefRefPtr<CefBrowser> Browser, CefRefPtr<CefFrame> Frame, TransitionType CefTransitionType)
- {
- }
- void FCEFInterfaceBrowserHandler::OnLoadingStateChange(CefRefPtr<CefBrowser> Browser, bool bIsLoading, bool bCanGoBack, bool bCanGoForward)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->NotifyDocumentLoadingStateChange(bIsLoading);
- }
- }
- bool FCEFInterfaceBrowserHandler::GetRootScreenRect(CefRefPtr<CefBrowser> Browser, CefRect& Rect)
- {
- if (CefCurrentlyOn(TID_UI))
- {
- // CEF may call this off the main gamethread which slate requires, so double check here
- FDisplayMetrics DisplayMetrics;
- FSlateApplication::Get().GetDisplayMetrics(DisplayMetrics);
- Rect.width = DisplayMetrics.PrimaryDisplayWidth;
- Rect.height = DisplayMetrics.PrimaryDisplayHeight;
- return true;
- }
-
- return false;
- }
- void FCEFInterfaceBrowserHandler::GetViewRect(CefRefPtr<CefBrowser> Browser, CefRect& Rect)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->GetViewRect(Rect);
- }
- else
- {
- // CEF requires at least a 1x1 area for painting
- Rect.x = Rect.y = 0;
- Rect.width = Rect.height = 1;
- }
- }
- void FCEFInterfaceBrowserHandler::OnPaint(CefRefPtr<CefBrowser> Browser,
- PaintElementType Type,
- const RectList& DirtyRects,
- const void* Buffer,
- int Width, int Height)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->OnPaint(Type, DirtyRects, Buffer, Width, Height);
- }
- }
- void FCEFInterfaceBrowserHandler::OnAcceleratedPaint(CefRefPtr<CefBrowser> Browser,
- PaintElementType Type,
- const RectList& DirtyRects,
- void* SharedHandle)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->OnAcceleratedPaint(Type, DirtyRects, SharedHandle);
- }
- }
- bool FCEFInterfaceBrowserHandler::OnCursorChange(CefRefPtr<CefBrowser> Browser, CefCursorHandle Cursor, cef_cursor_type_t Type, const CefCursorInfo& CustomCursorInfo)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- return BrowserWindow->OnCursorChange(Cursor, Type, CustomCursorInfo);
- }
- return false;
- }
- void FCEFInterfaceBrowserHandler::OnPopupShow(CefRefPtr<CefBrowser> Browser, bool bShow)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->ShowPopupMenu(bShow);
- }
- }
- void FCEFInterfaceBrowserHandler::OnPopupSize(CefRefPtr<CefBrowser> Browser, const CefRect& Rect)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->SetPopupMenuPosition(Rect);
- }
- }
- bool FCEFInterfaceBrowserHandler::GetScreenInfo(CefRefPtr<CefBrowser> Browser, CefScreenInfo& ScreenInfo)
- {
- TSharedPtr<FWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- ScreenInfo.depth = 24;
- if (BrowserWindow.IsValid() && BrowserWindow->GetParentWindow().IsValid())
- {
- ScreenInfo.device_scale_factor = BrowserWindow->GetParentWindow()->GetNativeWindow()->GetDPIScaleFactor();
- }
- else
- {
- FDisplayMetrics DisplayMetrics;
- FDisplayMetrics::RebuildDisplayMetrics(DisplayMetrics);
- ScreenInfo.device_scale_factor = FPlatformApplicationMisc::GetDPIScaleFactorAtPoint(DisplayMetrics.PrimaryDisplayWorkAreaRect.Left, DisplayMetrics.PrimaryDisplayWorkAreaRect.Top);
- }
- return true;
- }
- #if !PLATFORM_LINUX
- void FCEFInterfaceBrowserHandler::OnImeCompositionRangeChanged(
- CefRefPtr<CefBrowser> Browser,
- const CefRange& SelectionRange,
- const CefRenderHandler::RectList& CharacterBounds)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->OnImeCompositionRangeChanged(Browser, SelectionRange, CharacterBounds);
- }
- }
- #endif
- CefResourceRequestHandler::ReturnValue FCEFInterfaceBrowserHandler::OnBeforeResourceLoad(CefRefPtr<CefBrowser> Browser, CefRefPtr<CefFrame> Frame, CefRefPtr<CefRequest> Request, CefRefPtr<CefRequestCallback> Callback)
- {
- if (Request->IsReadOnly())
- {
- LOG_CEF_LOAD("FCEFInterfaceBrowserHandler::OnBeforeResourceLoad - readonly");
- // we can't alter this request so just allow it through
- return RV_CONTINUE;
- }
- // Current thread is IO thread. We need to invoke BrowserWindow->GetResourceContent on the UI (aka Game) thread:
- CefPostTask(TID_UI, new FCEFInterfaceBrowserClosureTask(this, [=]()
- {
- const FString LanguageHeaderText(TEXT("Accept-Language"));
- const FString LocaleCode = FWebInterfaceBrowserSingleton::GetCurrentLocaleCode();
- CefRequest::HeaderMap HeaderMap;
- Request->GetHeaderMap(HeaderMap);
- auto LanguageHeader = HeaderMap.find(TCHAR_TO_WCHAR(*LanguageHeaderText));
- if (LanguageHeader != HeaderMap.end())
- {
- (*LanguageHeader).second = TCHAR_TO_WCHAR(*LocaleCode);
- }
- else
- {
- HeaderMap.insert(std::pair<CefString, CefString>(TCHAR_TO_WCHAR(*LanguageHeaderText), TCHAR_TO_WCHAR(*LocaleCode)));
- }
- LOG_CEF_LOAD( "FCEFInterfaceBrowserHandler::OnBeforeResourceLoad" );
- if (BeforeResourceLoadDelegate.IsBound())
- {
- // Allow appending the Authorization header if this was NOT a RT_XHR type of page load
- bool bAllowCredentials = URLRequestAllowsCredentials(WCHAR_TO_TCHAR(Request->GetURL().ToWString().c_str()));
- FRequestHeaders AdditionalHeaders;
- BeforeResourceLoadDelegate.Execute(Request->GetURL(), Request->GetResourceType(), AdditionalHeaders, bAllowCredentials);
- for (auto Iter = AdditionalHeaders.CreateConstIterator(); Iter; ++Iter)
- {
- const FString& Header = Iter.Key();
- const FString& Value = Iter.Value();
- HeaderMap.insert(std::pair<CefString, CefString>(TCHAR_TO_WCHAR(*Header), TCHAR_TO_WCHAR(*Value)));
- }
- }
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- TOptional<FString> Contents = BrowserWindow->GetResourceContent(Frame, Request);
- if(Contents.IsSet())
- {
- Contents.GetValue().ReplaceInline(TEXT("\n"), TEXT(""), ESearchCase::CaseSensitive);
- Contents.GetValue().ReplaceInline(TEXT("\r"), TEXT(""), ESearchCase::CaseSensitive);
- // pass the text we'd like to come back as a response to the request post data
- CefRefPtr<CefPostData> PostData = CefPostData::Create();
- CefRefPtr<CefPostDataElement> Element = CefPostDataElement::Create();
- FTCHARToUTF8 UTF8String(*Contents.GetValue());
- Element->SetToBytes(UTF8String.Length(), UTF8String.Get());
- PostData->AddElement(Element);
- Request->SetPostData(PostData);
- // Set a custom request header, so we know the mime type if it was specified as a hash on the dummy URL
- std::string Url = Request->GetURL().ToString();
- std::string::size_type HashPos = Url.find_last_of('#');
- if (HashPos != std::string::npos)
- {
- std::string MimeType = Url.substr(HashPos + 1);
- HeaderMap.insert(std::pair<CefString, CefString>(TCHAR_TO_WCHAR(TEXT("Content-Type")), MimeType));
- }
- // Change http method to tell GetResourceHandler to return the content
- Request->SetMethod(TCHAR_TO_WCHAR(*CustomContentMethod));
- }
- }
- if (Request->IsReadOnly())
- {
- LOG_CEF_LOAD("FCEFInterfaceBrowserHandler::OnBeforeResourceLoad - readonly");
- }
- else
- {
- Request->SetHeaderMap(HeaderMap);
- }
- Callback->Continue(true);
- }));
- // Tell CEF that we're handling this asynchronously.
- return RV_CONTINUE_ASYNC;
- }
- void FCEFInterfaceBrowserHandler::OnResourceLoadComplete(
- CefRefPtr<CefBrowser> Browser,
- CefRefPtr<CefFrame> Frame,
- CefRefPtr<CefRequest> Request,
- CefRefPtr<CefResponse> Response,
- URLRequestStatus Status,
- int64 Received_content_length)
- {
- LOG_CEF_LOAD("FCEFInterfaceBrowserHandler::OnResourceLoadComplete");
- // Current thread is IO thread. We need to invoke our delegates on the UI (aka Game) thread:
- CefPostTask(TID_UI, new FCEFInterfaceBrowserClosureTask(this, [=]()
- {
- auto resType = Request->GetResourceType();
- const FString URL = WCHAR_TO_TCHAR(Request->GetURL().ToWString().c_str());
- if (MainFrameLoadTypes.Contains(URL))
- {
- // CEF has a bug where it confuses a MAIN_FRAME load for a XHR one, so fix it up here if we detect it.
- resType = CefRequest::ResourceType::RT_MAIN_FRAME;
- }
- ResourceLoadCompleteDelegate.ExecuteIfBound(Request->GetURL(), resType, Status, Received_content_length);
- // this load is done, clear the request from our map
- MainFrameLoadTypes.Remove(URL);
- }));
- }
- void FCEFInterfaceBrowserHandler::OnResourceRedirect(CefRefPtr<CefBrowser> browser,
- CefRefPtr<CefFrame> Frame,
- CefRefPtr<CefRequest> Request,
- CefRefPtr<CefResponse> Response,
- CefString& new_url)
- {
- LOG_CEF_LOAD("FCEFInterfaceBrowserHandler::OnResourceRedirect");
- // Current thread is IO thread. We need to invoke our delegates on the UI (aka Game) thread:
- CefPostTask(TID_UI, new FCEFInterfaceBrowserClosureTask(this, [=]()
- {
- // this load is effectively done, clear the request from our map
- MainFrameLoadTypes.Remove(WCHAR_TO_TCHAR(Request->GetURL().ToWString().c_str()));
- }));
- }
- void FCEFInterfaceBrowserHandler::OnRenderProcessTerminated(CefRefPtr<CefBrowser> Browser, TerminationStatus Status)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->OnRenderProcessTerminated(Status);
- }
- }
- bool FCEFInterfaceBrowserHandler::OnBeforeBrowse(CefRefPtr<CefBrowser> Browser,
- CefRefPtr<CefFrame> Frame,
- CefRefPtr<CefRequest> Request,
- bool user_gesture,
- bool IsRedirect)
- {
- CefRequest::ResourceType RequestType = Request->GetResourceType();
- // We only want to append Authorization headers to main frame and similar requests
- // BUGBUG - in theory we want to support XHR requests that have the access-control-allow-credentials header but CEF doesn't give us preflight details here
- if (RequestType == CefRequest::ResourceType::RT_MAIN_FRAME || RequestType == CefRequest::ResourceType::RT_SUB_FRAME || RequestType == CefRequest::ResourceType::RT_SUB_RESOURCE)
- {
- // record that we saw this URL request as a main frame load
- MainFrameLoadTypes.Add(WCHAR_TO_TCHAR(Request->GetURL().ToWString().c_str()), RequestType);
- }
- // Current thread: UI thread
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- LOG_CEF_LOAD("FCEFInterfaceBrowserHandler::OnBeforeBrowse");
- if(BrowserWindow->OnBeforeBrowse(Browser, Frame, Request, user_gesture, IsRedirect))
- {
- return true;
- }
- }
- return false;
- }
- CefRefPtr<CefResourceHandler> FCEFInterfaceBrowserHandler::GetResourceHandler( CefRefPtr<CefBrowser> Browser, CefRefPtr< CefFrame > Frame, CefRefPtr< CefRequest > Request )
- {
- if (Request->GetMethod() == TCHAR_TO_WCHAR(*CustomContentMethod))
- {
- // Content override header will be set by OnBeforeResourceLoad before passing the request on to this.
- if (Request->GetPostData() && Request->GetPostData()->GetElementCount() > 0)
- {
- // get the mime type from Content-Type header (default to text/html to support old behavior)
- FString MimeType = TEXT("text/html"); // default if not specified
- CefRequest::HeaderMap HeaderMap;
- Request->GetHeaderMap(HeaderMap);
- auto ContentOverride = HeaderMap.find(TCHAR_TO_WCHAR(TEXT("Content-Type")));
- if (ContentOverride != HeaderMap.end())
- {
- MimeType = WCHAR_TO_TCHAR(ContentOverride->second.ToWString().c_str());
- }
- // reply with the post data
- CefPostData::ElementVector Elements;
- Request->GetPostData()->GetElements(Elements);
- return new FCEFInterfaceBrowserByteResource(Elements[0], MimeType);
- }
- }
- return nullptr;
- }
- CefRefPtr<CefResourceRequestHandler> FCEFInterfaceBrowserHandler::GetResourceRequestHandler( CefRefPtr<CefBrowser> Browser, CefRefPtr<CefFrame> Frame,
- CefRefPtr<CefRequest> Request, bool is_navigation, bool is_download, const CefString& request_initiator, bool& disable_default_handling)
- {
- LOG_CEF_LOAD("FCEFInterfaceBrowserHandler::GetResourceRequestHandler");
- if (bInterceptLoadRequests)
- return this;
- return nullptr;
- }
- void FCEFInterfaceBrowserHandler::SetBrowserWindow(TSharedPtr<FCEFWebInterfaceBrowserWindow> InBrowserWindow)
- {
- BrowserWindowPtr = InBrowserWindow;
- if (InBrowserWindow.IsValid())
- {
- // Register any JS bindings that are setup in the new browser. In theory there should be 0 here as we are still being created.
- CefRefPtr<CefProcessMessage> SetValueMessage = CefProcessMessage::Create(TCHAR_TO_WCHAR(TEXT("CEF::STARTUP")));
- CefRefPtr<CefListValue> MessageArguments = SetValueMessage->GetArgumentList();
- CefRefPtr<CefDictionaryValue> Bindings = InBrowserWindow->GetProcessInfo();
- if (Bindings.get())
- {
- MessageArguments->SetDictionary(0, Bindings);
- }
- InBrowserWindow->GetCefBrowser()->GetMainFrame()->SendProcessMessage(PID_RENDERER, SetValueMessage);
- }
- }
- bool FCEFInterfaceBrowserHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> Browser,
- CefRefPtr<CefFrame> Frame,
- CefProcessId SourceProcess,
- CefRefPtr<CefProcessMessage> Message)
- {
- bool Retval = false;
- FString MessageName = WCHAR_TO_TCHAR(Message->GetName().ToWString().c_str());
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- if (MessageName.StartsWith(TEXT("CEF::BROWSERCREATED")))
- {
- // Register any JS bindings that are setup in the new browser. In theory there should be 0 here as we are still being created.
- CefRefPtr<CefProcessMessage> SetValueMessage = CefProcessMessage::Create(TCHAR_TO_WCHAR(TEXT("CEF::STARTUP")));
- CefRefPtr<CefListValue> MessageArguments = SetValueMessage->GetArgumentList();
- CefRefPtr<CefDictionaryValue> Bindings = BrowserWindow->GetProcessInfo();
- if (Bindings.get())
- {
- MessageArguments->SetDictionary(0, Bindings);
- }
- // CEF has a race condition for newly constructed browser objects, we may route this to the wrong renderer if we send right away
- // so just PostTake to send this message next frame
- CefPostTask(TID_UI, new FCEFInterfaceBrowserClosureTask(this, [=]()
- {
- Frame->SendProcessMessage(PID_RENDERER, SetValueMessage);
- }));
- }
- else
- {
- Retval = BrowserWindow->OnProcessMessageReceived(Browser, Frame, SourceProcess, Message);
- }
- }
- return Retval;
- }
- bool FCEFInterfaceBrowserHandler::ShowDevTools(const CefRefPtr<CefBrowser>& Browser)
- {
- CefPoint Point;
- CefString TargetUrl = "chrome-devtools://devtools/devtools.html";
- CefString TargetFrameName = "devtools";
- CefPopupFeatures PopupFeatures;
- CefWindowInfo WindowInfo;
- CefRefPtr<CefClient> NewClient;
- CefBrowserSettings BrowserSettings;
- bool NoJavascriptAccess = false;
- PopupFeatures.xSet = false;
- PopupFeatures.ySet = false;
- PopupFeatures.heightSet = false;
- PopupFeatures.widthSet = false;
- PopupFeatures.menuBarVisible = false;
- PopupFeatures.toolBarVisible = false;
- PopupFeatures.statusBarVisible = false;
- // Set max framerate to maximum supported.
- BrowserSettings.windowless_frame_rate = 60;
- // Disable plugins
- BrowserSettings.plugins = STATE_DISABLED;
- // Dev Tools look best with a white background color
- BrowserSettings.background_color = CefColorSetARGB(255, 255, 255, 255);
- // OnBeforePopup already takes care of all the details required to ask the host application to create a new browser window.
- bool bSuppressWindowCreation = OnBeforePopup(Browser, Browser->GetFocusedFrame(), TargetUrl, TargetFrameName, PopupFeatures, WindowInfo, NewClient, BrowserSettings, &NoJavascriptAccess);
- if(! bSuppressWindowCreation)
- {
- Browser->GetHost()->ShowDevTools(WindowInfo, NewClient, BrowserSettings, Point);
- }
- return !bSuppressWindowCreation;
- }
- bool FCEFInterfaceBrowserHandler::OnKeyEvent(CefRefPtr<CefBrowser> Browser,
- const CefKeyEvent& Event,
- CefEventHandle OsEvent)
- {
- // Show dev tools on CMD/CTRL+SHIFT+I
- if( (Event.type == KEYEVENT_RAWKEYDOWN || Event.type == KEYEVENT_KEYDOWN || Event.type == KEYEVENT_CHAR) &&
- #if PLATFORM_MAC
- (Event.modifiers == (EVENTFLAG_COMMAND_DOWN | EVENTFLAG_SHIFT_DOWN)) &&
- #else
- (Event.modifiers == (EVENTFLAG_CONTROL_DOWN | EVENTFLAG_SHIFT_DOWN)) &&
- #endif
- (Event.windows_key_code == 'I' ||
- Event.unmodified_character == 'i' || Event.unmodified_character == 'I') &&
- IWebInterfaceBrowserModule::Get().GetSingleton()->IsDevToolsShortcutEnabled()
- )
- {
- return ShowDevTools(Browser);
- }
- #if PLATFORM_MAC
- // We need to handle standard Copy/Paste/etc... shortcuts on OS X
- if( (Event.type == KEYEVENT_RAWKEYDOWN || Event.type == KEYEVENT_KEYDOWN) &&
- (Event.modifiers & EVENTFLAG_COMMAND_DOWN) != 0 &&
- (Event.modifiers & EVENTFLAG_CONTROL_DOWN) == 0 &&
- (Event.modifiers & EVENTFLAG_ALT_DOWN) == 0 &&
- ( (Event.modifiers & EVENTFLAG_SHIFT_DOWN) == 0 || Event.unmodified_character == 'z' )
- )
- {
- CefRefPtr<CefFrame> Frame = Browser->GetFocusedFrame();
- if (Frame)
- {
- switch (Event.unmodified_character)
- {
- case 'a':
- Frame->SelectAll();
- return true;
- case 'c':
- Frame->Copy();
- return true;
- case 'v':
- Frame->Paste();
- return true;
- case 'x':
- Frame->Cut();
- return true;
- case 'z':
- if( (Event.modifiers & EVENTFLAG_SHIFT_DOWN) == 0 )
- {
- Frame->Undo();
- }
- else
- {
- Frame->Redo();
- }
- return true;
- }
- }
- }
- #endif
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- return BrowserWindow->OnUnhandledKeyEvent(Event);
- }
- return false;
- }
- bool FCEFInterfaceBrowserHandler::OnJSDialog(CefRefPtr<CefBrowser> Browser, const CefString& OriginUrl, JSDialogType DialogType, const CefString& MessageText, const CefString& DefaultPromptText, CefRefPtr<CefJSDialogCallback> Callback, bool& OutSuppressMessage)
- {
- bool Retval = false;
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- Retval = BrowserWindow->OnJSDialog(DialogType, MessageText, DefaultPromptText, Callback, OutSuppressMessage);
- }
- return Retval;
- }
- bool FCEFInterfaceBrowserHandler::OnBeforeUnloadDialog(CefRefPtr<CefBrowser> Browser, const CefString& MessageText, bool IsReload, CefRefPtr<CefJSDialogCallback> Callback)
- {
- bool Retval = false;
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- Retval = BrowserWindow->OnBeforeUnloadDialog(MessageText, IsReload, Callback);
- }
- return Retval;
- }
- void FCEFInterfaceBrowserHandler::OnResetDialogState(CefRefPtr<CefBrowser> Browser)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- BrowserWindow->OnResetDialogState();
- }
- }
- void FCEFInterfaceBrowserHandler::OnBeforeContextMenu(CefRefPtr<CefBrowser> Browser, CefRefPtr<CefFrame> Frame, CefRefPtr<CefContextMenuParams> Params, CefRefPtr<CefMenuModel> Model)
- {
- Model->Clear();
- }
- void FCEFInterfaceBrowserHandler::OnDraggableRegionsChanged(CefRefPtr<CefBrowser> Browser, CefRefPtr<CefFrame> frame, const std::vector<CefDraggableRegion>& Regions)
- {
- TSharedPtr<FCEFWebInterfaceBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
- if (BrowserWindow.IsValid())
- {
- TArray<FWebInterfaceBrowserDragRegion> DragRegions;
- for (uint32 Idx = 0; Idx < Regions.size(); Idx++)
- {
- DragRegions.Add(FWebInterfaceBrowserDragRegion(
- FIntRect(Regions[Idx].bounds.x, Regions[Idx].bounds.y, Regions[Idx].bounds.x + Regions[Idx].bounds.width, Regions[Idx].bounds.y + Regions[Idx].bounds.height),
- Regions[Idx].draggable ? true : false));
- }
- BrowserWindow->UpdateDragRegions(DragRegions);
- }
- }
- CefRefPtr<CefCookieAccessFilter> FCEFInterfaceBrowserHandler::GetCookieAccessFilter(
- CefRefPtr<CefBrowser> Browser,
- CefRefPtr<CefFrame> Frame,
- CefRefPtr<CefRequest> Request)
- {
- FString Url = WCHAR_TO_TCHAR(Request->GetURL().ToWString().c_str());
- TArray<FString> UrlParts;
- if (Url.ParseIntoArray(UrlParts, TEXT("/"), true) >= 2)
- {
- if (UrlParts[1].Contains(TEXT(".epicgames.com")) || UrlParts[1].Contains(TEXT(".epicgames.net")))
- {
- // We only support custom cookie alteration for the epicgames domains right now.
- // There are limitations/bugs in CEF when the cookie filtering it on making it fail to pass cookies for some requests, so
- // we want to limit the scope of the filtering. See https://jira.it.epicgames.com/browse/DISTRO-1847 as an example of a bug
- // caused by filtering
- return this;
- }
- }
- return nullptr;
- }
- bool FCEFInterfaceBrowserHandler::CanSaveCookie(CefRefPtr<CefBrowser> browser,
- CefRefPtr<CefFrame> frame,
- CefRefPtr<CefRequest> request,
- CefRefPtr<CefResponse> response,
- const CefCookie& cookie)
- {
- if (bAllowAllCookies)
- {
- return true;
- }
- // these two cookies shouldn't be saved by the client. While we are debugging why the backend is causing them to be set filter them out
- if (CefString(&cookie.name).ToString() == "store-token" || CefString(&cookie.name) == "EPIC_SESSION_DIESEL")
- return false;
- return true;
- }
- bool FCEFInterfaceBrowserHandler::CanSendCookie(CefRefPtr<CefBrowser> Browser,
- CefRefPtr<CefFrame> Frame,
- CefRefPtr<CefRequest> Request,
- const CefCookie& Cookie)
- {
- if (bAllowAllCookies)
- {
- return true;
- }
- FString RequestURL(WCHAR_TO_TCHAR(Request->GetURL().ToWString().c_str()));
- FString ReffererURL(WCHAR_TO_TCHAR(Request->GetReferrerURL().ToWString().c_str()));
- if (ReffererURL.Contains("marketplace-website-node-launcher-") && RequestURL.Contains("graphql.epicgames.com"))
- {
- // requests from the marketplace UE4 page to graphql can exceed the header size limits so manually prune this large cookie here
- if (CefString(&Cookie.name).ToString() == "ecma")
- return false;
- }
- return true;
- }
- bool FCEFInterfaceBrowserHandler::URLRequestAllowsCredentials(const FString& URL) const
- {
- // if we inserted this URL into our map then we want to allow credentials for it
- if (MainFrameLoadTypes.Find(URL) != nullptr)
- return true;
- // check the explicit allowlist also
- for (const FString& AuthorizationHeaderAllowListURL : AuthorizationHeaderAllowListURLS)
- {
- if (URL.Contains(AuthorizationHeaderAllowListURL))
- {
- return true;
- }
- }
- return false;
- }
- #undef LOCTEXT_NAMESPACE
- #endif // WITH_CEF
|