Skip to content
This repository has been archived by the owner on Jun 5, 2023. It is now read-only.

Add draft implementation of the WebView using EdgeHtml control #16

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions src/WebWindow.Native/IAsyncOperationHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#pragma once

#include <combaseapi.h>

namespace AsyncOpHelpers
{
template <typename TIOperationResult, typename TIResults>
HRESULT WaitForCompletionAndGetResults(
ABI::Windows::Foundation::IAsyncOperation<TIOperationResult>* operationIn,
TIResults* results)
{
typedef ABI::Windows::Foundation::IAsyncOperation<TIOperationResult> TIOperation;
typedef ABI::Windows::Foundation::IAsyncOperationCompletedHandler<TIOperationResult> TIDelegate;

class EventDelegate :
public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::Delegate>,
TIDelegate,
Microsoft::WRL::FtmBase>
{
public:
EventDelegate()
: _status(AsyncStatus::Started), _hEventCompleted(nullptr)
{
}

HRESULT RuntimeClassInitialize()
{
_hEventCompleted = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);

return _hEventCompleted == nullptr ? HRESULT_FROM_WIN32(GetLastError()) : S_OK;
}


HRESULT __stdcall Invoke(
_In_ TIOperation*,
_In_ AsyncStatus status)
{
_status = status;
SetEvent(_hEventCompleted);
return S_OK;
}

HANDLE GetEvent()
{
return _hEventCompleted;
}

AsyncStatus GetStatus()
{
return _status;
}

private:
~EventDelegate()
{
CloseHandle(_hEventCompleted);
}

AsyncStatus _status;
HANDLE _hEventCompleted;
};


Microsoft::WRL::ComPtr<TIOperation> operation = operationIn;
Microsoft::WRL::ComPtr<EventDelegate> eventCallback;

HRESULT hr = Microsoft::WRL::MakeAndInitialize<EventDelegate>(&eventCallback);


if (SUCCEEDED(hr))
{
hr = operation->put_Completed(eventCallback.Get());

if (SUCCEEDED(hr))
{
HANDLE waitForEvents[1] = {eventCallback->GetEvent()};
DWORD handleCount = ARRAYSIZE(waitForEvents);
DWORD handleIndex = 0;
HRESULT hr = CoWaitForMultipleHandles(0, INFINITE, handleCount, waitForEvents, &handleIndex);

if (SUCCEEDED(hr) && handleIndex != 0)
{
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
}

if (SUCCEEDED(hr))
{
if (eventCallback->GetStatus() == AsyncStatus::Completed)
{
hr = operation->GetResults(results);
}
else
{
Microsoft::WRL::ComPtr<IAsyncInfo> asyncInfo;

if (SUCCEEDED(operation->QueryInterface(IID_PPV_ARGS(&asyncInfo))))
{
asyncInfo->get_ErrorCode(&hr);
}
}
}
}
}

return hr;
}
}
5 changes: 3 additions & 2 deletions src/WebWindow.Native/WebWindow.Native.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;shlwapi.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
Expand Down Expand Up @@ -143,7 +143,7 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;shlwapi.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
Expand All @@ -152,6 +152,7 @@
<ClCompile Include="WebWindow.Windows.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="IAsyncOperationHelper.h" />
<ClInclude Include="WebWindow.h" />
</ItemGroup>
<ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions src/WebWindow.Native/WebWindow.Native.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
<ClInclude Include="WebWindow.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IAsyncOperationHelper.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
Expand Down
192 changes: 179 additions & 13 deletions src/WebWindow.Native/WebWindow.Windows.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
#include "WebWindow.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>

#include <windows.web.ui.h>
#include <windows.web.ui.interop.h>
#include <windows.foundation.h>
#include <windows.foundation.collections.h>
#include <wrl.h>
#include <wrl/wrappers/corewrappers.h>
#include <stdio.h>
#include <map>
#include <mutex>
#include <condition_variable>
#include <comdef.h>
#include <atomic>
#include <Shlwapi.h>
#include "IAsyncOperationHelper.h"
#include <strsafe.h>

