mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
999 lines
33 KiB
C++
999 lines
33 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE 6 technical preview.
|
|
Copyright (c) 2020 - Raw Material Software Limited
|
|
|
|
You may use this code under the terms of the GPL v3
|
|
(see www.gnu.org/licenses).
|
|
|
|
For this technical preview, this file is not subject to commercial licensing.
|
|
|
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
|
DISCLAIMED.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
namespace juce
|
|
{
|
|
|
|
struct InternalWebViewType
|
|
{
|
|
InternalWebViewType() {}
|
|
virtual ~InternalWebViewType() {}
|
|
|
|
virtual void createBrowser() = 0;
|
|
virtual bool hasBrowserBeenCreated() = 0;
|
|
|
|
virtual void goToURL (const String&, const StringArray*, const MemoryBlock*) = 0;
|
|
|
|
virtual void stop() = 0;
|
|
virtual void goBack() = 0;
|
|
virtual void goForward() = 0;
|
|
virtual void refresh() = 0;
|
|
|
|
virtual void focusGained() {}
|
|
virtual void setWebViewSize (int, int) = 0;
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InternalWebViewType)
|
|
};
|
|
|
|
#if JUCE_MINGW
|
|
JUCE_DECLARE_UUID_GETTER (IOleClientSite, "00000118-0000-0000-c000-000000000046")
|
|
JUCE_DECLARE_UUID_GETTER (IDispatch, "00020400-0000-0000-c000-000000000046")
|
|
|
|
#ifndef WebBrowser
|
|
class WebBrowser;
|
|
#endif
|
|
#endif
|
|
|
|
JUCE_DECLARE_UUID_GETTER (DWebBrowserEvents2, "34A715A0-6587-11D0-924A-0020AFC7AC4D")
|
|
JUCE_DECLARE_UUID_GETTER (IConnectionPointContainer, "B196B284-BAB4-101A-B69C-00AA00341D07")
|
|
JUCE_DECLARE_UUID_GETTER (IWebBrowser2, "D30C1661-CDAF-11D0-8A3E-00C04FC9E26E")
|
|
JUCE_DECLARE_UUID_GETTER (WebBrowser, "8856F961-340A-11D0-A96B-00C04FD705A2")
|
|
|
|
//==============================================================================
|
|
class Win32WebView : public InternalWebViewType,
|
|
public ActiveXControlComponent
|
|
{
|
|
public:
|
|
Win32WebView (WebBrowserComponent& owner)
|
|
{
|
|
owner.addAndMakeVisible (this);
|
|
}
|
|
|
|
~Win32WebView() override
|
|
{
|
|
if (connectionPoint != nullptr)
|
|
connectionPoint->Unadvise (adviseCookie);
|
|
|
|
if (browser != nullptr)
|
|
browser->Release();
|
|
}
|
|
|
|
void createBrowser() override
|
|
{
|
|
auto webCLSID = __uuidof (WebBrowser);
|
|
createControl (&webCLSID);
|
|
|
|
auto iidWebBrowser2 = __uuidof (IWebBrowser2);
|
|
auto iidConnectionPointContainer = __uuidof (IConnectionPointContainer);
|
|
|
|
browser = (IWebBrowser2*) queryInterface (&iidWebBrowser2);
|
|
|
|
if (auto connectionPointContainer = (IConnectionPointContainer*) queryInterface (&iidConnectionPointContainer))
|
|
{
|
|
connectionPointContainer->FindConnectionPoint (__uuidof (DWebBrowserEvents2), &connectionPoint);
|
|
|
|
if (connectionPoint != nullptr)
|
|
{
|
|
auto* owner = dynamic_cast<WebBrowserComponent*> (Component::getParentComponent());
|
|
jassert (owner != nullptr);
|
|
|
|
auto handler = new EventHandler (*owner);
|
|
connectionPoint->Advise (handler, &adviseCookie);
|
|
handler->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool hasBrowserBeenCreated() override
|
|
{
|
|
return browser != nullptr;
|
|
}
|
|
|
|
void goToURL (const String& url, const StringArray* headers, const MemoryBlock* postData) override
|
|
{
|
|
if (browser != nullptr)
|
|
{
|
|
VARIANT headerFlags, frame, postDataVar, headersVar; // (_variant_t isn't available in all compilers)
|
|
VariantInit (&headerFlags);
|
|
VariantInit (&frame);
|
|
VariantInit (&postDataVar);
|
|
VariantInit (&headersVar);
|
|
|
|
if (headers != nullptr)
|
|
{
|
|
V_VT (&headersVar) = VT_BSTR;
|
|
V_BSTR (&headersVar) = SysAllocString ((const OLECHAR*) headers->joinIntoString ("\r\n").toWideCharPointer());
|
|
}
|
|
|
|
if (postData != nullptr && postData->getSize() > 0)
|
|
{
|
|
auto sa = SafeArrayCreateVector (VT_UI1, 0, (ULONG) postData->getSize());
|
|
|
|
if (sa != nullptr)
|
|
{
|
|
void* data = nullptr;
|
|
SafeArrayAccessData (sa, &data);
|
|
jassert (data != nullptr);
|
|
|
|
if (data != nullptr)
|
|
{
|
|
postData->copyTo (data, 0, postData->getSize());
|
|
SafeArrayUnaccessData (sa);
|
|
|
|
VARIANT postDataVar2;
|
|
VariantInit (&postDataVar2);
|
|
V_VT (&postDataVar2) = VT_ARRAY | VT_UI1;
|
|
V_ARRAY (&postDataVar2) = sa;
|
|
|
|
sa = nullptr;
|
|
postDataVar = postDataVar2;
|
|
}
|
|
else
|
|
{
|
|
SafeArrayDestroy (sa);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto urlBSTR = SysAllocString ((const OLECHAR*) url.toWideCharPointer());
|
|
browser->Navigate (urlBSTR, &headerFlags, &frame, &postDataVar, &headersVar);
|
|
SysFreeString (urlBSTR);
|
|
|
|
VariantClear (&headerFlags);
|
|
VariantClear (&frame);
|
|
VariantClear (&postDataVar);
|
|
VariantClear (&headersVar);
|
|
}
|
|
}
|
|
|
|
void stop() override
|
|
{
|
|
if (browser != nullptr)
|
|
browser->Stop();
|
|
}
|
|
|
|
void goBack() override
|
|
{
|
|
if (browser != nullptr)
|
|
browser->GoBack();
|
|
}
|
|
|
|
void goForward() override
|
|
{
|
|
if (browser != nullptr)
|
|
browser->GoForward();
|
|
}
|
|
|
|
void refresh() override
|
|
{
|
|
if (browser != nullptr)
|
|
browser->Refresh();
|
|
}
|
|
|
|
void focusGained() override
|
|
{
|
|
auto iidOleObject = __uuidof (IOleObject);
|
|
auto iidOleWindow = __uuidof (IOleWindow);
|
|
|
|
if (auto oleObject = (IOleObject*) queryInterface (&iidOleObject))
|
|
{
|
|
if (auto oleWindow = (IOleWindow*) queryInterface (&iidOleWindow))
|
|
{
|
|
IOleClientSite* oleClientSite = nullptr;
|
|
|
|
if (SUCCEEDED (oleObject->GetClientSite (&oleClientSite)))
|
|
{
|
|
HWND hwnd;
|
|
oleWindow->GetWindow (&hwnd);
|
|
oleObject->DoVerb (OLEIVERB_UIACTIVATE, nullptr, oleClientSite, 0, hwnd, nullptr);
|
|
oleClientSite->Release();
|
|
}
|
|
|
|
oleWindow->Release();
|
|
}
|
|
|
|
oleObject->Release();
|
|
}
|
|
}
|
|
|
|
void setWebViewSize (int width, int height) override
|
|
{
|
|
setSize (width, height);
|
|
}
|
|
|
|
private:
|
|
IWebBrowser2* browser = nullptr;
|
|
IConnectionPoint* connectionPoint = nullptr;
|
|
DWORD adviseCookie = 0;
|
|
|
|
//==============================================================================
|
|
struct EventHandler : public ComBaseClassHelper<IDispatch>,
|
|
public ComponentMovementWatcher
|
|
{
|
|
EventHandler (WebBrowserComponent& w) : ComponentMovementWatcher (&w), owner (w) {}
|
|
|
|
JUCE_COMRESULT GetTypeInfoCount (UINT*) override { return E_NOTIMPL; }
|
|
JUCE_COMRESULT GetTypeInfo (UINT, LCID, ITypeInfo**) override { return E_NOTIMPL; }
|
|
JUCE_COMRESULT GetIDsOfNames (REFIID, LPOLESTR*, UINT, LCID, DISPID*) override { return E_NOTIMPL; }
|
|
|
|
JUCE_COMRESULT Invoke (DISPID dispIdMember, REFIID /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, DISPPARAMS* pDispParams,
|
|
VARIANT* /*pVarResult*/, EXCEPINFO* /*pExcepInfo*/, UINT* /*puArgErr*/) override
|
|
{
|
|
if (dispIdMember == DISPID_BEFORENAVIGATE2)
|
|
{
|
|
*pDispParams->rgvarg->pboolVal
|
|
= owner.pageAboutToLoad (getStringFromVariant (pDispParams->rgvarg[5].pvarVal)) ? VARIANT_FALSE
|
|
: VARIANT_TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
if (dispIdMember == 273 /*DISPID_NEWWINDOW3*/)
|
|
{
|
|
owner.newWindowAttemptingToLoad (pDispParams->rgvarg[0].bstrVal);
|
|
*pDispParams->rgvarg[3].pboolVal = VARIANT_TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
if (dispIdMember == DISPID_DOCUMENTCOMPLETE)
|
|
{
|
|
owner.pageFinishedLoading (getStringFromVariant (pDispParams->rgvarg[0].pvarVal));
|
|
return S_OK;
|
|
}
|
|
|
|
if (dispIdMember == 271 /*DISPID_NAVIGATEERROR*/)
|
|
{
|
|
int statusCode = pDispParams->rgvarg[1].pvarVal->intVal;
|
|
*pDispParams->rgvarg[0].pboolVal = VARIANT_FALSE;
|
|
|
|
// IWebBrowser2 also reports http status codes here, we need
|
|
// report only network errors
|
|
if (statusCode < 0)
|
|
{
|
|
LPTSTR messageBuffer = nullptr;
|
|
auto size = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
nullptr, statusCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPTSTR) &messageBuffer, 0, nullptr);
|
|
|
|
String message (messageBuffer, size);
|
|
LocalFree (messageBuffer);
|
|
|
|
if (! owner.pageLoadHadNetworkError (message))
|
|
*pDispParams->rgvarg[0].pboolVal = VARIANT_TRUE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
if (dispIdMember == 263 /*DISPID_WINDOWCLOSING*/)
|
|
{
|
|
owner.windowCloseRequest();
|
|
|
|
// setting this bool tells the browser to ignore the event - we'll handle it.
|
|
if (pDispParams->cArgs > 0 && pDispParams->rgvarg[0].vt == (VT_BYREF | VT_BOOL))
|
|
*pDispParams->rgvarg[0].pboolVal = VARIANT_TRUE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
void componentMovedOrResized (bool, bool) override {}
|
|
void componentPeerChanged() override {}
|
|
void componentVisibilityChanged() override { owner.visibilityChanged(); }
|
|
|
|
private:
|
|
WebBrowserComponent& owner;
|
|
|
|
static String getStringFromVariant (VARIANT* v)
|
|
{
|
|
return (v->vt & VT_BYREF) != 0 ? *v->pbstrVal
|
|
: v->bstrVal;
|
|
}
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EventHandler)
|
|
};
|
|
};
|
|
|
|
#if JUCE_USE_WINRT_WEBVIEW
|
|
|
|
extern RTL_OSVERSIONINFOW getWindowsVersionInfo();
|
|
|
|
using namespace Microsoft::WRL;
|
|
|
|
using namespace ABI::Windows::Foundation;
|
|
using namespace ABI::Windows::Storage::Streams;
|
|
using namespace ABI::Windows::Web;
|
|
using namespace ABI::Windows::Web::UI;
|
|
using namespace ABI::Windows::Web::UI::Interop;
|
|
using namespace ABI::Windows::Web::Http;
|
|
using namespace ABI::Windows::Web::Http::Headers;
|
|
|
|
//==============================================================================
|
|
class WinRTWebView : public InternalWebViewType,
|
|
public Component,
|
|
public ComponentMovementWatcher
|
|
{
|
|
public:
|
|
WinRTWebView (WebBrowserComponent& o)
|
|
: ComponentMovementWatcher (&o),
|
|
owner (o)
|
|
{
|
|
if (! WinRTWrapper::getInstance()->isInitialised())
|
|
throw std::runtime_error ("Failed to initialise the WinRT wrapper");
|
|
|
|
if (! createWebViewProcess())
|
|
throw std::runtime_error ("Failed to create the WebViewControlProcess");
|
|
|
|
owner.addAndMakeVisible (this);
|
|
}
|
|
|
|
~WinRTWebView() override
|
|
{
|
|
if (webViewControl != nullptr)
|
|
webViewControl->Stop();
|
|
|
|
removeEventHandlers();
|
|
|
|
webViewProcess->Terminate();
|
|
}
|
|
|
|
void createBrowser() override
|
|
{
|
|
if (webViewControl == nullptr)
|
|
createWebViewControl();
|
|
}
|
|
|
|
bool hasBrowserBeenCreated() override
|
|
{
|
|
return webViewControl != nullptr || isCreating;
|
|
}
|
|
|
|
void goToURL (const String& url, const StringArray* headers, const MemoryBlock* postData) override
|
|
{
|
|
if (webViewControl != nullptr)
|
|
{
|
|
if ((headers != nullptr && ! headers->isEmpty())
|
|
|| (postData != nullptr && postData->getSize() > 0))
|
|
{
|
|
auto requestMessage = createHttpRequestMessage (url, headers, postData);
|
|
webViewControl->NavigateWithHttpRequestMessage (requestMessage.get());
|
|
}
|
|
else
|
|
{
|
|
auto uri = createURI (url);
|
|
webViewControl->Navigate (uri.get());
|
|
}
|
|
}
|
|
}
|
|
|
|
void stop() override
|
|
{
|
|
if (webViewControl != nullptr)
|
|
webViewControl->Stop();
|
|
}
|
|
|
|
void goBack() override
|
|
{
|
|
if (webViewControl != nullptr)
|
|
{
|
|
boolean canGoBack = false;
|
|
webViewControl->get_CanGoBack (&canGoBack);
|
|
|
|
if (canGoBack)
|
|
webViewControl->GoBack();
|
|
}
|
|
}
|
|
|
|
void goForward() override
|
|
{
|
|
if (webViewControl != nullptr)
|
|
{
|
|
boolean canGoForward = false;
|
|
webViewControl->get_CanGoForward (&canGoForward);
|
|
|
|
if (canGoForward)
|
|
webViewControl->GoForward();
|
|
}
|
|
}
|
|
|
|
void refresh() override
|
|
{
|
|
if (webViewControl != nullptr)
|
|
webViewControl->Refresh();
|
|
}
|
|
|
|
void setWebViewSize (int width, int height) override
|
|
{
|
|
setSize (width, height);
|
|
}
|
|
|
|
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
|
|
{
|
|
if (auto* peer = owner.getTopLevelComponent()->getPeer())
|
|
setControlBounds (peer->getAreaCoveredBy (owner));
|
|
}
|
|
|
|
void componentPeerChanged() override
|
|
{
|
|
componentMovedOrResized (true, true);
|
|
}
|
|
|
|
void componentVisibilityChanged() override
|
|
{
|
|
setControlVisible (owner.isShowing());
|
|
|
|
componentPeerChanged();
|
|
owner.visibilityChanged();
|
|
}
|
|
|
|
private:
|
|
//==============================================================================
|
|
template<typename OperationResultType, typename ResultsType>
|
|
static HRESULT waitForCompletion (IAsyncOperation<OperationResultType>* op, ResultsType* results)
|
|
{
|
|
using OperationType = IAsyncOperation<OperationResultType>;
|
|
using DelegateType = IAsyncOperationCompletedHandler<OperationResultType>;
|
|
|
|
struct EventDelegate : public RuntimeClass<RuntimeClassFlags<RuntimeClassType::Delegate>,
|
|
DelegateType,
|
|
FtmBase>
|
|
{
|
|
EventDelegate() = default;
|
|
|
|
~EventDelegate()
|
|
{
|
|
CloseHandle (eventCompleted);
|
|
}
|
|
|
|
HRESULT RuntimeClassInitialize()
|
|
{
|
|
eventCompleted = CreateEventEx (nullptr, nullptr, 0, EVENT_ALL_ACCESS);
|
|
return eventCompleted == nullptr ? HRESULT_FROM_WIN32 (GetLastError()) : S_OK;
|
|
}
|
|
|
|
HRESULT Invoke (OperationType*, AsyncStatus newStatus)
|
|
{
|
|
status = newStatus;
|
|
SetEvent (eventCompleted);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
AsyncStatus status = AsyncStatus::Started;
|
|
HANDLE eventCompleted = nullptr;
|
|
};
|
|
|
|
WinRTWrapper::ComPtr<OperationType> operation = op;
|
|
WinRTWrapper::ComPtr<EventDelegate> eventCallback;
|
|
|
|
auto hr = MakeAndInitialize<EventDelegate> (eventCallback.resetAndGetPointerAddress());
|
|
|
|
if (SUCCEEDED (hr))
|
|
{
|
|
hr = operation->put_Completed (eventCallback.get());
|
|
|
|
if (SUCCEEDED (hr))
|
|
{
|
|
HANDLE waitForEvents[1] { eventCallback->eventCompleted };
|
|
auto handleCount = (ULONG) ARRAYSIZE (waitForEvents);
|
|
DWORD handleIndex = 0;
|
|
|
|
hr = CoWaitForMultipleHandles (COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE,
|
|
INFINITE, handleCount, waitForEvents, &handleIndex);
|
|
|
|
if (SUCCEEDED (hr))
|
|
{
|
|
if (eventCallback->status == AsyncStatus::Completed)
|
|
{
|
|
hr = operation->GetResults (results);
|
|
}
|
|
else
|
|
{
|
|
WinRTWrapper::ComPtr<IAsyncInfo> asyncInfo;
|
|
|
|
if (SUCCEEDED (operation->QueryInterface (asyncInfo.resetAndGetPointerAddress())))
|
|
asyncInfo->get_ErrorCode (&hr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//==============================================================================
|
|
template<class ArgsType>
|
|
String getURIStringFromArgs (ArgsType& args)
|
|
{
|
|
WinRTWrapper::ComPtr<IUriRuntimeClass> uri;
|
|
args.get_Uri (uri.resetAndGetPointerAddress());
|
|
|
|
if (uri != nullptr)
|
|
{
|
|
HSTRING uriString;
|
|
uri->get_AbsoluteUri (&uriString);
|
|
|
|
return WinRTWrapper::getInstance()->hStringToString (uriString);
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
void addEventHandlers()
|
|
{
|
|
if (webViewControl != nullptr)
|
|
{
|
|
webViewControl->add_NavigationStarting (Callback<ITypedEventHandler<IWebViewControl*, WebViewControlNavigationStartingEventArgs*>> (
|
|
[this] (IWebViewControl*, IWebViewControlNavigationStartingEventArgs* args)
|
|
{
|
|
auto uriString = getURIStringFromArgs (*args);
|
|
|
|
if (uriString.isNotEmpty())
|
|
args->put_Cancel (! owner.pageAboutToLoad (uriString));
|
|
|
|
return S_OK;
|
|
}
|
|
).Get(), &navigationStartingToken);
|
|
|
|
webViewControl->add_NewWindowRequested (Callback<ITypedEventHandler<IWebViewControl*, WebViewControlNewWindowRequestedEventArgs*>> (
|
|
[this] (IWebViewControl*, IWebViewControlNewWindowRequestedEventArgs* args)
|
|
{
|
|
auto uriString = getURIStringFromArgs (*args);
|
|
|
|
if (uriString.isNotEmpty())
|
|
{
|
|
owner.newWindowAttemptingToLoad (uriString);
|
|
args->put_Handled (true);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
).Get(), &newWindowRequestedToken);
|
|
|
|
webViewControl->add_NavigationCompleted (Callback<ITypedEventHandler<IWebViewControl*, WebViewControlNavigationCompletedEventArgs*>> (
|
|
[this] (IWebViewControl*, IWebViewControlNavigationCompletedEventArgs* args)
|
|
{
|
|
auto uriString = getURIStringFromArgs (*args);
|
|
|
|
if (uriString.isNotEmpty())
|
|
{
|
|
boolean success;
|
|
args->get_IsSuccess (&success);
|
|
|
|
if (success)
|
|
{
|
|
owner.pageFinishedLoading (uriString);
|
|
}
|
|
else
|
|
{
|
|
WebErrorStatus status;
|
|
args->get_WebErrorStatus (&status);
|
|
|
|
owner.pageLoadHadNetworkError ("Error code: " + String (status));
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
).Get(), &navigationCompletedToken);
|
|
}
|
|
}
|
|
|
|
void removeEventHandlers()
|
|
{
|
|
if (webViewControl != nullptr)
|
|
{
|
|
if (navigationStartingToken.value != 0)
|
|
webViewControl->remove_NavigationStarting (navigationStartingToken);
|
|
|
|
if (newWindowRequestedToken.value != 0)
|
|
webViewControl->remove_NewWindowRequested (newWindowRequestedToken);
|
|
|
|
if (navigationCompletedToken.value != 0)
|
|
webViewControl->remove_NavigationCompleted (navigationCompletedToken);
|
|
}
|
|
}
|
|
|
|
bool createWebViewProcess()
|
|
{
|
|
auto webViewControlProcessFactory
|
|
= WinRTWrapper::getInstance()->getWRLFactory<IWebViewControlProcessFactory> (RuntimeClass_Windows_Web_UI_Interop_WebViewControlProcess);
|
|
|
|
if (webViewControlProcessFactory == nullptr)
|
|
{
|
|
jassertfalse;
|
|
return false;
|
|
}
|
|
|
|
auto webViewProcessOptions
|
|
= WinRTWrapper::getInstance()->activateInstance<IWebViewControlProcessOptions> (RuntimeClass_Windows_Web_UI_Interop_WebViewControlProcessOptions,
|
|
__uuidof (IWebViewControlProcessOptions));
|
|
|
|
webViewProcessOptions->put_PrivateNetworkClientServerCapability (WebViewControlProcessCapabilityState_Enabled);
|
|
webViewControlProcessFactory->CreateWithOptions (webViewProcessOptions.get(), webViewProcess.resetAndGetPointerAddress());
|
|
|
|
return webViewProcess != nullptr;
|
|
}
|
|
|
|
void createWebViewControl()
|
|
{
|
|
if (auto* peer = getPeer())
|
|
{
|
|
ScopedValueSetter<bool> svs (isCreating, true);
|
|
|
|
WinRTWrapper::ComPtr<IAsyncOperation<WebViewControl*>> createWebViewAsyncOperation;
|
|
|
|
webViewProcess->CreateWebViewControlAsync ((INT64) peer->getNativeHandle(), {},
|
|
createWebViewAsyncOperation.resetAndGetPointerAddress());
|
|
|
|
waitForCompletion (createWebViewAsyncOperation.get(), webViewControl.resetAndGetPointerAddress());
|
|
|
|
addEventHandlers();
|
|
componentMovedOrResized (true, true);
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
WinRTWrapper::ComPtr<IUriRuntimeClass> createURI (const String& url)
|
|
{
|
|
auto uriRuntimeFactory
|
|
= WinRTWrapper::getInstance()->getWRLFactory <IUriRuntimeClassFactory> (RuntimeClass_Windows_Foundation_Uri);
|
|
|
|
if (uriRuntimeFactory == nullptr)
|
|
{
|
|
jassertfalse;
|
|
return {};
|
|
}
|
|
|
|
WinRTWrapper::ScopedHString hstr (url);
|
|
WinRTWrapper::ComPtr<IUriRuntimeClass> uriRuntimeClass;
|
|
uriRuntimeFactory->CreateUri (hstr.get(), uriRuntimeClass.resetAndGetPointerAddress());
|
|
|
|
return uriRuntimeClass;
|
|
}
|
|
|
|
WinRTWrapper::ComPtr<IHttpContent> getPOSTContent (const MemoryBlock& postData)
|
|
{
|
|
auto factory = WinRTWrapper::getInstance()->getWRLFactory<IHttpStringContentFactory> (RuntimeClass_Windows_Web_Http_HttpStringContent);
|
|
|
|
if (factory == nullptr)
|
|
{
|
|
jassertfalse;
|
|
return {};
|
|
}
|
|
|
|
WinRTWrapper::ScopedHString hStr (postData.toString());
|
|
|
|
WinRTWrapper::ComPtr<IHttpContent> content;
|
|
factory->CreateFromString (hStr.get(), content.resetAndGetPointerAddress());
|
|
|
|
return content;
|
|
}
|
|
|
|
WinRTWrapper::ComPtr<IHttpMethod> getMethod (bool isPOST)
|
|
{
|
|
auto methodFactory = WinRTWrapper::getInstance()->getWRLFactory<IHttpMethodStatics> (RuntimeClass_Windows_Web_Http_HttpMethod);
|
|
|
|
if (methodFactory == nullptr)
|
|
{
|
|
jassertfalse;
|
|
return {};
|
|
}
|
|
|
|
WinRTWrapper::ComPtr<IHttpMethod> method;
|
|
|
|
if (isPOST)
|
|
methodFactory->get_Post (method.resetAndGetPointerAddress());
|
|
else
|
|
methodFactory->get_Get (method.resetAndGetPointerAddress());
|
|
|
|
return method;
|
|
}
|
|
|
|
void addHttpHeaders (WinRTWrapper::ComPtr<IHttpRequestMessage>& requestMessage, const StringArray& headers)
|
|
{
|
|
WinRTWrapper::ComPtr<IHttpRequestHeaderCollection> headerCollection;
|
|
requestMessage->get_Headers (headerCollection.resetAndGetPointerAddress());
|
|
|
|
for (int i = 0; i < headers.size(); ++i)
|
|
{
|
|
WinRTWrapper::ScopedHString headerName (headers[i].upToFirstOccurrenceOf (":", false, false).trim());
|
|
WinRTWrapper::ScopedHString headerValue (headers[i].fromFirstOccurrenceOf (":", false, false).trim());
|
|
|
|
headerCollection->Append (headerName.get(), headerValue.get());
|
|
}
|
|
}
|
|
|
|
WinRTWrapper::ComPtr<IHttpRequestMessage> createHttpRequestMessage (const String& url,
|
|
const StringArray* headers,
|
|
const MemoryBlock* postData)
|
|
{
|
|
auto requestFactory
|
|
= WinRTWrapper::getInstance()->getWRLFactory<IHttpRequestMessageFactory> (RuntimeClass_Windows_Web_Http_HttpRequestMessage);
|
|
|
|
if (requestFactory == nullptr)
|
|
{
|
|
jassertfalse;
|
|
return {};
|
|
}
|
|
|
|
bool isPOSTRequest = (postData != nullptr && postData->getSize() > 0);
|
|
auto method = getMethod (isPOSTRequest);
|
|
|
|
auto uri = createURI (url);
|
|
|
|
WinRTWrapper::ComPtr<IHttpRequestMessage> requestMessage;
|
|
requestFactory->Create (method.get(), uri.get(), requestMessage.resetAndGetPointerAddress());
|
|
|
|
if (isPOSTRequest)
|
|
{
|
|
auto content = getPOSTContent (*postData);
|
|
requestMessage->put_Content (content.get());
|
|
}
|
|
|
|
if (headers != nullptr && ! headers->isEmpty())
|
|
addHttpHeaders (requestMessage, *headers);
|
|
|
|
return requestMessage;
|
|
}
|
|
|
|
//==============================================================================
|
|
void setControlBounds (Rectangle<int> newBounds) const
|
|
{
|
|
if (webViewControl != nullptr)
|
|
{
|
|
#if JUCE_WIN_PER_MONITOR_DPI_AWARE
|
|
if (auto* peer = owner.getTopLevelComponent()->getPeer())
|
|
newBounds = (newBounds.toDouble() * peer->getPlatformScaleFactor()).toNearestInt();
|
|
#endif
|
|
|
|
WinRTWrapper::ComPtr<IWebViewControlSite> site;
|
|
|
|
if (SUCCEEDED (webViewControl->QueryInterface (site.resetAndGetPointerAddress())))
|
|
site->put_Bounds ({ static_cast<FLOAT> (newBounds.getX()), static_cast<FLOAT> (newBounds.getY()),
|
|
static_cast<FLOAT> (newBounds.getWidth()), static_cast<FLOAT> (newBounds.getHeight()) });
|
|
}
|
|
|
|
}
|
|
|
|
void setControlVisible (bool shouldBeVisible) const
|
|
{
|
|
if (webViewControl != 0)
|
|
{
|
|
WinRTWrapper::ComPtr<IWebViewControlSite> site;
|
|
|
|
if (SUCCEEDED (webViewControl->QueryInterface (site.resetAndGetPointerAddress())))
|
|
site->put_IsVisible (shouldBeVisible);
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
WebBrowserComponent& owner;
|
|
|
|
WinRTWrapper::ComPtr<IWebViewControlProcess> webViewProcess;
|
|
WinRTWrapper::ComPtr<IWebViewControl> webViewControl;
|
|
|
|
EventRegistrationToken navigationStartingToken { 0 },
|
|
newWindowRequestedToken { 0 },
|
|
navigationCompletedToken { 0 };
|
|
|
|
bool isCreating = false;
|
|
};
|
|
|
|
#endif
|
|
|
|
//==============================================================================
|
|
class WebBrowserComponent::Pimpl
|
|
{
|
|
public:
|
|
Pimpl (WebBrowserComponent& owner)
|
|
{
|
|
#if JUCE_USE_WINRT_WEBVIEW
|
|
auto windowsVersionInfo = getWindowsVersionInfo();
|
|
|
|
if (windowsVersionInfo.dwMajorVersion >= 10 && windowsVersionInfo.dwBuildNumber >= 17763)
|
|
{
|
|
try
|
|
{
|
|
internal.reset (new WinRTWebView (owner));
|
|
}
|
|
catch (std::runtime_error&) {}
|
|
}
|
|
#endif
|
|
|
|
if (internal == nullptr)
|
|
internal.reset (new Win32WebView (owner));
|
|
}
|
|
|
|
InternalWebViewType& getInternalWebView()
|
|
{
|
|
return *internal;
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<InternalWebViewType> internal;
|
|
};
|
|
|
|
//==============================================================================
|
|
WebBrowserComponent::WebBrowserComponent (bool unloadWhenHidden)
|
|
: browser (new Pimpl (*this)),
|
|
unloadPageWhenBrowserIsHidden (unloadWhenHidden)
|
|
{
|
|
setOpaque (true);
|
|
}
|
|
|
|
WebBrowserComponent::~WebBrowserComponent()
|
|
{
|
|
}
|
|
|
|
//==============================================================================
|
|
void WebBrowserComponent::goToURL (const String& url,
|
|
const StringArray* headers,
|
|
const MemoryBlock* postData)
|
|
{
|
|
lastURL = url;
|
|
|
|
if (headers != nullptr)
|
|
lastHeaders = *headers;
|
|
else
|
|
lastHeaders.clear();
|
|
|
|
if (postData != nullptr)
|
|
lastPostData = *postData;
|
|
else
|
|
lastPostData.reset();
|
|
|
|
blankPageShown = false;
|
|
|
|
if (! browser->getInternalWebView().hasBrowserBeenCreated())
|
|
checkWindowAssociation();
|
|
|
|
browser->getInternalWebView().goToURL (url, headers, postData);
|
|
}
|
|
|
|
void WebBrowserComponent::stop()
|
|
{
|
|
browser->getInternalWebView().stop();
|
|
}
|
|
|
|
void WebBrowserComponent::goBack()
|
|
{
|
|
lastURL.clear();
|
|
blankPageShown = false;
|
|
|
|
browser->getInternalWebView().goBack();
|
|
}
|
|
|
|
void WebBrowserComponent::goForward()
|
|
{
|
|
lastURL.clear();
|
|
|
|
browser->getInternalWebView().goForward();
|
|
}
|
|
|
|
void WebBrowserComponent::refresh()
|
|
{
|
|
browser->getInternalWebView().refresh();
|
|
}
|
|
|
|
//==============================================================================
|
|
void WebBrowserComponent::paint (Graphics& g)
|
|
{
|
|
if (! browser->getInternalWebView().hasBrowserBeenCreated())
|
|
{
|
|
g.fillAll (Colours::white);
|
|
checkWindowAssociation();
|
|
}
|
|
}
|
|
|
|
void WebBrowserComponent::checkWindowAssociation()
|
|
{
|
|
if (isShowing())
|
|
{
|
|
if (! browser->getInternalWebView().hasBrowserBeenCreated() && getPeer() != nullptr)
|
|
{
|
|
browser->getInternalWebView().createBrowser();
|
|
reloadLastURL();
|
|
}
|
|
else
|
|
{
|
|
if (blankPageShown)
|
|
goBack();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (browser != nullptr && unloadPageWhenBrowserIsHidden && ! blankPageShown)
|
|
{
|
|
// when the component becomes invisible, some stuff like flash
|
|
// carries on playing audio, so we need to force it onto a blank
|
|
// page to avoid this..
|
|
|
|
blankPageShown = true;
|
|
browser->getInternalWebView().goToURL ("about:blank", 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WebBrowserComponent::reloadLastURL()
|
|
{
|
|
if (lastURL.isNotEmpty())
|
|
{
|
|
goToURL (lastURL, &lastHeaders, &lastPostData);
|
|
lastURL.clear();
|
|
}
|
|
}
|
|
|
|
void WebBrowserComponent::parentHierarchyChanged()
|
|
{
|
|
checkWindowAssociation();
|
|
}
|
|
|
|
void WebBrowserComponent::resized()
|
|
{
|
|
browser->getInternalWebView().setWebViewSize (getWidth(), getHeight());
|
|
}
|
|
|
|
void WebBrowserComponent::visibilityChanged()
|
|
{
|
|
checkWindowAssociation();
|
|
}
|
|
|
|
void WebBrowserComponent::focusGained (FocusChangeType)
|
|
{
|
|
browser->getInternalWebView().focusGained();
|
|
}
|
|
|
|
void WebBrowserComponent::clearCookies()
|
|
{
|
|
HeapBlock<::INTERNET_CACHE_ENTRY_INFOA> entry;
|
|
::DWORD entrySize = sizeof (::INTERNET_CACHE_ENTRY_INFOA);
|
|
::HANDLE urlCacheHandle = ::FindFirstUrlCacheEntryA ("cookie:", entry.getData(), &entrySize);
|
|
|
|
if (urlCacheHandle == nullptr && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
entry.realloc (1, entrySize);
|
|
urlCacheHandle = ::FindFirstUrlCacheEntryA ("cookie:", entry.getData(), &entrySize);
|
|
}
|
|
|
|
if (urlCacheHandle != nullptr)
|
|
{
|
|
for (;;)
|
|
{
|
|
::DeleteUrlCacheEntryA (entry.getData()->lpszSourceUrlName);
|
|
|
|
if (::FindNextUrlCacheEntryA (urlCacheHandle, entry.getData(), &entrySize) == 0)
|
|
{
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
entry.realloc (1, entrySize);
|
|
|
|
if (::FindNextUrlCacheEntryA (urlCacheHandle, entry.getData(), &entrySize) != 0)
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
FindCloseUrlCache (urlCacheHandle);
|
|
}
|
|
}
|
|
|
|
} // namespace juce
|