void CPluginFilter::ClearFilters()
{
  // Clear filter maps
  CriticalSection::Lock filterEngineLock(s_criticalSectionFilterMap);
  m_elementHideTags.clear();
  m_elementHideTagsId.clear();
  m_elementHideTagsClass.clear();
}
bool CPluginFilter::AddFilterElementHide(std::wstring filterText)
{
  DEBUG_FILTER(L"Input: " + filterText + L" filterFile" + filterFile);
  CriticalSection::Lock filterEngineLock(s_criticalSectionFilterMap);
  {
    // Create filter descriptor
    std::auto_ptr<CFilterElementHide> filter;
    wchar_t separatorChar;
    do
    {
      auto chunkEnd = filterText.find_first_of(L"+>");
      if (chunkEnd != std::wstring::npos && chunkEnd > 0)
      {
        separatorChar = filterText[chunkEnd];
      }
      else
      {
        chunkEnd = filterText.length();
        separatorChar = L'\0';
      }

      std::auto_ptr<CFilterElementHide> filterParent(filter);
      filter.reset(new CFilterElementHide(TrimStringRight(filterText.substr(0, chunkEnd))));
      if (filterParent.get() != 0)
      {
        filter->m_predecessor.reset(filterParent.release());
      }

      if (separatorChar != L'\0') // complex selector
      {
        filterText = TrimStringLeft(filterText.substr(chunkEnd + 1));
        if (separatorChar == '+')
          filter->m_type = CFilterElementHide::TRAVERSER_TYPE_IMMEDIATE;
        else if (separatorChar == '>')
          filter->m_type = CFilterElementHide::TRAVERSER_TYPE_PARENT;
      }
      else // Terminating element (simple selector)
      {
        if (!filter->m_tagId.empty())
        {
          m_elementHideTagsId.insert(std::make_pair(std::make_pair(filter->m_tag, filter->m_tagId), *filter));
        }
        else if (!filter->m_tagClassName.empty())
        {
          m_elementHideTagsClass.insert(std::make_pair(std::make_pair(filter->m_tag, filter->m_tagClassName), *filter));
        }
        else
        {
          m_elementHideTags.insert(std::make_pair(filter->m_tag, *filter));
        }
      }
    } while (separatorChar != '\0');
  }

  return true;
}
bool CPluginFilter::LoadHideFilters(std::vector<std::wstring> filters)
{
  ClearFilters();
  bool isRead = false;
  CPluginClient* client = CPluginClient::GetInstance();

  // Parse hide string
  int pos = 0;
  CriticalSection::Lock filterEngineLock(s_criticalSectionFilterMap);
  {
    for (std::vector<std::wstring>::iterator it = filters.begin(); it < filters.end(); ++it)
    {
      CString filter((*it).c_str());
      // If the line is not commented out
      if (!filter.Trim().IsEmpty() && filter.GetAt(0) != '!' && filter.GetAt(0) != '[')
      {
        int filterType = 0;

        // See http://adblockplus.org/en/filters for further documentation

        try
        {
          AddFilterElementHide(filter);
        }
        catch(...)
        {
#ifdef ENABLE_DEBUG_RESULT
          CPluginDebug::DebugResult(L"Error loading hide filter: " + ToWstring(filter));
#endif
        }
      }
    }
  }

  return isRead;
}
bool CPluginFilter::IsElementHidden(const std::wstring& tag, IHTMLElement* pEl, const std::wstring& domain, const std::wstring& indent) const
{
  CString tagCString = ToCString(tag);

  CString id;
  CComBSTR bstrId;
  if (SUCCEEDED(pEl->get_id(&bstrId)) && bstrId)
  {
    id = bstrId;
  }

  CString classNames;
  CComBSTR bstrClassNames;
  if (SUCCEEDED(pEl->get_className(&bstrClassNames)) && bstrClassNames)
  {
    classNames = bstrClassNames;
  }

  CriticalSection::Lock filterEngineLock(s_criticalSectionFilterMap);
  {
    // Search tag/id filters
    if (!id.IsEmpty())
    {
      std::pair<TFilterElementHideTagsNamed::const_iterator, TFilterElementHideTagsNamed::const_iterator> idItEnum =
        m_elementHideTagsId.equal_range(std::make_pair(tagCString, id));
      for (TFilterElementHideTagsNamed::const_iterator idIt = idItEnum.first; idIt != idItEnum.second; idIt ++)
      {
        if (idIt->second.IsMatchFilterElementHide(pEl))
        {
#ifdef ENABLE_DEBUG_RESULT
          DEBUG_HIDE_EL(indent + "HideEl::Found (tag/id) filter:" + idIt->second.m_filterText)
            CPluginDebug::DebugResultHiding(tag, L"id:" + ToWstring(id), ToWstring(idIt->second.m_filterText));
#endif
          return true;
        }
      }

      // Search general id
      idItEnum = m_elementHideTagsId.equal_range(std::make_pair("", id));
      for (TFilterElementHideTagsNamed::const_iterator idIt = idItEnum.first; idIt != idItEnum.second; idIt ++)
      {
        if (idIt->second.IsMatchFilterElementHide(pEl))
        {
#ifdef ENABLE_DEBUG_RESULT
          DEBUG_HIDE_EL(indent + "HideEl::Found (?/id) filter:" + idIt->second.m_filterText)
            CPluginDebug::DebugResultHiding(tag, L"id:" + ToWstring(id), ToWstring(idIt->second.m_filterText));
#endif
          return true;
        }
      }
    }

    // Search tag/className filters
    if (!classNames.IsEmpty())
    {
      int pos = 0;
      CString className = classNames.Tokenize(L" \t\n\r", pos);
      while (pos >= 0)
      {
        std::pair<TFilterElementHideTagsNamed::const_iterator, TFilterElementHideTagsNamed::const_iterator> classItEnum = 
          m_elementHideTagsClass.equal_range(std::make_pair(tagCString, className));

        for (TFilterElementHideTagsNamed::const_iterator classIt = classItEnum.first; classIt != classItEnum.second; ++classIt)
        {
          if (classIt->second.IsMatchFilterElementHide(pEl))
          {
#ifdef ENABLE_DEBUG_RESULT
            DEBUG_HIDE_EL(indent + "HideEl::Found (tag/class) filter:" + classIt->second.m_filterText)
              CPluginDebug::DebugResultHiding(tag, L"class:" + ToWstring(className), ToWstring(classIt->second.m_filterText));
#endif
            return true;
          }
        }

        // Search general class name
        classItEnum = m_elementHideTagsClass.equal_range(std::make_pair("", className));
        for (TFilterElementHideTagsNamed::const_iterator classIt = classItEnum.first; classIt != classItEnum.second; ++ classIt)
        {
          if (classIt->second.IsMatchFilterElementHide(pEl))
          {
#ifdef ENABLE_DEBUG_RESULT
            DEBUG_HIDE_EL(indent + L"HideEl::Found (?/class) filter:" + ToWString(classIt->second.m_filterText));
            CPluginDebug::DebugResultHiding(tag, L"class:" + ToWstring(className), ToWstring(classIt->second.m_filterText));
#endif
            return true;
          }
        }

        // Next class name
        className = classNames.Tokenize(L" \t\n\r", pos);
      }
    }

    // Search tag filters
    std::pair<TFilterElementHideTags::const_iterator, TFilterElementHideTags::const_iterator> tagItEnum 
      = m_elementHideTags.equal_range(tagCString);
    for (TFilterElementHideTags::const_iterator tagIt = tagItEnum.first; tagIt != tagItEnum.second; ++ tagIt)
    {
      if (tagIt->second.IsMatchFilterElementHide(pEl))
      {
#ifdef ENABLE_DEBUG_RESULT
        DEBUG_HIDE_EL(indent + "HideEl::Found (tag) filter:" + tagIt->second.m_filterText)
          CPluginDebug::DebugResultHiding(tag, L"-", ToWstring(tagIt->second.m_filterText));
#endif
        return true;
      }
    }
  }

  return false;
}
bool CPluginFilter::AddFilterElementHide(CString filterText)
{
  DEBUG_FILTER("Input: " + filterText + " filterFile" + filterFile);
  CriticalSection::Lock filterEngineLock(s_criticalSectionFilterMap);
  {
    CString filterString  = filterText;
    // Create filter descriptor
    std::auto_ptr<CFilterElementHide> filter;

    CString wholeFilterString = filterString;
    wchar_t separatorChar;
    do
    {
      int chunkEnd = filterText.FindOneOf(L"+>");
      if (chunkEnd > 0)
      {
        separatorChar = filterText.GetAt(chunkEnd);
      }
      else
      {
        chunkEnd = filterText.GetLength();
        separatorChar = L'\0';
      }

      CString filterChunk = filterText.Left(chunkEnd).TrimRight();
      std::auto_ptr<CFilterElementHide> filterParent(filter);

      filter.reset(new CFilterElementHide(filterChunk));

      if (filterParent.get() != 0)
      {
        filter->m_predecessor.reset(filterParent.release());
      }

      if (separatorChar != L'\0') // complex selector
      {
        filterText = filterText.Mid(chunkEnd + 1).TrimLeft();
        if (separatorChar == '+')
          filter->m_type = CFilterElementHide::TRAVERSER_TYPE_IMMEDIATE;
        else if (separatorChar == '>')
          filter->m_type = CFilterElementHide::TRAVERSER_TYPE_PARENT;
      }
      else // Terminating element (simple selector)
      {
        if (!filter->m_tagId.IsEmpty())
        {
          m_elementHideTagsId.insert(std::make_pair(std::make_pair(filter->m_tag, filter->m_tagId), *filter));
        }
        else if (!filter->m_tagClassName.IsEmpty())
        {
          m_elementHideTagsClass.insert(std::make_pair(std::make_pair(filter->m_tag, filter->m_tagClassName), *filter));
        }
        else
        {
          std::pair<CString, CFilterElementHide> pair = std::make_pair(filter->m_tag, *filter);
          m_elementHideTags.insert(pair);
        }
      }
    } while (separatorChar != '\0');
  }

  return true;
}
bool CPluginFilter::IsElementHidden(const std::wstring& tag, IHTMLElement* pEl, const std::wstring& domain, const std::wstring& indent) const
{
  std::wstring id;
  CComBSTR idBstr;
  if (SUCCEEDED(pEl->get_id(&idBstr)) && idBstr)
  {
    id = ToWstring(idBstr);
  }
  std::wstring classNames;
  CComBSTR classNamesBstr;
  if (SUCCEEDED(pEl->get_className(&classNamesBstr)) && classNamesBstr)
  {
    classNames = ToWstring(classNamesBstr);
  }

  CriticalSection::Lock filterEngineLock(s_criticalSectionFilterMap);
  {
    // Search tag/id filters
    if (!id.empty())
    {
      auto idItEnum = m_elementHideTagsId.equal_range(std::make_pair(tag, id));
      for (auto idIt = idItEnum.first; idIt != idItEnum.second; ++idIt)
      {
        if (idIt->second.IsMatchFilterElementHide(pEl))
        {
#ifdef ENABLE_DEBUG_RESULT
          DEBUG_HIDE_EL(indent + L"HideEl::Found (tag/id) filter:" + idIt->second.m_filterText);
          CPluginDebug::DebugResultHiding(tag, L"id:" + id, idIt->second.m_filterText);
#endif
          return true;
        }
      }

      // Search general id
      idItEnum = m_elementHideTagsId.equal_range(std::make_pair(L"", id));
      for (auto idIt = idItEnum.first; idIt != idItEnum.second; ++idIt)
      {
        if (idIt->second.IsMatchFilterElementHide(pEl))
        {
#ifdef ENABLE_DEBUG_RESULT
          DEBUG_HIDE_EL(indent + L"HideEl::Found (?/id) filter:" + idIt->second.m_filterText);
          CPluginDebug::DebugResultHiding(tag, L"id:" + id, idIt->second.m_filterText);
#endif
          return true;
        }
      }
    }

    // Search tag/className filters
    if (!classNames.empty())
    {
      wchar_t* nextToken = nullptr;
      const wchar_t* token = wcstok_s(&classNames[0], L" \t\n\r", &nextToken);
      while (token != nullptr)
      {
        std::wstring className(token);
        auto classItEnum = m_elementHideTagsClass.equal_range(std::make_pair(tag, className));
        for (auto classIt = classItEnum.first; classIt != classItEnum.second; ++classIt)
        {
          if (classIt->second.IsMatchFilterElementHide(pEl))
          {
#ifdef ENABLE_DEBUG_RESULT
            DEBUG_HIDE_EL(indent + L"HideEl::Found (tag/class) filter:" + classIt->second.m_filterText);
            CPluginDebug::DebugResultHiding(tag, L"class:" + className, classIt->second.m_filterText);
#endif
            return true;
          }
        }

        // Search general class name
        classItEnum = m_elementHideTagsClass.equal_range(std::make_pair(L"", className));
        for (auto classIt = classItEnum.first; classIt != classItEnum.second; ++ classIt)
        {
          if (classIt->second.IsMatchFilterElementHide(pEl))
          {
#ifdef ENABLE_DEBUG_RESULT
            DEBUG_HIDE_EL(indent + L"HideEl::Found (?/class) filter:" + classIt->second.m_filterText);
            CPluginDebug::DebugResultHiding(tag, L"class:" + className, classIt->second.m_filterText);
#endif
            return true;
          }
        }
        token = wcstok_s(nullptr, L" \t\n\r", &nextToken);
      }
    }

    // Search tag filters
    auto tagItEnum = m_elementHideTags.equal_range(tag);
    for (auto tagIt = tagItEnum.first; tagIt != tagItEnum.second; ++tagIt)
    {
      if (tagIt->second.IsMatchFilterElementHide(pEl))
      {
#ifdef ENABLE_DEBUG_RESULT
        DEBUG_HIDE_EL(indent + L"HideEl::Found (tag) filter:" + tagIt->second.m_filterText);
        CPluginDebug::DebugResultHiding(tag, L"-", tagIt->second.m_filterText);
#endif
        return true;
      }
    }
  }

  return false;
}