Files
UnrealEngineUWP/Engine/Source/Runtime/WebBrowser/Private/WebBrowserHandler.cpp
Keli Hlodversson 97a2e4a830 [INTEGRATE] SWebBrowser: Add hooks to to be able to handle alert and message boxes with a custom handler
OPP-3413
rb: Justin.Sargent
Merging CL#2637687 using UE4-To-UE4-LauncherDev

[CL 2638717 by Keli Hlodversson in Main branch]
2015-07-30 10:49:18 -04:00

494 lines
14 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "WebBrowserPrivatePCH.h"
#include "WebBrowserHandler.h"
#include "WebBrowserWindow.h"
#include "WebBrowserSingleton.h"
#include "WebBrowserPopupFeatures.h"
#define LOCTEXT_NAMESPACE "WebBrowserHandler"
#if WITH_CEF3
FWebBrowserHandler::FWebBrowserHandler()
: ShowErrorMessage(true)
{
}
void FWebBrowserHandler::OnTitleChange(CefRefPtr<CefBrowser> Browser, const CefString& Title)
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
BrowserWindow->SetTitle(Title);
}
}
void FWebBrowserHandler::OnAddressChange(CefRefPtr<CefBrowser> Browser, CefRefPtr<CefFrame> Frame, const CefString& Url)
{
if (Frame->IsMain())
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
BrowserWindow->SetUrl(Url);
}
}
}
bool FWebBrowserHandler::OnTooltip(CefRefPtr<CefBrowser> Browser, CefString& Text)
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
BrowserWindow->SetToolTip(Text);
}
return false;
}
void FWebBrowserHandler::OnAfterCreated(CefRefPtr<CefBrowser> Browser)
{
if(Browser->IsPopup())
{
TSharedPtr<FWebBrowserWindow> BrowserWindowParent = BrowserWindowParentPtr.Pin();
if(BrowserWindowParent.IsValid() && BrowserWindowParent->SupportsNewWindows())
{
TSharedPtr<FWebBrowserWindowInfo> NewBrowserWindowInfo = MakeShareable(new FWebBrowserWindowInfo(Browser, this));
TSharedPtr<IWebBrowserWindow> NewBrowserWindow = IWebBrowserModule::Get().GetSingleton()->CreateBrowserWindow(
BrowserWindowParent,
NewBrowserWindowInfo
);
{
// @todo: At the moment we need to downcast since the handler does not support using the interface.
TSharedPtr<FWebBrowserWindow> HandlerSpecificBrowserWindow = StaticCastSharedPtr<FWebBrowserWindow>(NewBrowserWindow);
BrowserWindowPtr = HandlerSpecificBrowserWindow;
}
// Request a UI window for the browser. If it is not created we do some cleanup.
bool bUIWindowCreated = BrowserWindowParent->RequestCreateWindow(NewBrowserWindow.ToSharedRef(), BrowserPopupFeatures);
if(!bUIWindowCreated)
{
NewBrowserWindow->CloseBrowser(true);
}
}
else
{
Browser->GetHost()->CloseBrowser(true);
}
}
}
bool FWebBrowserHandler::DoClose(CefRefPtr<CefBrowser> Browser)
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if(BrowserWindow.IsValid())
{
BrowserWindow->OnBrowserClosing();
}
return false;
}
void FWebBrowserHandler::OnBeforeClose(CefRefPtr<CefBrowser> Browser)
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
BrowserWindow->OnBrowserClosed();
}
}
bool FWebBrowserHandler::OnBeforePopup( CefRefPtr<CefBrowser> Browser,
CefRefPtr<CefFrame> Frame,
const CefString& TargetUrl,
const CefString& TArgetFrameName,
const CefPopupFeatures& PopupFeatures,
CefWindowInfo& WindowInfo,
CefRefPtr<CefClient>& Client,
CefBrowserSettings& Settings,
bool* NoJavascriptAccess )
{
// By default, we ignore any popup requests unless they are handled by us in some way.
bool bSupressCEFWindowCreation = true;
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
bSupressCEFWindowCreation = BrowserWindow->OnCefBeforePopup(TargetUrl, TArgetFrameName);
if(!bSupressCEFWindowCreation)
{
if(BrowserWindow->SupportsNewWindows())
{
CefRefPtr<FWebBrowserHandler> NewHandler(new FWebBrowserHandler);
NewHandler->SetBrowserWindowParent(BrowserWindow);
NewHandler->SetPopupFeatures(MakeShareable(new FWebBrowserPopupFeatures(PopupFeatures)));
Client = NewHandler;
CefWindowHandle ParentWindowHandle = BrowserWindow->GetCefBrowser()->GetHost()->GetWindowHandle();
// Allow overriding transparency setting for child windows
bool bUseTransparency = BrowserWindow->UseTransparency()
? NewHandler->BrowserPopupFeatures->GetAdditionalFeatures().Find(TEXT("Epic_NoTransparency")) == INDEX_NONE
: NewHandler->BrowserPopupFeatures->GetAdditionalFeatures().Find(TEXT("Epic_UseTransparency")) != INDEX_NONE;
// Always use off screen rendering so we can integrate with our windows
WindowInfo.SetAsWindowless(ParentWindowHandle, bUseTransparency);
// We need to rely on CEF to create our window so we set the WindowInfo, BrowserSettings, Client, and then return false
bSupressCEFWindowCreation = false;
}
else
{
bSupressCEFWindowCreation = true;
}
}
}
return bSupressCEFWindowCreation;
}
void FWebBrowserHandler::OnLoadError(CefRefPtr<CefBrowser> Browser,
CefRefPtr<CefFrame> Frame,
CefLoadHandler::ErrorCode InErrorCode,
const CefString& ErrorText,
const CefString& FailedUrl)
{
// Don't display an error for downloaded files.
if (InErrorCode == ERR_ABORTED)
{
return;
}
// notify browser window
if (Frame->IsMain())
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
BrowserWindow->NotifyDocumentError();
}
}
// Display a load error message.
if (ShowErrorMessage)
{
FFormatNamedArguments Args;
{
Args.Add(TEXT("FailedUrl"), FText::FromString(FailedUrl.ToWString().c_str()));
Args.Add(TEXT("ErrorText"), FText::FromString(ErrorText.ToWString().c_str()));
Args.Add(TEXT("ErrorCode"), FText::AsNumber(InErrorCode));
}
FText ErrorMsg = FText::Format(LOCTEXT("WebBrowserLoadError", "Failed to load URL {FailedUrl} with error {ErrorText} ({ErrorCode})."), Args);
FString ErrorHTML = TEXT("<html><body bgcolor=\"white\"><h2>") + ErrorMsg.ToString() + TEXT("</h2></body></html>");
Frame->LoadString(*ErrorHTML, FailedUrl);
}
}
void FWebBrowserHandler::OnLoadStart(CefRefPtr<CefBrowser> Browser, CefRefPtr<CefFrame> Frame)
{
}
void FWebBrowserHandler::OnLoadingStateChange(CefRefPtr<CefBrowser> Browser, bool bIsLoading, bool bCanGoBack, bool bCanGoForward)
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
BrowserWindow->NotifyDocumentLoadingStateChange(bIsLoading);
}
}
bool FWebBrowserHandler::GetViewRect(CefRefPtr<CefBrowser> Browser, CefRect& Rect)
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
return BrowserWindow->GetViewRect(Rect);
}
else
{
// Seems that if we return false from here or without a size > 0, popup menus will not show up.
Rect.width = 800;
Rect.height = 600;
return true;
}
}
void FWebBrowserHandler::OnPaint(CefRefPtr<CefBrowser> Browser,
PaintElementType Type,
const RectList& DirtyRects,
const void* Buffer,
int Width, int Height)
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
BrowserWindow->OnPaint(Type, DirtyRects, Buffer, Width, Height);
}
}
void FWebBrowserHandler::OnCursorChange(CefRefPtr<CefBrowser> Browser, CefCursorHandle Cursor, CefRenderHandler::CursorType Type, const CefCursorInfo& CustomCursorInfo)
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
BrowserWindow->OnCursorChange(Cursor, Type, CustomCursorInfo);
}
}
void FWebBrowserHandler::OnPopupShow(CefRefPtr<CefBrowser> Browser, bool bShow)
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
BrowserWindow->ShowPopupMenu(bShow);
}
}
void FWebBrowserHandler::OnPopupSize(CefRefPtr<CefBrowser> Browser, const CefRect& Rect)
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
BrowserWindow->SetPopupMenuPosition(Rect);
}
}
bool FWebBrowserHandler::OnBeforeResourceLoad(CefRefPtr<CefBrowser> Browser, CefRefPtr<CefFrame> Frame, CefRefPtr<CefRequest> Request)
{
const FString LanguageHeaderText(TEXT("Accept-Language"));
const FString LocaleCode = FWebBrowserSingleton::GetCurrentLocaleCode();
CefRequest::HeaderMap HeaderMap;
Request->GetHeaderMap(HeaderMap);
auto LanguageHeader = HeaderMap.find(*LanguageHeaderText);
if (LanguageHeader != HeaderMap.end())
{
(*LanguageHeader).second = *LocaleCode;
}
else
{
HeaderMap.insert(std::pair<CefString, CefString>(*LanguageHeaderText, *LocaleCode));
}
Request->SetHeaderMap(HeaderMap);
return false;
}
void FWebBrowserHandler::OnRenderProcessTerminated(CefRefPtr<CefBrowser> Browser, TerminationStatus Status)
{
}
bool FWebBrowserHandler::OnBeforeBrowse(CefRefPtr<CefBrowser> Browser,
CefRefPtr<CefFrame> Frame,
CefRefPtr<CefRequest> Request,
bool IsRedirect)
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
if(BrowserWindow->OnBeforeBrowse(Browser, Frame, Request, IsRedirect))
{
return true;
}
}
return false;
}
CefRefPtr<CefResourceHandler> FWebBrowserHandler::GetResourceHandler( CefRefPtr<CefBrowser> Browser, CefRefPtr< CefFrame > Frame, CefRefPtr< CefRequest > Request )
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
return BrowserWindow->GetResourceHandler(Frame, Request);
}
return NULL;
}
void FWebBrowserHandler::SetBrowserWindow(TSharedPtr<FWebBrowserWindow> InBrowserWindow)
{
BrowserWindowPtr = InBrowserWindow;
}
void FWebBrowserHandler::SetBrowserWindowParent(TSharedPtr<FWebBrowserWindow> InBrowserWindow)
{
BrowserWindowParentPtr = InBrowserWindow;
}
bool FWebBrowserHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> Browser,
CefProcessId SourceProcess,
CefRefPtr<CefProcessMessage> Message)
{
bool Retval = false;
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
Retval = BrowserWindow->OnProcessMessageReceived(Browser, SourceProcess, Message);
}
return Retval;
}
bool FWebBrowserHandler::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.locationBarVisible = false;
PopupFeatures.menuBarVisible = false;
PopupFeatures.toolBarVisible = false;
PopupFeatures.statusBarVisible = false;
PopupFeatures.resizable = true;
PopupFeatures.additionalFeatures = cef_string_list_alloc();
// Override the parent window setting for transparency, as the Dev Tools require a solid background
CefString OverrideTransparencyFeature = "Epic_NoTransparency";
cef_string_list_append(PopupFeatures.additionalFeatures, OverrideTransparencyFeature.GetStruct());
// 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 FWebBrowserHandler::OnKeyEvent(CefRefPtr<CefBrowser> Browser,
const CefKeyEvent& Event,
CefEventHandle OsEvent)
{
#if UE_BUILD_DEBUG
// Show dev tools on CMD/CTRL+ALT+I
if( (Event.type == KEYEVENT_RAWKEYDOWN || Event.type == KEYEVENT_KEYDOWN) &&
#if PLATFORM_MAC
(Event.modifiers == (EVENTFLAG_COMMAND_DOWN | EVENTFLAG_SHIFT_DOWN)) &&
#else
(Event.modifiers == (EVENTFLAG_CONTROL_DOWN | EVENTFLAG_SHIFT_DOWN)) &&
#endif
(Event.unmodified_character == 'i' || Event.unmodified_character == 'I')
)
{
return ShowDevTools(Browser);
}
#endif
#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<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
return BrowserWindow->OnUnhandledKeyEvent(Event);
}
return false;
}
bool FWebBrowserHandler::OnJSDialog(CefRefPtr<CefBrowser> Browser, const CefString& OriginUrl, const CefString& AcceptLang, JSDialogType DialogType, const CefString& MessageText, const CefString& DefaultPromptText, CefRefPtr<CefJSDialogCallback> Callback, bool& OutSuppressMessage)
{
bool Retval = false;
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
Retval = BrowserWindow->OnJSDialog(DialogType, MessageText, DefaultPromptText, Callback, OutSuppressMessage);
}
return Retval;
}
bool FWebBrowserHandler::OnBeforeUnloadDialog(CefRefPtr<CefBrowser> Browser, const CefString& MessageText, bool IsReload, CefRefPtr<CefJSDialogCallback> Callback)
{
bool Retval = false;
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
Retval = BrowserWindow->OnBeforeUnloadDialog(MessageText, IsReload, Callback);
}
return Retval;
}
void FWebBrowserHandler::OnResetDialogState(CefRefPtr<CefBrowser> Browser)
{
TSharedPtr<FWebBrowserWindow> BrowserWindow = BrowserWindowPtr.Pin();
if (BrowserWindow.IsValid())
{
BrowserWindow->OnResetDialogState();
}
}
#endif // WITH_CEF
#undef LOCTEXT_NAMESPACE