Files
UnrealEngineUWP/Engine/Source/Runtime/WebBrowser/Private/WebBrowserWindow.cpp
Matthew Griffin 315259b0d1 Fixed issues with merge of WebBrowser code from Launcher branch
#codereview Carlos.Cuello, Keli.Hlodversson

[CL 2510109 by Matthew Griffin in Main branch]
2015-04-13 06:16:41 -04:00

591 lines
15 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "WebBrowserPrivatePCH.h"
#include "WebBrowserWindow.h"
#include "SlateCore.h"
#include "SlateBasics.h"
#include "RHI.h"
#if WITH_CEF3
FWebBrowserWindow::FWebBrowserWindow(FIntPoint InViewportSize, FString InUrl, TOptional<FString> InContentsToLoad, bool bInShowErrorMessage)
: DocumentState(EWebBrowserDocumentState::NoDocument)
, UpdatableTexture(nullptr)
, CurrentUrl(InUrl)
, ViewportSize(InViewportSize)
, bIsClosing(false)
, bHasBeenPainted(false)
, ContentsToLoad(InContentsToLoad)
, ShowErrorMessage(bInShowErrorMessage)
{
TextureData.Reserve(ViewportSize.X * ViewportSize.Y * 4);
TextureData.SetNumZeroed(ViewportSize.X * ViewportSize.Y * 4);
bTextureDataDirty = true;
if (FSlateApplication::IsInitialized() && FSlateApplication::Get().GetRenderer().IsValid())
{
UpdatableTexture = FSlateApplication::Get().GetRenderer()->CreateUpdatableTexture(ViewportSize.X, ViewportSize.Y);
}
}
FWebBrowserWindow::~FWebBrowserWindow()
{
CloseBrowser();
if (FSlateApplication::IsInitialized() && FSlateApplication::Get().GetRenderer().IsValid() && UpdatableTexture != nullptr)
{
FSlateApplication::Get().GetRenderer()->ReleaseUpdatableTexture(UpdatableTexture);
}
UpdatableTexture = nullptr;
}
void FWebBrowserWindow::LoadURL(FString NewURL)
{
if (IsValid())
{
CefRefPtr<CefFrame> MainFrame = InternalCefBrowser->GetMainFrame();
if (MainFrame.get() != nullptr)
{
CefString URL = *NewURL;
MainFrame->LoadURL(URL);
}
}
}
void FWebBrowserWindow::LoadString(FString Contents, FString DummyURL)
{
if (IsValid())
{
CefRefPtr<CefFrame> MainFrame = InternalCefBrowser->GetMainFrame();
if (MainFrame.get() != nullptr)
{
CefString StringVal = *Contents;
CefString URL = *DummyURL;
MainFrame->LoadString(StringVal, URL);
}
}
}
void FWebBrowserWindow::SetViewportSize(FIntPoint WindowSize)
{
const int32 MaxSize = GetMax2DTextureDimension();
WindowSize.X = FMath::Min(WindowSize.X, MaxSize);
WindowSize.Y = FMath::Min(WindowSize.Y, MaxSize);
// Ignore sizes that can't be seen as it forces CEF to re-render whole image
if (WindowSize.X > 0 && WindowSize.Y > 0 && ViewportSize != WindowSize)
{
FIntPoint OldViewportSize = MoveTemp(ViewportSize);
TArray<uint8> OldTextureData = MoveTemp(TextureData);
ViewportSize = MoveTemp(WindowSize);
TextureData.SetNumZeroed(ViewportSize.X * ViewportSize.Y * 4);
// copy row by row to avoid texture distortion
const int32 WriteWidth = FMath::Min(OldViewportSize.X, ViewportSize.X) * 4;
const int32 WriteHeight = FMath::Min(OldViewportSize.Y, ViewportSize.Y);
for (int32 RowIndex = 0; RowIndex < WriteHeight; ++RowIndex)
{
FMemory::Memcpy(TextureData.GetData() + ViewportSize.X * RowIndex * 4, OldTextureData.GetData() + OldViewportSize.X * RowIndex * 4, WriteWidth);
}
if (UpdatableTexture != nullptr)
{
UpdatableTexture->ResizeTexture(ViewportSize.X, ViewportSize.Y);
UpdatableTexture->UpdateTextureThreadSafe(TextureData);
bTextureDataDirty = false;
}
if (IsValid())
{
InternalCefBrowser->GetHost()->WasResized();
}
}
}
FSlateShaderResource* FWebBrowserWindow::GetTexture()
{
if (UpdatableTexture != nullptr)
{
if (bTextureDataDirty)
{
UpdatableTexture->UpdateTextureThreadSafe(TextureData);
bTextureDataDirty = false;
}
return UpdatableTexture->GetSlateResource();
}
return nullptr;
}
bool FWebBrowserWindow::IsValid() const
{
return InternalCefBrowser.get() != nullptr;
}
bool FWebBrowserWindow::HasBeenPainted() const
{
return bHasBeenPainted;
}
bool FWebBrowserWindow::IsClosing() const
{
return bIsClosing;
}
EWebBrowserDocumentState FWebBrowserWindow::GetDocumentLoadingState() const
{
return DocumentState;
}
FString FWebBrowserWindow::GetTitle() const
{
return Title;
}
FString FWebBrowserWindow::GetUrl() const
{
if (InternalCefBrowser != nullptr)
{
CefRefPtr<CefFrame> MainFrame = InternalCefBrowser->GetMainFrame();
if (MainFrame != nullptr)
{
return CurrentUrl;
}
}
return FString();
}
void FWebBrowserWindow::OnKeyDown(const FKeyEvent& InKeyEvent)
{
if (IsValid())
{
CefKeyEvent KeyEvent;
#if PLATFORM_MAC
KeyEvent.native_key_code = InKeyEvent.GetKeyCode();
KeyEvent.character = InKeyEvent.GetCharacter();
#else
KeyEvent.windows_key_code = InKeyEvent.GetKeyCode();
#endif
// TODO: Figure out whether this is a system key if we come across problems
/*KeyEvent.is_system_key = message == WM_SYSCHAR ||
message == WM_SYSKEYDOWN ||
message == WM_SYSKEYUP;*/
KeyEvent.type = KEYEVENT_RAWKEYDOWN;
KeyEvent.modifiers = GetCefKeyboardModifiers(InKeyEvent);
InternalCefBrowser->GetHost()->SendKeyEvent(KeyEvent);
}
}
void FWebBrowserWindow::OnKeyUp(const FKeyEvent& InKeyEvent)
{
if (IsValid())
{
CefKeyEvent KeyEvent;
#if PLATFORM_MAC
KeyEvent.native_key_code = InKeyEvent.GetKeyCode();
KeyEvent.character = InKeyEvent.GetCharacter();
#else
KeyEvent.windows_key_code = InKeyEvent.GetKeyCode();
#endif
// TODO: Figure out whether this is a system key if we come across problems
/*KeyEvent.is_system_key = message == WM_SYSCHAR ||
message == WM_SYSKEYDOWN ||
message == WM_SYSKEYUP;*/
KeyEvent.type = KEYEVENT_KEYUP;
KeyEvent.modifiers = GetCefKeyboardModifiers(InKeyEvent);
InternalCefBrowser->GetHost()->SendKeyEvent(KeyEvent);
}
}
void FWebBrowserWindow::OnKeyChar(const FCharacterEvent& InCharacterEvent)
{
if (IsValid())
{
CefKeyEvent KeyEvent;
#if PLATFORM_MAC
KeyEvent.character = InCharacterEvent.GetCharacter();
#else
KeyEvent.windows_key_code = InCharacterEvent.GetCharacter();
#endif
// TODO: Figure out whether this is a system key if we come across problems
/*KeyEvent.is_system_key = message == WM_SYSCHAR ||
message == WM_SYSKEYDOWN ||
message == WM_SYSKEYUP;*/
KeyEvent.type = KEYEVENT_CHAR;
KeyEvent.modifiers = GetCefInputModifiers(InCharacterEvent);
InternalCefBrowser->GetHost()->SendKeyEvent(KeyEvent);
}
}
void FWebBrowserWindow::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
if (IsValid())
{
FKey Button = MouseEvent.GetEffectingButton();
CefBrowserHost::MouseButtonType Type =
(Button == EKeys::LeftMouseButton ? MBT_LEFT : (
Button == EKeys::RightMouseButton ? MBT_RIGHT : MBT_MIDDLE));
CefMouseEvent Event;
FVector2D LocalPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
Event.x = LocalPos.X;
Event.y = LocalPos.Y;
Event.modifiers = GetCefMouseModifiers(MouseEvent);
InternalCefBrowser->GetHost()->SendMouseClickEvent(Event, Type, false,1);
}
}
void FWebBrowserWindow::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
if (IsValid())
{
FKey Button = MouseEvent.GetEffectingButton();
CefBrowserHost::MouseButtonType Type =
(Button == EKeys::LeftMouseButton ? MBT_LEFT : (
Button == EKeys::RightMouseButton ? MBT_RIGHT : MBT_MIDDLE));
CefMouseEvent Event;
FVector2D LocalPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
Event.x = LocalPos.X;
Event.y = LocalPos.Y;
Event.modifiers = GetCefMouseModifiers(MouseEvent);
InternalCefBrowser->GetHost()->SendMouseClickEvent(Event, Type, true, 1);
}
}
void FWebBrowserWindow::OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
if (IsValid())
{
FKey Button = MouseEvent.GetEffectingButton();
CefBrowserHost::MouseButtonType Type =
(Button == EKeys::LeftMouseButton ? MBT_LEFT : (
Button == EKeys::RightMouseButton ? MBT_RIGHT : MBT_MIDDLE));
CefMouseEvent Event;
FVector2D LocalPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
Event.x = LocalPos.X;
Event.y = LocalPos.Y;
Event.modifiers = GetCefMouseModifiers(MouseEvent);
InternalCefBrowser->GetHost()->SendMouseClickEvent(Event, Type, false, 2);
}
}
void FWebBrowserWindow::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
if (IsValid())
{
CefMouseEvent Event;
FVector2D LocalPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
Event.x = LocalPos.X;
Event.y = LocalPos.Y;
Event.modifiers = GetCefMouseModifiers(MouseEvent);
InternalCefBrowser->GetHost()->SendMouseMoveEvent(Event, false);
}
}
void FWebBrowserWindow::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
if(IsValid())
{
// The original delta is reduced so this should bring it back to what CEF expects
const float SpinFactor = 120.0f;
const float TrueDelta = MouseEvent.GetWheelDelta() * SpinFactor;
CefMouseEvent Event;
FVector2D LocalPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
Event.x = LocalPos.X;
Event.y = LocalPos.Y;
Event.modifiers = GetCefMouseModifiers(MouseEvent);
InternalCefBrowser->GetHost()->SendMouseWheelEvent(Event,
MouseEvent.IsShiftDown() ? TrueDelta : 0,
!MouseEvent.IsShiftDown() ? TrueDelta : 0);
}
}
void FWebBrowserWindow::OnFocus(bool SetFocus)
{
if (IsValid())
{
InternalCefBrowser->GetHost()->SendFocusEvent(SetFocus);
}
}
void FWebBrowserWindow::OnCaptureLost()
{
if (IsValid())
{
InternalCefBrowser->GetHost()->SendCaptureLostEvent();
}
}
bool FWebBrowserWindow::CanGoBack() const
{
if (IsValid())
{
return InternalCefBrowser->CanGoBack();
}
return false;
}
void FWebBrowserWindow::GoBack()
{
if (IsValid())
{
InternalCefBrowser->GoBack();
}
}
bool FWebBrowserWindow::CanGoForward() const
{
if (IsValid())
{
return InternalCefBrowser->CanGoForward();
}
return false;
}
void FWebBrowserWindow::GoForward()
{
if (IsValid())
{
InternalCefBrowser->GoForward();
}
}
bool FWebBrowserWindow::IsLoading() const
{
if (IsValid())
{
return InternalCefBrowser->IsLoading();
}
return false;
}
void FWebBrowserWindow::Reload()
{
if (IsValid())
{
InternalCefBrowser->Reload();
}
}
void FWebBrowserWindow::StopLoad()
{
if (IsValid())
{
InternalCefBrowser->StopLoad();
}
}
void FWebBrowserWindow::SetHandler(CefRefPtr<FWebBrowserHandler> InHandler)
{
if (InHandler.get())
{
Handler = InHandler;
Handler->SetBrowserWindow(SharedThis(this));
Handler->SetShowErrorMessage(ShowErrorMessage);
}
}
void FWebBrowserWindow::CloseBrowser()
{
bIsClosing = true;
if (IsValid())
{
InternalCefBrowser->GetHost()->CloseBrowser(false);
InternalCefBrowser = nullptr;
Handler = nullptr;
}
}
void FWebBrowserWindow::BindCefBrowser(CefRefPtr<CefBrowser> Browser)
{
InternalCefBrowser = Browser;
// Need to wait until this point if we want to start with a page loaded from a string
if (ContentsToLoad.IsSet())
{
LoadString(ContentsToLoad.GetValue(), CurrentUrl);
}
}
void FWebBrowserWindow::SetTitle(const CefString& InTitle)
{
Title = InTitle.ToWString().c_str();
TitleChangedEvent.Broadcast(Title);
}
void FWebBrowserWindow::SetUrl(const CefString& Url)
{
CurrentUrl = Url.ToWString().c_str();
OnUrlChanged().Broadcast(CurrentUrl);
}
bool FWebBrowserWindow::GetViewRect(CefRect& Rect)
{
Rect.x = 0;
Rect.y = 0;
Rect.width = ViewportSize.X;
Rect.height = ViewportSize.Y;
return true;
}
void FWebBrowserWindow::NotifyDocumentError()
{
DocumentState = EWebBrowserDocumentState::Error;
DocumentStateChangedEvent.Broadcast(DocumentState);
}
void FWebBrowserWindow::NotifyDocumentLoadingStateChange(bool IsLoading)
{
EWebBrowserDocumentState NewState = IsLoading
? EWebBrowserDocumentState::Loading
: EWebBrowserDocumentState::Completed;
if (DocumentState != EWebBrowserDocumentState::Error)
{
DocumentState = NewState;
}
DocumentStateChangedEvent.Broadcast(NewState);
}
void FWebBrowserWindow::OnPaint(CefRenderHandler::PaintElementType Type, const CefRenderHandler::RectList& DirtyRects, const void* Buffer, int Width, int Height)
{
const int32 BufferSize = Width*Height*4;
if (BufferSize == TextureData.Num() && DirtyRects.size() == 1 && DirtyRects[0].width == Width && DirtyRects[0].height == Height)
{
FMemory::Memcpy(TextureData.GetData(), Buffer, BufferSize);
}
else
{
for (CefRect Rect : DirtyRects)
{
// Skip rect if fully outside the viewport
if (Rect.x > ViewportSize.X || Rect.y > ViewportSize.Y)
continue;
const int32 WriteWidth = (FMath::Min(Rect.x + Rect.width, ViewportSize.X) - Rect.x) * 4;
const int32 WriteHeight = FMath::Min(Rect.y + Rect.height, ViewportSize.Y);
// copy row by row to skip pixels outside the rect
for (int32 RowIndex = Rect.y; RowIndex < WriteHeight; ++RowIndex)
{
FMemory::Memcpy(TextureData.GetData() + ( ViewportSize.X * RowIndex + Rect.x ) * 4 , static_cast<const uint8*>(Buffer) + (Width * RowIndex + Rect.x) * 4, WriteWidth);
}
}
}
bTextureDataDirty = true;
bHasBeenPainted = true;
NeedsRedrawEvent.Broadcast();
}
void FWebBrowserWindow::OnCursorChange(CefCursorHandle Cursor)
{
// TODO: Figure out Unreal cursor type from this,
// may need to reload unreal cursors to compare handles
//::SetCursor( Cursor );
}
int32 FWebBrowserWindow::GetCefKeyboardModifiers(const FKeyEvent& KeyEvent)
{
int32 Modifiers = GetCefInputModifiers(KeyEvent);
const FKey Key = KeyEvent.GetKey();
if (Key == EKeys::LeftAlt ||
Key == EKeys::LeftCommand ||
Key == EKeys::LeftControl ||
Key == EKeys::LeftShift)
{
Modifiers |= EVENTFLAG_IS_LEFT;
}
if (Key == EKeys::RightAlt ||
Key == EKeys::RightCommand ||
Key == EKeys::RightControl ||
Key == EKeys::RightShift)
{
Modifiers |= EVENTFLAG_IS_RIGHT;
}
if (Key == EKeys::NumPadZero ||
Key == EKeys::NumPadOne ||
Key == EKeys::NumPadTwo ||
Key == EKeys::NumPadThree ||
Key == EKeys::NumPadFour ||
Key == EKeys::NumPadFive ||
Key == EKeys::NumPadSix ||
Key == EKeys::NumPadSeven ||
Key == EKeys::NumPadEight ||
Key == EKeys::NumPadNine)
{
Modifiers |= EVENTFLAG_IS_KEY_PAD;
}
return Modifiers;
}
int32 FWebBrowserWindow::GetCefMouseModifiers(const FPointerEvent& InMouseEvent)
{
int32 Modifiers = GetCefInputModifiers(InMouseEvent);
if (InMouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton))
{
Modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON;
}
if (InMouseEvent.IsMouseButtonDown(EKeys::MiddleMouseButton))
{
Modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON;
}
if (InMouseEvent.IsMouseButtonDown(EKeys::RightMouseButton))
{
Modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON;
}
return Modifiers;
}
int32 FWebBrowserWindow::GetCefInputModifiers(const FInputEvent& InputEvent)
{
int32 Modifiers = 0;
if (InputEvent.IsShiftDown())
{
Modifiers |= EVENTFLAG_SHIFT_DOWN;
}
if (InputEvent.IsControlDown())
{
Modifiers |= EVENTFLAG_CONTROL_DOWN;
}
if (InputEvent.IsAltDown())
{
Modifiers |= EVENTFLAG_ALT_DOWN;
}
if (InputEvent.IsCommandDown())
{
Modifiers |= EVENTFLAG_COMMAND_DOWN;
}
if (InputEvent.AreCapsLocked())
{
Modifiers |= EVENTFLAG_CAPS_LOCK_ON;
}
// TODO: Add function for this if necessary
/*if (InputEvent.AreNumsLocked())
{
Modifiers |= EVENTFLAG_NUM_LOCK_ON;
}*/
return Modifiers;
}
#endif