#define WM_USER_SHOWMESSAGE (WM_USER + 0x0001)
#define WM_USER_INVOKE (WM_USER + 0x0002)

using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Storage::Streams;
using namespace ABI::Windows::Web::UI;
using namespace ABI::Windows::Web::UI::Interop;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LPCWSTR CLASS_NAME = L"WebWindow";
Expand All @@ -32,6 +48,70 @@ struct ShowMessageParams
UINT type;
};

void CheckFailure(_In_ HRESULT hr)
{
if (FAILED(hr))
{
WCHAR message[512] = L"";
StringCchPrintf(message, ARRAYSIZE(message), L"Error: 0x%x", hr);
MessageBoxW(nullptr, message, nullptr, MB_OK);
ExitProcess(-1);
}
}

template <typename TInterface>
Microsoft::WRL::ComPtr<TInterface> GetActivationFactoryFailFast(_In_z_ PCWSTR factoryClassName)
{
ComPtr<TInterface> factoryInstance;
CheckFailure(RoGetActivationFactory(
HStringReference(factoryClassName).Get(),
IID_PPV_ARGS(&factoryInstance)));
return factoryInstance;
}

template <typename TInterface>
Microsoft::WRL::ComPtr<TInterface> ActivateInstanceFailFast(_In_z_ PCWSTR className)
{
ComPtr<TInterface> classInstanceAsInspectable;
ComPtr<TInterface> classInstance;
CheckFailure(RoActivateInstance(
HStringReference(className).Get(),
&classInstanceAsInspectable));
CheckFailure(classInstanceAsInspectable.As(&classInstance));
return classInstance;
}

ComPtr<IUriRuntimeClass> CreateWinRtUri(_In_z_ PCWSTR uri, _In_ bool allowInvalidUri = false)
{
auto uriRuntimeClassFactory = GetActivationFactoryFailFast<IUriRuntimeClassFactory>(RuntimeClass_Windows_Foundation_Uri);
ComPtr<IUriRuntimeClass> uriRuntimeClass;
if (!allowInvalidUri)
{
CheckFailure(uriRuntimeClassFactory->CreateUri(HStringReference(uri).Get(), &uriRuntimeClass));
}
else
{
uriRuntimeClassFactory->CreateUri(HStringReference(uri).Get(), &uriRuntimeClass);
}
return uriRuntimeClass;
}

Rect HwndWindowRectToBoundsRect(_In_ HWND hwnd)
{
RECT windowRect = { 0 };
GetWindowRect(hwnd, &windowRect);

Rect bounds =
{
0,
0,
static_cast<float>(windowRect.right - windowRect.left),
static_cast<float>(windowRect.bottom - windowRect.top)
};

return bounds;
}

void WebWindow::Register(HINSTANCE hInstance)
{
_hInstance = hInstance;
Expand Down Expand Up @@ -120,6 +200,9 @@ LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
int width, height;
webWindow->GetSize(&width, &height);
webWindow->InvokeResized(width, height);
}
else {

}
return 0;
}
Expand Down Expand Up @@ -148,6 +231,22 @@ void WebWindow::RefitContent()
GetClientRect(_hWnd, &bounds);
_webviewWindow->put_Bounds(bounds);
}
else {
if (m_webViewControl)
{
RECT hwndBounds = { 0 };
GetClientRect(_hWnd, &hwndBounds);
const int clientWidth = hwndBounds.right - hwndBounds.left;
const int clientHeight = hwndBounds.bottom - hwndBounds.top;

SetWindowPos(_hWnd, nullptr, 0, 0, clientWidth, clientHeight - 0, SWP_NOZORDER);

Rect bounds = HwndWindowRectToBoundsRect(_hWnd);
ComPtr<IWebViewControlSite> site;
CheckFailure(m_webViewControl.As(&site));
CheckFailure(site->put_Bounds(bounds));
}
}
}

void WebWindow::SetTitle(AutoString title)
Expand Down Expand Up @@ -201,7 +300,7 @@ void WebWindow::Invoke(ACTION callback)
waitInfo.completionNotifier.wait(uLock, [&] { return waitInfo.isCompleted; });
}

