|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2017 - ROLI Ltd.
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 5 End-User License
- Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
- 27th April 2017).
-
- End User License Agreement: www.juce.com/juce-5-licence
- Privacy Policy: www.juce.com/juce-5-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- 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
- {
-
- 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")
-
- #if JUCE_MINGW
- #define DISPID_NAVIGATEERROR 271
- class WebBrowser;
- #endif
-
- JUCE_DECLARE_UUID_GETTER (WebBrowser, "8856F961-340A-11D0-A96B-00C04FD705A2")
-
- class WebBrowserComponent::Pimpl : public ActiveXControlComponent
- {
- public:
- Pimpl() {}
-
- ~Pimpl()
- {
- if (connectionPoint != nullptr)
- connectionPoint->Unadvise (adviseCookie);
-
- if (browser != nullptr)
- browser->Release();
- }
-
- void createBrowser()
- {
- 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*> (getParentComponent());
- jassert (owner != nullptr);
-
- auto handler = new EventHandler (*owner);
- connectionPoint->Advise (handler, &adviseCookie);
- handler->Release();
- }
- }
- }
-
- void goToURL (const String& url,
- const StringArray* headers,
- const MemoryBlock* postData)
- {
- if (browser != nullptr)
- {
- LPSAFEARRAY sa = 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)
- {
- 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;
-
- postDataVar = postDataVar2;
- }
- }
- }
-
- auto urlBSTR = SysAllocString ((const OLECHAR*) url.toWideCharPointer());
- browser->Navigate (urlBSTR, &headerFlags, &frame, &postDataVar, &headersVar);
- SysFreeString (urlBSTR);
-
- if (sa != nullptr)
- SafeArrayDestroy (sa);
-
- VariantClear (&headerFlags);
- VariantClear (&frame);
- VariantClear (&postDataVar);
- VariantClear (&headersVar);
- }
- }
-
- //==============================================================================
- IWebBrowser2* browser = nullptr;
-
- private:
- IConnectionPoint* connectionPoint = nullptr;
- DWORD adviseCookie = 0;
-
- //==============================================================================
- struct EventHandler : public ComBaseClassHelper<IDispatch>,
- public ComponentMovementWatcher
- {
- EventHandler (WebBrowserComponent& w) : ComponentMovementWatcher (&w), owner (w) {}
-
- JUCE_COMRESULT GetTypeInfoCount (UINT*) { return E_NOTIMPL; }
- JUCE_COMRESULT GetTypeInfo (UINT, LCID, ITypeInfo**) { return E_NOTIMPL; }
- JUCE_COMRESULT GetIDsOfNames (REFIID, LPOLESTR*, UINT, LCID, DISPID*) { return E_NOTIMPL; }
-
- JUCE_COMRESULT Invoke (DISPID dispIdMember, REFIID /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, DISPPARAMS* pDispParams,
- VARIANT* /*pVarResult*/, EXCEPINFO* /*pExcepInfo*/, UINT* /*puArgErr*/)
- {
- 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 == 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 erros
- 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)
- };
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
- };
-
-
- //==============================================================================
- WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_)
- : browser (nullptr),
- blankPageShown (false),
- unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
- {
- setOpaque (true);
- addAndMakeVisible (browser = new Pimpl());
- }
-
- WebBrowserComponent::~WebBrowserComponent()
- {
- delete browser;
- }
-
- //==============================================================================
- 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->browser == nullptr)
- checkWindowAssociation();
-
- browser->goToURL (url, headers, postData);
- }
-
- void WebBrowserComponent::stop()
- {
- if (browser->browser != nullptr)
- browser->browser->Stop();
- }
-
- void WebBrowserComponent::goBack()
- {
- lastURL.clear();
- blankPageShown = false;
-
- if (browser->browser != nullptr)
- browser->browser->GoBack();
- }
-
- void WebBrowserComponent::goForward()
- {
- lastURL.clear();
-
- if (browser->browser != nullptr)
- browser->browser->GoForward();
- }
-
- void WebBrowserComponent::refresh()
- {
- if (browser->browser != nullptr)
- browser->browser->Refresh();
- }
-
- //==============================================================================
- void WebBrowserComponent::paint (Graphics& g)
- {
- if (browser->browser == nullptr)
- {
- g.fillAll (Colours::white);
- checkWindowAssociation();
- }
- }
-
- void WebBrowserComponent::checkWindowAssociation()
- {
- if (isShowing())
- {
- if (browser->browser == nullptr && getPeer() != nullptr)
- {
- browser->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->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->setSize (getWidth(), getHeight());
- }
-
- void WebBrowserComponent::visibilityChanged()
- {
- checkWindowAssociation();
- }
-
- void WebBrowserComponent::focusGained (FocusChangeType)
- {
- auto iidOleObject = __uuidof (IOleObject);
- auto iidOleWindow = __uuidof (IOleWindow);
-
- if (auto oleObject = (IOleObject*) browser->queryInterface (&iidOleObject))
- {
- if (auto oleWindow = (IOleWindow*) browser->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 WebBrowserComponent::clearCookies()
- {
- HeapBlock<::INTERNET_CACHE_ENTRY_INFO> entry;
- ::DWORD entrySize = sizeof (::INTERNET_CACHE_ENTRY_INFO);
-
- #if JUCE_MINGW
- const auto searchPattern = "cookie:";
- #else
- const auto searchPattern = TEXT ("cookie:");
- #endif
- ::HANDLE urlCacheHandle = ::FindFirstUrlCacheEntry (searchPattern, entry.getData(), &entrySize);
-
- if (urlCacheHandle == nullptr && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
- {
- entry.realloc (1, entrySize);
- urlCacheHandle = ::FindFirstUrlCacheEntry (searchPattern, entry.getData(), &entrySize);
- }
-
- if (urlCacheHandle != nullptr)
- {
- for (;;)
- {
- ::DeleteUrlCacheEntry (entry.getData()->lpszSourceUrlName);
-
- if (::FindNextUrlCacheEntry (urlCacheHandle, entry.getData(), &entrySize) == 0)
- {
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
- {
- entry.realloc (1, entrySize);
-
- if (::FindNextUrlCacheEntry (urlCacheHandle, entry.getData(), &entrySize) != 0)
- continue;
- }
-
- break;
- }
- }
-
- FindCloseUrlCache (urlCacheHandle);
- }
- }
-
- } // namespace juce
|