|  | /*
  ==============================================================================
   This file is part of the JUCE 6 technical preview.
   Copyright (c) 2017 - ROLI Ltd.
   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
 |