void WebWindow::AttachWebView()
bool WebWindow::AttachWebViewChromium()
{
std::atomic_flag flag = ATOMIC_FLAG_INIT;
flag.test_and_set();
Expand Down Expand Up @@ -291,26 +390,93 @@ void WebWindow::AttachWebView()

if (envResult != S_OK)
{
_com_error err(envResult);
LPCTSTR errMsg = err.ErrorMessage();
MessageBox(_hWnd, errMsg, L"Error instantiating webview", MB_OK);
return false;
}
else

// Block until it's ready. This simplifies things for the caller, so they
// don't need to regard this process as async.
MSG msg = { };
while (flag.test_and_set() && GetMessage(&msg, NULL, 0, 0))
{
// Block until it's ready. This simplifies things for the caller, so they
// don't need to regard this process as async.
MSG msg = { };
while (flag.test_and_set() && GetMessage(&msg, NULL, 0, 0))
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return true;
}

bool WebWindow::AttachWebViewEdge()
{
// winrt::init_apartment(winrt::apartment_type::single_threaded);
HRESULT envResult = RoInitialize(RO_INIT_SINGLETHREADED);

// Use default options if options weren't set on the App during App::RunNewInstance
if (!m_processOptions)
{
m_processOptions = ActivateInstanceFailFast<IWebViewControlProcessOptions>(RuntimeClass_Windows_Web_UI_Interop_WebViewControlProcessOptions);
}

// Use a default new process if one wasn't set on the App during App::RunNewInstance
if (!m_process)
{
ComPtr<IWebViewControlProcessFactory> webViewControlProcessFactory = GetActivationFactoryFailFast<IWebViewControlProcessFactory>(RuntimeClass_Windows_Web_UI_Interop_WebViewControlProcess);
CheckFailure(webViewControlProcessFactory->CreateWithOptions(m_processOptions.Get(), &m_process));
}

ComPtr<IAsyncOperation<WebViewControl*>> createWebViewAsyncOperation;
CheckFailure(m_process->CreateWebViewControlAsync(
reinterpret_cast<INT64>(_hWnd),
HwndWindowRectToBoundsRect(_hWnd),
&createWebViewAsyncOperation));

CheckFailure(AsyncOpHelpers::WaitForCompletionAndGetResults(createWebViewAsyncOperation.Get(), m_webViewControl.ReleaseAndGetAddressOf()));

EventRegistrationToken token = { 0 };
HRESULT hr = m_webViewControl->add_ContentLoading(Callback<ITypedEventHandler<IWebViewControl*, WebViewControlContentLoadingEventArgs*>>(
[this](IWebViewControl* webViewControl, IWebViewControlContentLoadingEventArgs* args) -> HRESULT
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
ComPtr<IUriRuntimeClass> uri;
CheckFailure(args->get_Uri(&uri));
HString uriAsHString;
CheckFailure(uri->get_AbsoluteUri(uriAsHString.ReleaseAndGetAddressOf()));
//SetWindowText(m_addressbarWindow, uriAsHString.GetRawBuffer(nullptr));
return S_OK;
}).Get(), &token);
CheckFailure(hr);

//_com_error err(envResult);
//LPCTSTR errMsg = err.ErrorMessage();
//MessageBox(_hWnd, errMsg, L"Error instantiating webview", MB_OK);

return envResult == S_OK;
}

void WebWindow::AttachWebView()
{
if (AttachWebViewChromium())
{
return;
}

// Fallback to Edge rendering.
AttachWebViewEdge();
}

void WebWindow::NavigateToUrl(AutoString url)
{
_webviewWindow->Navigate(url);
if (_webviewWindow)
{
_webviewWindow->Navigate(url);
}
else
{
ComPtr<IUriRuntimeClass> uri = CreateWinRtUri(url, true);
if (uri != nullptr)
{
//m_webViewControl->Navigate(uri.Get());
CheckFailure(m_webViewControl->Navigate(uri.Get()));
}
}
}

void WebWindow::NavigateToString(AutoString content)
Expand Down
Loading