| Index: src/plugin/PluginClass.cpp | 
| diff --git a/src/plugin/PluginClass.cpp b/src/plugin/PluginClass.cpp | 
| index 0a588b0b95929bae95ee9b15605b6929ea6b7535..13815556d820025d5d60c82c33282f22ad590f78 100644 | 
| --- a/src/plugin/PluginClass.cpp | 
| +++ b/src/plugin/PluginClass.cpp | 
| @@ -32,6 +32,7 @@ | 
| #include "../shared/Version.h" | 
| #include <thread> | 
| #include <array> | 
| +#include "WebBrowserEventsListener.h" | 
|  | 
| #ifdef DEBUG_HIDE_EL | 
| DWORD profileTime = 0; | 
| @@ -98,7 +99,7 @@ namespace AdblockPlus | 
| } | 
|  | 
| CPluginClass::CPluginClass() | 
| -  : m_webBrowser2(nullptr) | 
| +  : m_data(std::make_shared<Data>()) | 
| { | 
| DEBUG_GENERAL([this]() -> std::wstring | 
| { | 
| @@ -120,7 +121,7 @@ CPluginClass::CPluginClass() | 
| m_isInitializedOk = false; | 
|  | 
|  | 
| -  m_tab = new CPluginTab(); | 
| +  m_data->tab.reset(new CPluginTab()); | 
|  | 
| Dictionary::Create(GetBrowserLanguage()); | 
| } | 
| @@ -134,18 +135,18 @@ CPluginClass::~CPluginClass() | 
| return s; | 
| }()); | 
|  | 
| -  delete m_tab; | 
| +  m_data.reset(); | 
| } | 
|  | 
| HWND CPluginClass::GetBrowserHWND() const | 
| { | 
| -  if (!m_webBrowser2) | 
| +  if (!m_data->webBrowser2) | 
| { | 
| -    DEBUG_ERROR_LOG(0, 0, 0, "CPluginClass::GetBrowserHWND - Reached with m_webBrowser2 == nullptr"); | 
| +    DEBUG_ERROR_LOG(0, 0, 0, "CPluginClass::GetBrowserHWND - Reached with webBrowser2 == nullptr"); | 
| return nullptr; | 
| } | 
| SHANDLE_PTR hBrowserWndHandle = 0; | 
| -  HRESULT hr = m_webBrowser2->get_HWND(&hBrowserWndHandle); | 
| +  HRESULT hr = m_data->webBrowser2->get_HWND(&hBrowserWndHandle); | 
| if (FAILED(hr)) | 
| { | 
| DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_UI, PLUGIN_ERROR_UI_GET_BROWSER_WINDOW, "Class::GetBrowserHWND - failed"); | 
| @@ -156,7 +157,7 @@ HWND CPluginClass::GetBrowserHWND() const | 
|  | 
| bool CPluginClass::IsRootBrowser(IWebBrowser2* otherBrowser) | 
| { | 
| -  return m_webBrowser2.IsEqualObject(otherBrowser); | 
| +  return m_data->webBrowser2.IsEqualObject(otherBrowser); | 
| } | 
|  | 
| CComQIPtr<IWebBrowser2> CPluginClass::GetAsyncBrowser() | 
| @@ -175,21 +176,21 @@ CComQIPtr<IWebBrowser2> CPluginClass::GetAsyncBrowser() | 
| std::wstring CPluginClass::GetBrowserUrl() const | 
| { | 
| std::wstring url; | 
| -  if (m_webBrowser2) | 
| +  if (m_data->webBrowser2) | 
| { | 
| CComBSTR bstrURL; | 
| -    if (SUCCEEDED(m_webBrowser2->get_LocationURL(&bstrURL))) | 
| +    if (SUCCEEDED(m_data->webBrowser2->get_LocationURL(&bstrURL))) | 
| { | 
| url = ToWstring(bstrURL); | 
| } | 
| } | 
| else | 
| { | 
| -    DEBUG_GENERAL(L"CPluginClass::GetBrowserUrl - Reached with m_webBrowser2 == nullptr (probable invariant violation)"); | 
| +    DEBUG_GENERAL(L"CPluginClass::GetBrowserUrl - Reached with webBrowser2 == nullptr (probable invariant violation)"); | 
| } | 
| if (url.empty()) | 
| { | 
| -    url = m_tab->GetDocumentUrl(); | 
| +    url = m_data->tab->GetDocumentUrl(); | 
| } | 
| return url; | 
| } | 
| @@ -233,8 +234,8 @@ STDMETHODIMP CPluginClass::SetSite(IUnknown* unknownSite) | 
| /* | 
| * We were instantiated as a BHO, so our site is always of type IWebBrowser2. | 
| */ | 
| -      m_webBrowser2 = ATL::CComQIPtr<IWebBrowser2>(unknownSite); | 
| -      if (!m_webBrowser2) | 
| +      m_data->webBrowser2 = ATL::CComQIPtr<IWebBrowser2>(unknownSite); | 
| +      if (!m_data->webBrowser2) | 
| { | 
| throw std::logic_error("CPluginClass::SetSite - Unable to convert site pointer to IWebBrowser2*"); | 
| } | 
| @@ -242,7 +243,7 @@ STDMETHODIMP CPluginClass::SetSite(IUnknown* unknownSite) | 
| { | 
| std::wstringstream ss; | 
| ss << L"CPluginClass::SetSite, this = " << ToHexLiteral(this); | 
| -          ss << L", browser = " << ToHexLiteral(m_webBrowser2); | 
| +          ss << L", browser = " << ToHexLiteral(m_data->webBrowser2); | 
| return ss.str(); | 
| }()); | 
|  | 
| @@ -260,7 +261,7 @@ STDMETHODIMP CPluginClass::SetSite(IUnknown* unknownSite) | 
|  | 
| try | 
| { | 
| -        HRESULT hr = DispEventAdvise(m_webBrowser2); | 
| +        HRESULT hr = DispEventAdvise(m_data->webBrowser2); | 
| if (SUCCEEDED(hr)) | 
| { | 
| m_isAdvised = true; | 
| @@ -297,6 +298,7 @@ STDMETHODIMP CPluginClass::SetSite(IUnknown* unknownSite) | 
| }()); | 
|  | 
| Unadvise(); | 
| +      assert(m_data->connectedWebBrowsersCache.empty() && "Connected web browser cache should be already empty"); | 
|  | 
| // Destroy window | 
| if (m_pWndProcStatus) | 
| @@ -336,7 +338,7 @@ STDMETHODIMP CPluginClass::SetSite(IUnknown* unknownSite) | 
| } | 
| s_criticalSectionLocal.Unlock(); | 
|  | 
| -      m_webBrowser2 = nullptr; | 
| +      m_data->webBrowser2 = nullptr; | 
|  | 
| DEBUG_GENERAL("================================================================================\nNEW TAB UI - END\n================================================================================") | 
|  | 
| @@ -486,6 +488,7 @@ void STDMETHODCALLTYPE CPluginClass::OnBeforeNavigate2( | 
| return; | 
| } | 
| std::wstring url = ToWstring(urlVariant->bstrVal); | 
| +    EnsureWebBrowserConnected(webBrowser); | 
|  | 
| // If webbrowser2 is equal to top level browser (as set in SetSite), we are | 
| // navigating new page | 
| @@ -495,7 +498,7 @@ void STDMETHODCALLTYPE CPluginClass::OnBeforeNavigate2( | 
| } | 
| else if (IsRootBrowser(webBrowser)) | 
| { | 
| -      m_tab->OnNavigate(url); | 
| +      m_data->tab->OnNavigate(url); | 
| DEBUG_GENERAL( | 
| L"================================================================================\n" | 
| L"Begin main navigation url:" + url + L"\n" | 
| @@ -509,7 +512,7 @@ void STDMETHODCALLTYPE CPluginClass::OnBeforeNavigate2( | 
| else | 
| { | 
| DEBUG_NAVI(L"Navi::Begin navigation url:" + url) | 
| -      m_tab->CacheFrame(url); | 
| +      m_data->tab->CacheFrame(url); | 
| } | 
| } | 
| catch (...) | 
| @@ -522,32 +525,13 @@ void STDMETHODCALLTYPE CPluginClass::OnDownloadComplete() | 
| { | 
| try | 
| { | 
| -    if (!m_webBrowser2) | 
| +    if (!m_data->webBrowser2) | 
| { | 
| -      DEBUG_ERROR_LOG(0, 0, 0, "CPluginClass::OnDownloadComplete - Reached with m_webBrowser2 == nullptr"); | 
| +      DEBUG_ERROR_LOG(0, 0, 0, "CPluginClass::OnDownloadComplete - Reached with webBrowser2 == nullptr"); | 
| return; | 
| } | 
| DEBUG_NAVI(L"Navi::Download Complete") | 
| -    m_tab->OnDownloadComplete(m_webBrowser2); | 
| -  } | 
| -  catch (...) | 
| -  { | 
| -  } | 
| -} | 
| - | 
| -// Entry point | 
| -void STDMETHODCALLTYPE CPluginClass::OnDocumentComplete(IDispatch* frameBrowserDisp, VARIANT* /*urlOrPidl*/) | 
| -{ | 
| -  try | 
| -  { | 
| -    DEBUG_NAVI(L"Navi::Document Complete"); | 
| -    ATL::CComQIPtr<IWebBrowser2> webBrowser2 = frameBrowserDisp; | 
| -    if (!webBrowser2) | 
| -    { | 
| -      return; | 
| -    } | 
| -    std::wstring frameSrc = GetLocationUrl(*webBrowser2); | 
| -    m_tab->OnDocumentComplete(webBrowser2, frameSrc, IsRootBrowser(webBrowser2)); | 
| +      m_data->tab->OnDownloadComplete(m_data->webBrowser2); | 
| } | 
| catch (...) | 
| { | 
| @@ -989,7 +973,7 @@ CPluginClass* CPluginClass::FindInstance(HWND hStatusBarWnd) | 
|  | 
| CPluginTab* CPluginClass::GetTab() | 
| { | 
| -  return m_tab; | 
| +  return m_data->tab.get(); | 
| } | 
|  | 
| CPluginTab* CPluginClass::GetTab(DWORD dwThreadId) | 
| @@ -1001,7 +985,7 @@ CPluginTab* CPluginClass::GetTab(DWORD dwThreadId) | 
| std::map<DWORD,CPluginClass*>::const_iterator it = s_threadInstances.find(dwThreadId); | 
| if (it != s_threadInstances.end()) | 
| { | 
| -      tab = it->second->m_tab; | 
| +      tab = it->second->m_data->tab.get(); | 
| } | 
| } | 
| s_criticalSectionLocal.Unlock(); | 
| @@ -1246,7 +1230,7 @@ STDMETHODIMP CPluginClass::Exec(const GUID*, DWORD nCmdID, DWORD, VARIANTARG*, V | 
| } | 
|  | 
| // Create menu | 
| -    HMENU hMenu = CreatePluginMenu(m_tab->GetDocumentUrl()); | 
| +    HMENU hMenu = CreatePluginMenu(m_data->tab->GetDocumentUrl()); | 
| if (!hMenu) | 
| { | 
| return E_FAIL; | 
| @@ -1662,19 +1646,18 @@ void CPluginClass::UpdateStatusBar() | 
| } | 
| } | 
|  | 
| - | 
| void CPluginClass::Unadvise() | 
| { | 
| -  if (!m_webBrowser2) | 
| +  if (!m_data->webBrowser2) | 
| { | 
| -    DEBUG_ERROR_LOG(0, 0, 0, "CPluginClass::Unadvise - Reached with m_webBrowser2 == nullptr"); | 
| +    DEBUG_ERROR_LOG(0, 0, 0, "CPluginClass::Unadvise - Reached with webBrowser2 == nullptr"); | 
| return; | 
| } | 
| s_criticalSectionLocal.Lock(); | 
| { | 
| if (m_isAdvised) | 
| { | 
| -      HRESULT hr = DispEventUnadvise(m_webBrowser2); | 
| +      HRESULT hr = DispEventUnadvise(m_data->webBrowser2); | 
| if (FAILED(hr)) | 
| { | 
| DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_SET_SITE, PLUGIN_ERROR_SET_SITE_UNADVISE, "Class::Unadvise - Unadvise"); | 
| @@ -1685,6 +1668,42 @@ void CPluginClass::Unadvise() | 
| s_criticalSectionLocal.Unlock(); | 
| } | 
|  | 
| +void CPluginClass::EnsureWebBrowserConnected(const ATL::CComPtr<IWebBrowser2>& webBrowser) | 
| +{ | 
| +  auto it = m_data->connectedWebBrowsersCache.find(webBrowser); | 
| +  if (it != m_data->connectedWebBrowsersCache.end()) | 
| +  { | 
| +    return; | 
| +  } | 
| +  ATL::CComObject<WebBrowserEventsListener>* listenerImpl = nullptr; | 
| +  if (FAILED(ATL::CComObject<WebBrowserEventsListener>::CreateInstance(&listenerImpl))) | 
| +  { | 
| +    return; | 
| +  } | 
| +  ATL::CComPtr<IUnknown> listenerRefCounterGuard(listenerImpl->GetUnknown()); | 
| +  std::weak_ptr<Data> dataForCapturing = m_data; | 
| +  auto onListenerDestroy = [webBrowser, dataForCapturing] | 
| +  { | 
| +    if (auto data = dataForCapturing.lock()) | 
| +    { | 
| +      data->connectedWebBrowsersCache.erase(webBrowser); | 
| +    } | 
| +  }; | 
| +  auto onReloaded = [webBrowser, dataForCapturing] | 
| +  { | 
| +    if (auto data = dataForCapturing.lock()) | 
| +    { | 
| +      auto frameSrc = GetLocationUrl(*webBrowser); | 
| +      data->tab->OnDocumentComplete(webBrowser, frameSrc, data->webBrowser2.IsEqualObject(webBrowser)); | 
| +    } | 
| +  }; | 
| +  if (FAILED(listenerImpl->Init(webBrowser, onListenerDestroy, onReloaded))) | 
| +  { | 
| +    return; | 
| +  } | 
| +  m_data->connectedWebBrowsersCache.emplace(webBrowser, listenerImpl); | 
| +} | 
| + | 
| HICON CPluginClass::GetIcon(int type) | 
| { | 
| HICON icon = NULL; | 
|  |