| Index: src/plugin/PluginTabBase.cpp | 
| diff --git a/src/plugin/PluginTabBase.cpp b/src/plugin/PluginTabBase.cpp | 
| index 011c5b5b673e158c18dc40170e361e56b83d3ef0..e1e9940209e52da2114872c9cf14473c0e411f3b 100644 | 
| --- a/src/plugin/PluginTabBase.cpp | 
| +++ b/src/plugin/PluginTabBase.cpp | 
| @@ -23,14 +23,69 @@ | 
| #include "PluginTabBase.h" | 
| #include "IeVersion.h" | 
| #include "../shared/Utils.h" | 
| +#include "../shared/EventWithSetter.h" | 
| #include <Mshtmhst.h> | 
| +#include <mutex> | 
| + | 
| +class CPluginTab::AsyncPluginFilter | 
| +{ | 
| +public: | 
| +  static std::shared_ptr<AsyncPluginFilter> CreateAsync(const std::wstring& domain) | 
| +  { | 
| +    std::shared_ptr<AsyncPluginFilter> asyncFilter = std::make_shared<AsyncPluginFilter>(); | 
| +    std::weak_ptr<AsyncPluginFilter> weakAsyncData = asyncFilter; | 
| +    auto eventSetter = asyncFilter->event.CreateSetter(); | 
| +    try | 
| +    { | 
| +      std::thread([domain, weakAsyncData, eventSetter] | 
| +      { | 
| +        try | 
| +        { | 
| +          CreateAsyncImpl(domain, weakAsyncData, eventSetter); | 
| +        } | 
| +	catch (...) | 
| +        { | 
| +          // As a thread-main function, we truncate any C++ exception. | 
| +        } | 
| +      }).detach(); | 
| +      // TODO: we should do something with that `detach` above. | 
| +    } | 
| +    catch (const std::system_error& ex) | 
| +    { | 
| +      DEBUG_SYSTEM_EXCEPTION(ex, PLUGIN_ERROR_THREAD, PLUGIN_ERROR_MAIN_THREAD_CREATE_PROCESS, | 
| +        "Class::Thread - Failed to start filter loader thread"); | 
| +    } | 
| +    return asyncFilter; | 
| +  } | 
| +  PluginFilterPtr GetFilter() | 
| +  { | 
| +    if (!event.Wait()) | 
| +      return PluginFilterPtr(); | 
| +    std::lock_guard<std::mutex> lock(mutex); | 
| +    return filter; | 
| +  } | 
| +private: | 
| +  static void CreateAsyncImpl(const std::wstring& domain, std::weak_ptr<AsyncPluginFilter> weakAsyncData, const std::shared_ptr<EventWithSetter::Setter>& setter) | 
| +  { | 
| +    std::unique_ptr<CPluginFilter> pluginFilter(new CPluginFilter(CPluginClient::GetInstance()->GetElementHidingSelectors(domain))); | 
| +    if (auto asyncData = weakAsyncData.lock()) | 
| +    { | 
| +      { | 
| +        std::lock_guard<std::mutex> lock(asyncData->mutex); | 
| +        asyncData->filter = move(pluginFilter); | 
| +      } | 
| +      setter->Set(); | 
| +    } | 
| +  } | 
| +  EventWithSetter event; | 
| +  std::mutex mutex; | 
| +  PluginFilterPtr filter; | 
| +}; | 
|  | 
| CPluginTab::CPluginTab() | 
| : m_isActivated(false) | 
| , m_continueThreadRunning(true) | 
| { | 
| -  m_filter.hideFiltersLoadedEvent = CreateEvent(NULL, true, false, NULL); | 
| - | 
| CPluginClient* client = CPluginClient::GetInstance(); | 
| if (AdblockPlus::IE::InstalledMajorVersion() < 10) | 
| { | 
| @@ -46,14 +101,11 @@ CPluginTab::CPluginTab() | 
| DEBUG_SYSTEM_EXCEPTION(ex, PLUGIN_ERROR_THREAD, PLUGIN_ERROR_TAB_THREAD_CREATE_PROCESS, | 
| "Tab::Thread - Failed to create tab thread"); | 
| } | 
| -  m_traverser = new CPluginDomTraverser(static_cast<CPluginTab*>(this)); | 
| } | 
|  | 
|  | 
| CPluginTab::~CPluginTab() | 
| { | 
| -  delete m_traverser; | 
| -  m_traverser = NULL; | 
| m_continueThreadRunning = false; | 
| if (m_thread.joinable()) { | 
| m_thread.join(); | 
| @@ -82,40 +134,13 @@ void CPluginTab::OnUpdate() | 
| m_isActivated = true; | 
| } | 
|  | 
| -namespace | 
| -{ | 
| -  // Entry Point | 
| -  void FilterLoader(CPluginFilter* filter, const std::wstring& domain) | 
| -  { | 
| -    try | 
| -    { | 
| -      filter->LoadHideFilters(CPluginClient::GetInstance()->GetElementHidingSelectors(domain)); | 
| -      SetEvent(filter->hideFiltersLoadedEvent); | 
| -    } | 
| -    catch (...) | 
| -    { | 
| -      // As a thread-main function, we truncate any C++ exception. | 
| -    } | 
| -  } | 
| -} | 
| - | 
| void CPluginTab::OnNavigate(const std::wstring& url) | 
| { | 
| SetDocumentUrl(url); | 
| -  ClearFrameCache(GetDocumentDomain()); | 
| -  std::wstring domainString = GetDocumentDomain(); | 
| -  ResetEvent(m_filter.hideFiltersLoadedEvent); | 
| -  try | 
| -  { | 
| -    std::thread filterLoaderThread(&FilterLoader, &m_filter, GetDocumentDomain()); | 
| -    filterLoaderThread.detach(); // TODO: but actually we should wait for the thread in the dtr. | 
| -  } | 
| -  catch (const std::system_error& ex) | 
| -  { | 
| -    DEBUG_SYSTEM_EXCEPTION(ex, PLUGIN_ERROR_THREAD, PLUGIN_ERROR_MAIN_THREAD_CREATE_PROCESS, | 
| -      "Class::Thread - Failed to start filter loader thread"); | 
| -  } | 
| -  m_traverser->ClearCache(); | 
| +  std::wstring domain = GetDocumentDomain(); | 
| +  ClearFrameCache(domain); | 
| +  m_asyncPluginFilter = AsyncPluginFilter::CreateAsync(domain); | 
| +  m_traverser.reset(); | 
| } | 
|  | 
| namespace | 
| @@ -213,6 +238,119 @@ void CPluginTab::InjectABP(IWebBrowser2* browser) | 
| } | 
| } | 
|  | 
| +bool CPluginTab::IsTraverserEnabled() | 
| +{ | 
| +  return !IsCSSInjectionEnabled(); | 
| +} | 
| + | 
| +bool CPluginTab::IsCSSInjectionEnabled() | 
| +{ | 
| +  return IsWindowsVistaOrLater() && AdblockPlus::IE::InstalledMajorVersion() >= 10; | 
| +} | 
| + | 
| +namespace | 
| +{ | 
| +  void InjectABPCSS(IHTMLDocument2& htmlDocument2, const std::vector<std::wstring>& hideFilters) | 
| +  { | 
| +    // pseudocode: styleHtmlElement = htmlDocument2.createElement("style"); | 
| +    ATL::CComQIPtr<IHTMLStyleElement> styleHtmlElement; | 
| +    { | 
| +      ATL::CComPtr<IHTMLElement> stylePureHtmlElement; | 
| +      if (FAILED(htmlDocument2.createElement(ATL::CComBSTR(L"style"), &stylePureHtmlElement))) | 
| +      { | 
| +        DEBUG_GENERAL(L"Cannot create style element"); | 
| +        return; | 
| +      } | 
| +      if (!(styleHtmlElement = stylePureHtmlElement)) | 
| +      { | 
| +        DEBUG_GENERAL(L"Cannot obtain IHTMLStyleElement from IHTMLElement"); | 
| +        return; | 
| +      } | 
| +    } | 
| +    // pseudocode: styleHtmlElement.type = "text/css"; | 
| +    if (FAILED(styleHtmlElement->put_type(ATL::CComBSTR("text/css")))) | 
| +    { | 
| +      DEBUG_GENERAL(L"Cannot set type text/css"); | 
| +      return; | 
| +    } | 
| +    // pseudocode: styleSheet4 = styleHtmlElement.sheet; | 
| +    ATL::CComQIPtr<IHTMLStyleSheet4> styleSheet4; | 
| +    { | 
| +      // IHTMLStyleElement2 is availabe starting from IE9, Vista | 
| +      ATL::CComQIPtr<IHTMLStyleElement2> styleHtmlElement2 = styleHtmlElement; | 
| +      if (!styleHtmlElement2) | 
| +      { | 
| +        DEBUG_GENERAL(L"Cannot obtain IHTMLStyleElement2 from IHTMLStyleElement"); | 
| +        return; | 
| +      } | 
| +      ATL::CComQIPtr<IHTMLStyleSheet> styleSheet; | 
| +      if (FAILED(styleHtmlElement2->get_sheet(&styleSheet)) || !styleSheet) | 
| +      { | 
| +        DEBUG_GENERAL(L"Cannot obtain IHTMLStyleSheet"); | 
| +        return; | 
| +      } | 
| +      // IHTMLStyleSheet4 is availabe starting from IE9, Vista | 
| +      styleSheet4 = styleSheet; | 
| +      if (!styleSheet4) | 
| +      { | 
| +        DEBUG_GENERAL(L"Cannot obtain IHTMLStyleSheet4"); | 
| +        return; | 
| +      } | 
| +    } | 
| +    // pseudocode: for (auto i = 0; i < hideFilters.length; ++i) { | 
| +    // pseudocode:   i = styleSheet4.insertRule(hideFilters + cssValue, i); | 
| +    // pseudocode: } | 
| +    long newIndex = 0; | 
| +    std::wstring cssValue = L"{ display: none !important; }"; | 
| +    for (const auto& selector : hideFilters) | 
| +    { | 
| +      auto cssRule = selector + cssValue; | 
| +      ATL::CComBSTR selector(cssRule.size(), cssRule.c_str()); | 
| +      if (SUCCEEDED(styleSheet4->insertRule(selector, newIndex, &newIndex))) | 
| +      { | 
| +        ++newIndex; | 
| +      } | 
| +      else | 
| +      { | 
| +        DEBUG_GENERAL(L"Cannot add rule for selector " + cssRule); | 
| +      } | 
| +    } | 
| + | 
| +    // pseudocode: htmlDocument2.head.appendChild(styleHtmlElement); | 
| +    { | 
| +      // IHTMLDocument7 is availabe starting from IE9, Vista | 
| +      ATL::CComQIPtr<IHTMLDocument7> htmlDocument7 = &htmlDocument2; | 
| +      if (!htmlDocument7) | 
| +      { | 
| +        DEBUG_GENERAL(L"Cannot obtain IHTMLDocument7 from htmlDocument2"); | 
| +        return; | 
| +      } | 
| +      ATL::CComPtr<IHTMLElement> headHtmlElement; | 
| +      if (FAILED(htmlDocument7->get_head(&headHtmlElement))) | 
| +      { | 
| +        DEBUG_GENERAL(L"Cannot obtain head from pDoc7"); | 
| +        return; | 
| +      } | 
| +      ATL::CComQIPtr<IHTMLDOMNode> headNode = headHtmlElement; | 
| +      if (!headNode) | 
| +      { | 
| +        DEBUG_GENERAL(L"Cannot obtain headNode from headHtmlElement"); | 
| +        return; | 
| +      } | 
| +      ATL::CComQIPtr<IHTMLDOMNode> styleNode = styleHtmlElement; | 
| +      if (!styleNode) | 
| +      { | 
| +        DEBUG_GENERAL(L"Cannot obtain IHTMLDOMNode from stylePureHtmlElement"); | 
| +        return; | 
| +      } | 
| +      if (FAILED(headNode->appendChild(styleNode, nullptr))) | 
| +      { | 
| +        DEBUG_GENERAL(L"Cannot append blocking style"); | 
| +      } | 
| +    } | 
| +  } | 
| +} | 
| + | 
| namespace | 
| { | 
| ATL::CComPtr<IWebBrowser2> GetParent(IWebBrowser2& browser) | 
| @@ -261,11 +399,27 @@ namespace | 
|  | 
| void CPluginTab::OnDownloadComplete(IWebBrowser2* browser) | 
| { | 
| -  CPluginClient* client = CPluginClient::GetInstance(); | 
| -  std::wstring url = GetDocumentUrl(); | 
| -  if (!client->IsWhitelistedUrl(url) && !client->IsElemhideWhitelistedOnDomain(url)) | 
| +  if (IsTraverserEnabled()) | 
| { | 
| -    m_traverser->TraverseDocument(browser, GetDocumentDomain(), GetDocumentUrl()); | 
| +    CPluginClient* client = CPluginClient::GetInstance(); | 
| +    std::wstring url = GetDocumentUrl(); | 
| +    if (!client->IsWhitelistedUrl(url) && !client->IsElemhideWhitelistedOnDomain(url)) | 
| +    { | 
| +      if (!m_traverser) | 
| +      { | 
| +        assert(m_asyncPluginFilter && "Filter initialization should be already at least started"); | 
| +        if (m_asyncPluginFilter) | 
| +        { | 
| +          auto pluginFilter = m_asyncPluginFilter->GetFilter(); | 
| +          assert(pluginFilter && "Plugin filter should be a valid object"); | 
| +          if (pluginFilter) | 
| +            m_traverser.reset(new CPluginDomTraverser(pluginFilter)); | 
| +        } | 
| +      } | 
| +      assert(m_traverser && "Traverser should be a valid object"); | 
| +      if (m_traverser) | 
| +        m_traverser->TraverseDocument(browser, GetDocumentDomain(), GetDocumentUrl()); | 
| +    } | 
| } | 
| InjectABP(browser); | 
| } | 
| @@ -300,6 +454,24 @@ void CPluginTab::OnDocumentComplete(IWebBrowser2* browser, const std::wstring& u | 
| return; | 
| } | 
|  | 
| +  if (IsCSSInjectionEnabled() && CPluginSettings::GetInstance()->GetPluginEnabled()) | 
| +  { | 
| +    if (!IsFrameWhiteListed(browser)) | 
| +    { | 
| +      DEBUG_GENERAL(L"Inject CSS into " + url); | 
| +      assert(m_asyncPluginFilter && "Filter initialization should be already at least started"); | 
| +      if (m_asyncPluginFilter) | 
| +      { | 
| +        auto pluginFilter = m_asyncPluginFilter->GetFilter(); | 
| +        assert(pluginFilter && "Plugin filter should be a valid object"); | 
| +        if (pluginFilter) | 
| +        { | 
| +          InjectABPCSS(*pDoc, pluginFilter->GetHideFilters()); | 
| +        } | 
| +      } | 
| +    } | 
| +  } | 
| + | 
| CComPtr<IOleObject> pOleObj; | 
| pDocDispatch->QueryInterface(&pOleObj); | 
| if (!pOleObj) | 
|  |